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