|  | /* | 
|  | *  Copyright 2019 The Chromium Authors. All rights reserved. | 
|  | *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  | #include "rtc_base/strong_alias.h" | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <type_traits> | 
|  | #include <utility> | 
|  |  | 
|  | #include "rtc_base/containers/flat_map.h" | 
|  | #include "test/gtest.h" | 
|  |  | 
|  | // This is a copy of | 
|  | // https://source.chromium.org/chromium/chromium/src/+/main:base/types/strong_alias_unittest.cc | 
|  | // but adapted to use WebRTC's includes, remove unit tests that test the ostream | 
|  | // operator (it's removed in this port) and other adaptations to pass lint. | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace { | 
|  |  | 
|  | // For test correctnenss, it's important that these getters return lexically | 
|  | // incrementing values as `index` grows. | 
|  | template <typename T> | 
|  | T GetExampleValue(int index); | 
|  |  | 
|  | template <> | 
|  | int GetExampleValue<int>(int index) { | 
|  | return 5 + index; | 
|  | } | 
|  | template <> | 
|  | uint64_t GetExampleValue<uint64_t>(int index) { | 
|  | return 500U + index; | 
|  | } | 
|  |  | 
|  | template <> | 
|  | std::string GetExampleValue<std::string>(int index) { | 
|  | return std::string('a', index); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | template <typename T> | 
|  | class StrongAliasTest : public ::testing::Test {}; | 
|  |  | 
|  | using TestedTypes = ::testing::Types<int, uint64_t, std::string>; | 
|  | TYPED_TEST_SUITE(StrongAliasTest, TestedTypes); | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, ValueAccessesUnderlyingValue) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  |  | 
|  | // Const value getter. | 
|  | const FooAlias const_alias(GetExampleValue<TypeParam>(1)); | 
|  | EXPECT_EQ(GetExampleValue<TypeParam>(1), const_alias.value()); | 
|  | static_assert(std::is_const<typename std::remove_reference< | 
|  | decltype(const_alias.value())>::type>::value, | 
|  | "Reference returned by const value getter should be const."); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, ExplicitConversionToUnderlyingValue) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  |  | 
|  | const FooAlias const_alias(GetExampleValue<TypeParam>(1)); | 
|  | EXPECT_EQ(GetExampleValue<TypeParam>(1), static_cast<TypeParam>(const_alias)); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, CanBeCopyConstructed) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | FooAlias alias(GetExampleValue<TypeParam>(0)); | 
|  | FooAlias copy_constructed = alias; | 
|  | EXPECT_EQ(copy_constructed, alias); | 
|  |  | 
|  | FooAlias copy_assigned; | 
|  | copy_assigned = alias; | 
|  | EXPECT_EQ(copy_assigned, alias); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, CanBeMoveConstructed) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | FooAlias alias(GetExampleValue<TypeParam>(0)); | 
|  | FooAlias move_constructed = std::move(alias); | 
|  | EXPECT_EQ(move_constructed, FooAlias(GetExampleValue<TypeParam>(0))); | 
|  |  | 
|  | FooAlias alias2(GetExampleValue<TypeParam>(2)); | 
|  | FooAlias move_assigned; | 
|  | move_assigned = std::move(alias2); | 
|  | EXPECT_EQ(move_assigned, FooAlias(GetExampleValue<TypeParam>(2))); | 
|  |  | 
|  | // Check that FooAlias is nothrow move constructible. This matters for | 
|  | // performance when used in std::vectors. | 
|  | static_assert(std::is_nothrow_move_constructible<FooAlias>::value, | 
|  | "Error: Alias is not nothow move constructible"); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, CanBeConstructedFromMoveOnlyType) { | 
|  | // Note, using a move-only unique_ptr to T: | 
|  | using FooAlias = StrongAlias<class FooTag, std::unique_ptr<TypeParam>>; | 
|  |  | 
|  | FooAlias a(std::make_unique<TypeParam>(GetExampleValue<TypeParam>(0))); | 
|  | EXPECT_EQ(*a.value(), GetExampleValue<TypeParam>(0)); | 
|  |  | 
|  | auto bare_value = std::make_unique<TypeParam>(GetExampleValue<TypeParam>(1)); | 
|  | FooAlias b(std::move(bare_value)); | 
|  | EXPECT_EQ(*b.value(), GetExampleValue<TypeParam>(1)); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, MutableOperatorArrow) { | 
|  | // Note, using a move-only unique_ptr to T: | 
|  | using Ptr = std::unique_ptr<TypeParam>; | 
|  | using FooAlias = StrongAlias<class FooTag, Ptr>; | 
|  |  | 
|  | FooAlias a(std::make_unique<TypeParam>()); | 
|  | EXPECT_TRUE(a.value()); | 
|  |  | 
|  | // Check that `a` can be modified through the use of operator->. | 
|  | a->reset(); | 
|  |  | 
|  | EXPECT_FALSE(a.value()); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, MutableOperatorStar) { | 
|  | // Note, using a move-only unique_ptr to T: | 
|  | using Ptr = std::unique_ptr<TypeParam>; | 
|  | using FooAlias = StrongAlias<class FooTag, Ptr>; | 
|  |  | 
|  | FooAlias a(std::make_unique<TypeParam>()); | 
|  | FooAlias b(std::make_unique<TypeParam>()); | 
|  | EXPECT_TRUE(*a); | 
|  | EXPECT_TRUE(*b); | 
|  |  | 
|  | // Check that both the mutable l-value and r-value overloads work and we can | 
|  | // move out of the aliases. | 
|  | { Ptr ignore(*std::move(a)); } | 
|  | { Ptr ignore(std::move(*b)); } | 
|  |  | 
|  | EXPECT_FALSE(a.value()); | 
|  | EXPECT_FALSE(b.value()); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, MutableValue) { | 
|  | // Note, using a move-only unique_ptr to T: | 
|  | using Ptr = std::unique_ptr<TypeParam>; | 
|  | using FooAlias = StrongAlias<class FooTag, Ptr>; | 
|  |  | 
|  | FooAlias a(std::make_unique<TypeParam>()); | 
|  | FooAlias b(std::make_unique<TypeParam>()); | 
|  | EXPECT_TRUE(a.value()); | 
|  | EXPECT_TRUE(b.value()); | 
|  |  | 
|  | // Check that both the mutable l-value and r-value overloads work and we can | 
|  | // move out of the aliases. | 
|  | { Ptr ignore(std::move(a).value()); } | 
|  | { Ptr ignore(std::move(b.value())); } | 
|  |  | 
|  | EXPECT_FALSE(a.value()); | 
|  | EXPECT_FALSE(b.value()); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, SizeSameAsUnderlyingType) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | static_assert(sizeof(FooAlias) == sizeof(TypeParam), | 
|  | "StrongAlias should be as large as the underlying type."); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, IsDefaultConstructible) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | static_assert(std::is_default_constructible<FooAlias>::value, | 
|  | "Should be possible to default-construct a StrongAlias."); | 
|  | static_assert( | 
|  | std::is_trivially_default_constructible<FooAlias>::value == | 
|  | std::is_trivially_default_constructible<TypeParam>::value, | 
|  | "Should be possible to trivially default-construct a StrongAlias iff the " | 
|  | "underlying type is trivially default constructible."); | 
|  | } | 
|  |  | 
|  | TEST(StrongAliasTest, TrivialTypeAliasIsStandardLayout) { | 
|  | using FooAlias = StrongAlias<class FooTag, int>; | 
|  | static_assert(std::is_standard_layout<FooAlias>::value, | 
|  | "int-based alias should have standard layout. "); | 
|  | static_assert(std::is_trivially_copyable<FooAlias>::value, | 
|  | "int-based alias should be trivially copyable. "); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, CannotBeCreatedFromDifferentAlias) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | using BarAlias = StrongAlias<class BarTag, TypeParam>; | 
|  | static_assert(!std::is_constructible<FooAlias, BarAlias>::value, | 
|  | "Should be impossible to construct FooAlias from a BarAlias."); | 
|  | static_assert(!std::is_convertible<BarAlias, FooAlias>::value, | 
|  | "Should be impossible to convert a BarAlias into FooAlias."); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, CannotBeImplicitlyConverterToUnderlyingValue) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | static_assert(!std::is_convertible<FooAlias, TypeParam>::value, | 
|  | "Should be impossible to implicitly convert a StrongAlias into " | 
|  | "an underlying type."); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, ComparesEqualToSameValue) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | // Comparison to self: | 
|  | const FooAlias a = FooAlias(GetExampleValue<TypeParam>(0)); | 
|  | EXPECT_EQ(a, a); | 
|  | EXPECT_FALSE(a != a); | 
|  | EXPECT_TRUE(a >= a); | 
|  | EXPECT_TRUE(a <= a); | 
|  | EXPECT_FALSE(a > a); | 
|  | EXPECT_FALSE(a < a); | 
|  | // Comparison to other equal object: | 
|  | const FooAlias b = FooAlias(GetExampleValue<TypeParam>(0)); | 
|  | EXPECT_EQ(a, b); | 
|  | EXPECT_FALSE(a != b); | 
|  | EXPECT_TRUE(a >= b); | 
|  | EXPECT_TRUE(a <= b); | 
|  | EXPECT_FALSE(a > b); | 
|  | EXPECT_FALSE(a < b); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, ComparesCorrectlyToDifferentValue) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | const FooAlias a = FooAlias(GetExampleValue<TypeParam>(0)); | 
|  | const FooAlias b = FooAlias(GetExampleValue<TypeParam>(1)); | 
|  | EXPECT_NE(a, b); | 
|  | EXPECT_FALSE(a == b); | 
|  | EXPECT_TRUE(b >= a); | 
|  | EXPECT_TRUE(a <= b); | 
|  | EXPECT_TRUE(b > a); | 
|  | EXPECT_TRUE(a < b); | 
|  | } | 
|  |  | 
|  | TEST(StrongAliasTest, CanBeDerivedFrom) { | 
|  | // Aliases can be enriched by custom operations or validations if needed. | 
|  | // Ideally, one could go from a 'using' declaration to a derived class to add | 
|  | // those methods without the need to change any other code. | 
|  | class CountryCode : public StrongAlias<CountryCode, std::string> { | 
|  | public: | 
|  | explicit CountryCode(const std::string& value) | 
|  | : StrongAlias<CountryCode, std::string>::StrongAlias(value) { | 
|  | if (value_.length() != 2) { | 
|  | // Country code invalid! | 
|  | value_.clear();  // is_null() will return true. | 
|  | } | 
|  | } | 
|  |  | 
|  | bool is_null() const { return value_.empty(); } | 
|  | }; | 
|  |  | 
|  | CountryCode valid("US"); | 
|  | EXPECT_FALSE(valid.is_null()); | 
|  |  | 
|  | CountryCode invalid("United States"); | 
|  | EXPECT_TRUE(invalid.is_null()); | 
|  | } | 
|  |  | 
|  | TEST(StrongAliasTest, CanWrapComplexStructures) { | 
|  | // A pair of strings implements odering and can, in principle, be used as | 
|  | // a base of StrongAlias. | 
|  | using PairOfStrings = std::pair<std::string, std::string>; | 
|  | using ComplexAlias = StrongAlias<class FooTag, PairOfStrings>; | 
|  |  | 
|  | ComplexAlias a1{std::make_pair("aaa", "bbb")}; | 
|  | ComplexAlias a2{std::make_pair("ccc", "ddd")}; | 
|  | EXPECT_TRUE(a1 < a2); | 
|  |  | 
|  | EXPECT_TRUE(a1.value() == PairOfStrings("aaa", "bbb")); | 
|  |  | 
|  | // Note a caveat, an std::pair doesn't have an overload of operator<<, and it | 
|  | // cannot be easily added since ADL rules would require it to be in the std | 
|  | // namespace. So we can't print ComplexAlias. | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, CanBeKeysInFlatMap) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | flat_map<FooAlias, std::string> map; | 
|  |  | 
|  | FooAlias k1(GetExampleValue<TypeParam>(0)); | 
|  | FooAlias k2(GetExampleValue<TypeParam>(1)); | 
|  |  | 
|  | map[k1] = "value1"; | 
|  | map[k2] = "value2"; | 
|  |  | 
|  | EXPECT_EQ(map[k1], "value1"); | 
|  | EXPECT_EQ(map[k2], "value2"); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, CanBeKeysInStdMap) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | std::map<FooAlias, std::string> map; | 
|  |  | 
|  | FooAlias k1(GetExampleValue<TypeParam>(0)); | 
|  | FooAlias k2(GetExampleValue<TypeParam>(1)); | 
|  |  | 
|  | map[k1] = "value1"; | 
|  | map[k2] = "value2"; | 
|  |  | 
|  | EXPECT_EQ(map[k1], "value1"); | 
|  | EXPECT_EQ(map[k2], "value2"); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(StrongAliasTest, CanDifferentiateOverloads) { | 
|  | using FooAlias = StrongAlias<class FooTag, TypeParam>; | 
|  | using BarAlias = StrongAlias<class BarTag, TypeParam>; | 
|  | class Scope { | 
|  | public: | 
|  | static std::string Overload(FooAlias /*unused*/) { return "FooAlias"; } | 
|  | static std::string Overload(BarAlias /*unused*/) { return "BarAlias"; } | 
|  | }; | 
|  | EXPECT_EQ("FooAlias", Scope::Overload(FooAlias())); | 
|  | EXPECT_EQ("BarAlias", Scope::Overload(BarAlias())); | 
|  | } | 
|  |  | 
|  | TEST(StrongAliasTest, EnsureConstexpr) { | 
|  | using FooAlias = StrongAlias<class FooTag, int>; | 
|  |  | 
|  | // Check constructors. | 
|  | static constexpr FooAlias kZero{}; | 
|  | static constexpr FooAlias kOne(1); | 
|  |  | 
|  | // Check operator*. | 
|  | static_assert(*kZero == 0, ""); | 
|  | static_assert(*kOne == 1, ""); | 
|  |  | 
|  | // Check value(). | 
|  | static_assert(kZero.value() == 0, ""); | 
|  | static_assert(kOne.value() == 1, ""); | 
|  |  | 
|  | // Check explicit conversions to underlying type. | 
|  | static_assert(static_cast<int>(kZero) == 0, ""); | 
|  | static_assert(static_cast<int>(kOne) == 1, ""); | 
|  |  | 
|  | // Check comparison operations. | 
|  | static_assert(kZero == kZero, ""); | 
|  | static_assert(kZero != kOne, ""); | 
|  | static_assert(kZero < kOne, ""); | 
|  | static_assert(kZero <= kOne, ""); | 
|  | static_assert(kOne > kZero, ""); | 
|  | static_assert(kOne >= kZero, ""); | 
|  | } | 
|  |  | 
|  | TEST(StrongAliasTest, BooleansAreEvaluatedAsBooleans) { | 
|  | using BoolAlias = StrongAlias<class BoolTag, bool>; | 
|  |  | 
|  | BoolAlias happy(true); | 
|  | BoolAlias sad(false); | 
|  |  | 
|  | EXPECT_TRUE(happy); | 
|  | EXPECT_FALSE(sad); | 
|  | EXPECT_TRUE(*happy); | 
|  | EXPECT_FALSE(*sad); | 
|  | } | 
|  | }  // namespace webrtc |