xref: /aosp_15_r20/external/webrtc/rtc_base/strong_alias_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright 2019 The Chromium Authors. All rights reserved.
3  *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
4  *
5  *  Use of this source code is governed by a BSD-style license
6  *  that can be found in the LICENSE file in the root of the source
7  *  tree. An additional intellectual property rights grant can be found
8  *  in the file PATENTS.  All contributing project authors may
9  *  be found in the AUTHORS file in the root of the source tree.
10  */
11 #include "rtc_base/strong_alias.h"
12 
13 #include <cstdint>
14 #include <map>
15 #include <memory>
16 #include <string>
17 #include <type_traits>
18 #include <utility>
19 
20 #include "rtc_base/containers/flat_map.h"
21 #include "rtc_base/gunit.h"
22 #include "test/gmock.h"
23 
24 // This is a copy of
25 // https://source.chromium.org/chromium/chromium/src/+/main:base/types/strong_alias_unittest.cc
26 // but adapted to use WebRTC's includes, remove unit tests that test the ostream
27 // operator (it's removed in this port) and other adaptations to pass lint.
28 
29 namespace webrtc {
30 namespace {
31 
32 // For test correctnenss, it's important that these getters return lexically
33 // incrementing values as `index` grows.
34 template <typename T>
35 T GetExampleValue(int index);
36 
37 template <>
GetExampleValue(int index)38 int GetExampleValue<int>(int index) {
39   return 5 + index;
40 }
41 template <>
GetExampleValue(int index)42 uint64_t GetExampleValue<uint64_t>(int index) {
43   return 500U + index;
44 }
45 
46 template <>
GetExampleValue(int index)47 std::string GetExampleValue<std::string>(int index) {
48   return std::string('a', index);
49 }
50 
51 }  // namespace
52 
53 template <typename T>
54 class StrongAliasTest : public ::testing::Test {};
55 
56 using TestedTypes = ::testing::Types<int, uint64_t, std::string>;
57 TYPED_TEST_SUITE(StrongAliasTest, TestedTypes);
58 
TYPED_TEST(StrongAliasTest,ValueAccessesUnderlyingValue)59 TYPED_TEST(StrongAliasTest, ValueAccessesUnderlyingValue) {
60   using FooAlias = StrongAlias<class FooTag, TypeParam>;
61 
62   // Const value getter.
63   const FooAlias const_alias(GetExampleValue<TypeParam>(1));
64   EXPECT_EQ(GetExampleValue<TypeParam>(1), const_alias.value());
65   static_assert(std::is_const<typename std::remove_reference<decltype(
66                     const_alias.value())>::type>::value,
67                 "Reference returned by const value getter should be const.");
68 }
69 
TYPED_TEST(StrongAliasTest,ExplicitConversionToUnderlyingValue)70 TYPED_TEST(StrongAliasTest, ExplicitConversionToUnderlyingValue) {
71   using FooAlias = StrongAlias<class FooTag, TypeParam>;
72 
73   const FooAlias const_alias(GetExampleValue<TypeParam>(1));
74   EXPECT_EQ(GetExampleValue<TypeParam>(1), static_cast<TypeParam>(const_alias));
75 }
76 
TYPED_TEST(StrongAliasTest,CanBeCopyConstructed)77 TYPED_TEST(StrongAliasTest, CanBeCopyConstructed) {
78   using FooAlias = StrongAlias<class FooTag, TypeParam>;
79   FooAlias alias(GetExampleValue<TypeParam>(0));
80   FooAlias copy_constructed = alias;
81   EXPECT_EQ(copy_constructed, alias);
82 
83   FooAlias copy_assigned;
84   copy_assigned = alias;
85   EXPECT_EQ(copy_assigned, alias);
86 }
87 
TYPED_TEST(StrongAliasTest,CanBeMoveConstructed)88 TYPED_TEST(StrongAliasTest, CanBeMoveConstructed) {
89   using FooAlias = StrongAlias<class FooTag, TypeParam>;
90   FooAlias alias(GetExampleValue<TypeParam>(0));
91   FooAlias move_constructed = std::move(alias);
92   EXPECT_EQ(move_constructed, FooAlias(GetExampleValue<TypeParam>(0)));
93 
94   FooAlias alias2(GetExampleValue<TypeParam>(2));
95   FooAlias move_assigned;
96   move_assigned = std::move(alias2);
97   EXPECT_EQ(move_assigned, FooAlias(GetExampleValue<TypeParam>(2)));
98 
99   // Check that FooAlias is nothrow move constructible. This matters for
100   // performance when used in std::vectors.
101   static_assert(std::is_nothrow_move_constructible<FooAlias>::value,
102                 "Error: Alias is not nothow move constructible");
103 }
104 
TYPED_TEST(StrongAliasTest,CanBeConstructedFromMoveOnlyType)105 TYPED_TEST(StrongAliasTest, CanBeConstructedFromMoveOnlyType) {
106   // Note, using a move-only unique_ptr to T:
107   using FooAlias = StrongAlias<class FooTag, std::unique_ptr<TypeParam>>;
108 
109   FooAlias a(std::make_unique<TypeParam>(GetExampleValue<TypeParam>(0)));
110   EXPECT_EQ(*a.value(), GetExampleValue<TypeParam>(0));
111 
112   auto bare_value = std::make_unique<TypeParam>(GetExampleValue<TypeParam>(1));
113   FooAlias b(std::move(bare_value));
114   EXPECT_EQ(*b.value(), GetExampleValue<TypeParam>(1));
115 }
116 
TYPED_TEST(StrongAliasTest,MutableOperatorArrow)117 TYPED_TEST(StrongAliasTest, MutableOperatorArrow) {
118   // Note, using a move-only unique_ptr to T:
119   using Ptr = std::unique_ptr<TypeParam>;
120   using FooAlias = StrongAlias<class FooTag, Ptr>;
121 
122   FooAlias a(std::make_unique<TypeParam>());
123   EXPECT_TRUE(a.value());
124 
125   // Check that `a` can be modified through the use of operator->.
126   a->reset();
127 
128   EXPECT_FALSE(a.value());
129 }
130 
TYPED_TEST(StrongAliasTest,MutableOperatorStar)131 TYPED_TEST(StrongAliasTest, MutableOperatorStar) {
132   // Note, using a move-only unique_ptr to T:
133   using Ptr = std::unique_ptr<TypeParam>;
134   using FooAlias = StrongAlias<class FooTag, Ptr>;
135 
136   FooAlias a(std::make_unique<TypeParam>());
137   FooAlias b(std::make_unique<TypeParam>());
138   EXPECT_TRUE(*a);
139   EXPECT_TRUE(*b);
140 
141   // Check that both the mutable l-value and r-value overloads work and we can
142   // move out of the aliases.
143   { Ptr ignore(*std::move(a)); }
144   { Ptr ignore(std::move(*b)); }
145 
146   EXPECT_FALSE(a.value());
147   EXPECT_FALSE(b.value());
148 }
149 
TYPED_TEST(StrongAliasTest,MutableValue)150 TYPED_TEST(StrongAliasTest, MutableValue) {
151   // Note, using a move-only unique_ptr to T:
152   using Ptr = std::unique_ptr<TypeParam>;
153   using FooAlias = StrongAlias<class FooTag, Ptr>;
154 
155   FooAlias a(std::make_unique<TypeParam>());
156   FooAlias b(std::make_unique<TypeParam>());
157   EXPECT_TRUE(a.value());
158   EXPECT_TRUE(b.value());
159 
160   // Check that both the mutable l-value and r-value overloads work and we can
161   // move out of the aliases.
162   { Ptr ignore(std::move(a).value()); }
163   { Ptr ignore(std::move(b.value())); }
164 
165   EXPECT_FALSE(a.value());
166   EXPECT_FALSE(b.value());
167 }
168 
TYPED_TEST(StrongAliasTest,SizeSameAsUnderlyingType)169 TYPED_TEST(StrongAliasTest, SizeSameAsUnderlyingType) {
170   using FooAlias = StrongAlias<class FooTag, TypeParam>;
171   static_assert(sizeof(FooAlias) == sizeof(TypeParam),
172                 "StrongAlias should be as large as the underlying type.");
173 }
174 
TYPED_TEST(StrongAliasTest,IsDefaultConstructible)175 TYPED_TEST(StrongAliasTest, IsDefaultConstructible) {
176   using FooAlias = StrongAlias<class FooTag, TypeParam>;
177   static_assert(std::is_default_constructible<FooAlias>::value,
178                 "Should be possible to default-construct a StrongAlias.");
179   static_assert(
180       std::is_trivially_default_constructible<FooAlias>::value ==
181           std::is_trivially_default_constructible<TypeParam>::value,
182       "Should be possible to trivially default-construct a StrongAlias iff the "
183       "underlying type is trivially default constructible.");
184 }
185 
TEST(StrongAliasTest,TrivialTypeAliasIsStandardLayout)186 TEST(StrongAliasTest, TrivialTypeAliasIsStandardLayout) {
187   using FooAlias = StrongAlias<class FooTag, int>;
188   static_assert(std::is_standard_layout<FooAlias>::value,
189                 "int-based alias should have standard layout. ");
190   static_assert(std::is_trivially_copyable<FooAlias>::value,
191                 "int-based alias should be trivially copyable. ");
192 }
193 
TYPED_TEST(StrongAliasTest,CannotBeCreatedFromDifferentAlias)194 TYPED_TEST(StrongAliasTest, CannotBeCreatedFromDifferentAlias) {
195   using FooAlias = StrongAlias<class FooTag, TypeParam>;
196   using BarAlias = StrongAlias<class BarTag, TypeParam>;
197   static_assert(!std::is_constructible<FooAlias, BarAlias>::value,
198                 "Should be impossible to construct FooAlias from a BarAlias.");
199   static_assert(!std::is_convertible<BarAlias, FooAlias>::value,
200                 "Should be impossible to convert a BarAlias into FooAlias.");
201 }
202 
TYPED_TEST(StrongAliasTest,CannotBeImplicitlyConverterToUnderlyingValue)203 TYPED_TEST(StrongAliasTest, CannotBeImplicitlyConverterToUnderlyingValue) {
204   using FooAlias = StrongAlias<class FooTag, TypeParam>;
205   static_assert(!std::is_convertible<FooAlias, TypeParam>::value,
206                 "Should be impossible to implicitly convert a StrongAlias into "
207                 "an underlying type.");
208 }
209 
TYPED_TEST(StrongAliasTest,ComparesEqualToSameValue)210 TYPED_TEST(StrongAliasTest, ComparesEqualToSameValue) {
211   using FooAlias = StrongAlias<class FooTag, TypeParam>;
212   // Comparison to self:
213   const FooAlias a = FooAlias(GetExampleValue<TypeParam>(0));
214   EXPECT_EQ(a, a);
215   EXPECT_FALSE(a != a);
216   EXPECT_TRUE(a >= a);
217   EXPECT_TRUE(a <= a);
218   EXPECT_FALSE(a > a);
219   EXPECT_FALSE(a < a);
220   // Comparison to other equal object:
221   const FooAlias b = FooAlias(GetExampleValue<TypeParam>(0));
222   EXPECT_EQ(a, b);
223   EXPECT_FALSE(a != b);
224   EXPECT_TRUE(a >= b);
225   EXPECT_TRUE(a <= b);
226   EXPECT_FALSE(a > b);
227   EXPECT_FALSE(a < b);
228 }
229 
TYPED_TEST(StrongAliasTest,ComparesCorrectlyToDifferentValue)230 TYPED_TEST(StrongAliasTest, ComparesCorrectlyToDifferentValue) {
231   using FooAlias = StrongAlias<class FooTag, TypeParam>;
232   const FooAlias a = FooAlias(GetExampleValue<TypeParam>(0));
233   const FooAlias b = FooAlias(GetExampleValue<TypeParam>(1));
234   EXPECT_NE(a, b);
235   EXPECT_FALSE(a == b);
236   EXPECT_TRUE(b >= a);
237   EXPECT_TRUE(a <= b);
238   EXPECT_TRUE(b > a);
239   EXPECT_TRUE(a < b);
240 }
241 
TEST(StrongAliasTest,CanBeDerivedFrom)242 TEST(StrongAliasTest, CanBeDerivedFrom) {
243   // Aliases can be enriched by custom operations or validations if needed.
244   // Ideally, one could go from a 'using' declaration to a derived class to add
245   // those methods without the need to change any other code.
246   class CountryCode : public StrongAlias<CountryCode, std::string> {
247    public:
248     explicit CountryCode(const std::string& value)
249         : StrongAlias<CountryCode, std::string>::StrongAlias(value) {
250       if (value_.length() != 2) {
251         // Country code invalid!
252         value_.clear();  // is_null() will return true.
253       }
254     }
255 
256     bool is_null() const { return value_.empty(); }
257   };
258 
259   CountryCode valid("US");
260   EXPECT_FALSE(valid.is_null());
261 
262   CountryCode invalid("United States");
263   EXPECT_TRUE(invalid.is_null());
264 }
265 
TEST(StrongAliasTest,CanWrapComplexStructures)266 TEST(StrongAliasTest, CanWrapComplexStructures) {
267   // A pair of strings implements odering and can, in principle, be used as
268   // a base of StrongAlias.
269   using PairOfStrings = std::pair<std::string, std::string>;
270   using ComplexAlias = StrongAlias<class FooTag, PairOfStrings>;
271 
272   ComplexAlias a1{std::make_pair("aaa", "bbb")};
273   ComplexAlias a2{std::make_pair("ccc", "ddd")};
274   EXPECT_TRUE(a1 < a2);
275 
276   EXPECT_TRUE(a1.value() == PairOfStrings("aaa", "bbb"));
277 
278   // Note a caveat, an std::pair doesn't have an overload of operator<<, and it
279   // cannot be easily added since ADL rules would require it to be in the std
280   // namespace. So we can't print ComplexAlias.
281 }
282 
TYPED_TEST(StrongAliasTest,CanBeKeysInFlatMap)283 TYPED_TEST(StrongAliasTest, CanBeKeysInFlatMap) {
284   using FooAlias = StrongAlias<class FooTag, TypeParam>;
285   webrtc::flat_map<FooAlias, std::string> map;
286 
287   FooAlias k1(GetExampleValue<TypeParam>(0));
288   FooAlias k2(GetExampleValue<TypeParam>(1));
289 
290   map[k1] = "value1";
291   map[k2] = "value2";
292 
293   EXPECT_EQ(map[k1], "value1");
294   EXPECT_EQ(map[k2], "value2");
295 }
296 
TYPED_TEST(StrongAliasTest,CanBeKeysInStdMap)297 TYPED_TEST(StrongAliasTest, CanBeKeysInStdMap) {
298   using FooAlias = StrongAlias<class FooTag, TypeParam>;
299   std::map<FooAlias, std::string> map;
300 
301   FooAlias k1(GetExampleValue<TypeParam>(0));
302   FooAlias k2(GetExampleValue<TypeParam>(1));
303 
304   map[k1] = "value1";
305   map[k2] = "value2";
306 
307   EXPECT_EQ(map[k1], "value1");
308   EXPECT_EQ(map[k2], "value2");
309 }
310 
TYPED_TEST(StrongAliasTest,CanDifferentiateOverloads)311 TYPED_TEST(StrongAliasTest, CanDifferentiateOverloads) {
312   using FooAlias = StrongAlias<class FooTag, TypeParam>;
313   using BarAlias = StrongAlias<class BarTag, TypeParam>;
314   class Scope {
315    public:
316     static std::string Overload(FooAlias) { return "FooAlias"; }
317     static std::string Overload(BarAlias) { return "BarAlias"; }
318   };
319   EXPECT_EQ("FooAlias", Scope::Overload(FooAlias()));
320   EXPECT_EQ("BarAlias", Scope::Overload(BarAlias()));
321 }
322 
TEST(StrongAliasTest,EnsureConstexpr)323 TEST(StrongAliasTest, EnsureConstexpr) {
324   using FooAlias = StrongAlias<class FooTag, int>;
325 
326   // Check constructors.
327   static constexpr FooAlias kZero{};
328   static constexpr FooAlias kOne(1);
329 
330   // Check operator*.
331   static_assert(*kZero == 0, "");
332   static_assert(*kOne == 1, "");
333 
334   // Check value().
335   static_assert(kZero.value() == 0, "");
336   static_assert(kOne.value() == 1, "");
337 
338   // Check explicit conversions to underlying type.
339   static_assert(static_cast<int>(kZero) == 0, "");
340   static_assert(static_cast<int>(kOne) == 1, "");
341 
342   // Check comparison operations.
343   static_assert(kZero == kZero, "");
344   static_assert(kZero != kOne, "");
345   static_assert(kZero < kOne, "");
346   static_assert(kZero <= kOne, "");
347   static_assert(kOne > kZero, "");
348   static_assert(kOne >= kZero, "");
349 }
350 
TEST(StrongAliasTest,BooleansAreEvaluatedAsBooleans)351 TEST(StrongAliasTest, BooleansAreEvaluatedAsBooleans) {
352   using BoolAlias = StrongAlias<class BoolTag, bool>;
353 
354   BoolAlias happy(true);
355   BoolAlias sad(false);
356 
357   EXPECT_TRUE(happy);
358   EXPECT_FALSE(sad);
359   EXPECT_TRUE(*happy);
360   EXPECT_FALSE(*sad);
361 }
362 }  // namespace webrtc
363