blob: a87bc4de3738b697908acbec59e4a920367faa73 [file] [log] [blame]
Victor Boiviea8655192021-04-01 06:23:541/*
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 */
Victor Boivieb9182302021-09-21 12:51:0511#include "rtc_base/strong_alias.h"
Victor Boiviea8655192021-04-01 06:23:5412
13#include <cstdint>
14#include <map>
15#include <memory>
16#include <string>
17#include <type_traits>
Victor Boiviea8655192021-04-01 06:23:5418#include <utility>
19
Victor Boivie3ec9e032021-08-18 13:22:4220#include "rtc_base/containers/flat_map.h"
Victor Boiviea8655192021-04-01 06:23:5421#include "rtc_base/gunit.h"
22#include "test/gmock.h"
23
24// This is a copy of
Tony Herreb0ed1202021-07-22 15:40:4425// https://source.chromium.org/chromium/chromium/src/+/main:base/types/strong_alias_unittest.cc
Victor Boiviea8655192021-04-01 06:23:5426// 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
Victor Boivieb9182302021-09-21 12:51:0529namespace webrtc {
Victor Boiviea8655192021-04-01 06:23:5430namespace {
31
32// For test correctnenss, it's important that these getters return lexically
Artem Titov68e98fb2021-07-26 11:37:1333// incrementing values as `index` grows.
Victor Boiviea8655192021-04-01 06:23:5434template <typename T>
35T GetExampleValue(int index);
36
37template <>
38int GetExampleValue<int>(int index) {
39 return 5 + index;
40}
41template <>
42uint64_t GetExampleValue<uint64_t>(int index) {
43 return 500U + index;
44}
45
46template <>
47std::string GetExampleValue<std::string>(int index) {
48 return std::string('a', index);
49}
50
51} // namespace
52
53template <typename T>
54class StrongAliasTest : public ::testing::Test {};
55
56using TestedTypes = ::testing::Types<int, uint64_t, std::string>;
57TYPED_TEST_SUITE(StrongAliasTest, TestedTypes);
58
59TYPED_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
70TYPED_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
77TYPED_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
88TYPED_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
105TYPED_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
117TYPED_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
131TYPED_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
150TYPED_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
169TYPED_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
175TYPED_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
186TEST(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
194TYPED_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
203TYPED_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
210TYPED_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
230TYPED_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
242TEST(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
266TEST(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
Victor Boivie3ec9e032021-08-18 13:22:42283TYPED_TEST(StrongAliasTest, CanBeKeysInFlatMap) {
Victor Boiviea8655192021-04-01 06:23:54284 using FooAlias = StrongAlias<class FooTag, TypeParam>;
Victor Boivie3ec9e032021-08-18 13:22:42285 webrtc::flat_map<FooAlias, std::string> map;
Victor Boiviea8655192021-04-01 06:23:54286
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
297TYPED_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
311TYPED_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
323TEST(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}
Victor Boivie59d6e2a2021-04-20 12:25:06350
351TEST(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}
Victor Boivieb9182302021-09-21 12:51:05362} // namespace webrtc