1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/traits_bag.h"
6 
7 #include "testing/gmock/include/gmock/gmock.h"
8 
9 namespace base {
10 namespace trait_helpers {
11 namespace {
12 
13 struct ExampleTrait {};
14 
15 struct ExampleTrait2 {};
16 
17 enum class EnumTraitA { A, B, C };
18 
19 enum class EnumTraitB { ONE, TWO };
20 
21 struct TestTraits {
22   // List of traits that are valid inputs for the constructor below.
23   struct ValidTrait {
24     ValidTrait(ExampleTrait);
25     ValidTrait(EnumTraitA);
26     ValidTrait(EnumTraitB);
27   };
28 
29   template <class... ArgTypes,
30             class CheckArgumentsAreValid = std::enable_if_t<
31                 trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
TestTraitsbase::trait_helpers::__anonafc9b2220111::TestTraits32   constexpr TestTraits(ArgTypes... args)
33       : has_example_trait(trait_helpers::HasTrait<ExampleTrait, ArgTypes...>()),
34         enum_trait_a(
35             trait_helpers::GetEnum<EnumTraitA, EnumTraitA::A>(args...)),
36         enum_trait_b(
37             trait_helpers::GetEnum<EnumTraitB, EnumTraitB::ONE>(args...)) {}
38 
39   const bool has_example_trait;
40   const EnumTraitA enum_trait_a;
41   const EnumTraitB enum_trait_b;
42 };
43 
44 // Like TestTraits, except ExampleTrait is filtered away.
45 struct FilteredTestTraits : public TestTraits {
46   template <class... ArgTypes,
47             class CheckArgumentsAreValid = std::enable_if_t<
48                 trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
FilteredTestTraitsbase::trait_helpers::__anonafc9b2220111::FilteredTestTraits49   constexpr FilteredTestTraits(ArgTypes... args)
50       : TestTraits(Exclude<ExampleTrait>::Filter(args)...) {}
51 };
52 
53 struct RequiredEnumTestTraits {
54   // List of traits that are required inputs for the constructor below.
55   struct ValidTrait {
56     ValidTrait(EnumTraitA);
57   };
58 
59   // We require EnumTraitA to be specified.
60   template <class... ArgTypes,
61             class CheckArgumentsAreValid = std::enable_if_t<
62                 trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
RequiredEnumTestTraitsbase::trait_helpers::__anonafc9b2220111::RequiredEnumTestTraits63   constexpr RequiredEnumTestTraits(ArgTypes... args)
64       : enum_trait_a(trait_helpers::GetEnum<EnumTraitA>(args...)) {}
65 
66   const EnumTraitA enum_trait_a;
67 };
68 
69 struct OptionalEnumTestTraits {
70   // List of traits that are optional inputs for the constructor below.
71   struct ValidTrait {
72     ValidTrait(EnumTraitA);
73   };
74 
75   // EnumTraitA can optionally be specified.
76   template <class... ArgTypes,
77             class CheckArgumentsAreValid = std::enable_if_t<
78                 trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
OptionalEnumTestTraitsbase::trait_helpers::__anonafc9b2220111::OptionalEnumTestTraits79   constexpr OptionalEnumTestTraits(ArgTypes... args)
80       : enum_trait_a(trait_helpers::GetOptionalEnum<EnumTraitA>(args...)) {}
81 
82   const Optional<EnumTraitA> enum_trait_a;
83 };
84 
85 }  // namespace
86 
TEST(TraitsBagTest,DefaultConstructor)87 TEST(TraitsBagTest, DefaultConstructor) {
88   constexpr TestTraits trait_test_class;
89 
90   EXPECT_FALSE(trait_test_class.has_example_trait);
91 }
92 
TEST(TraitsBagTest,HasTrait)93 TEST(TraitsBagTest, HasTrait) {
94   constexpr TestTraits with_trait(ExampleTrait{});
95   constexpr TestTraits without_trait;
96 
97   EXPECT_TRUE(with_trait.has_example_trait);
98   EXPECT_FALSE(without_trait.has_example_trait);
99 }
100 
TEST(TraitsBagTest,GetEnumWithDefault)101 TEST(TraitsBagTest, GetEnumWithDefault) {
102   constexpr TestTraits defaults;
103 
104   EXPECT_EQ(defaults.enum_trait_a, EnumTraitA::A);
105   EXPECT_EQ(defaults.enum_trait_b, EnumTraitB::ONE);
106 
107   constexpr TestTraits a(EnumTraitA::A);
108   constexpr TestTraits b(EnumTraitA::B);
109   constexpr TestTraits c(EnumTraitA::C);
110 
111   EXPECT_EQ(a.enum_trait_a, EnumTraitA::A);
112   EXPECT_EQ(a.enum_trait_b, EnumTraitB::ONE);
113 
114   EXPECT_EQ(b.enum_trait_a, EnumTraitA::B);
115   EXPECT_EQ(b.enum_trait_b, EnumTraitB::ONE);
116 
117   EXPECT_EQ(c.enum_trait_a, EnumTraitA::C);
118   EXPECT_EQ(c.enum_trait_b, EnumTraitB::ONE);
119 
120   constexpr TestTraits a_one(EnumTraitA::A, EnumTraitB::ONE);
121   constexpr TestTraits b_one(EnumTraitA::B, EnumTraitB::ONE);
122   constexpr TestTraits c_one(EnumTraitA::C, EnumTraitB::ONE);
123 
124   EXPECT_EQ(a_one.enum_trait_a, EnumTraitA::A);
125   EXPECT_EQ(a_one.enum_trait_b, EnumTraitB::ONE);
126 
127   EXPECT_EQ(b_one.enum_trait_a, EnumTraitA::B);
128   EXPECT_EQ(b_one.enum_trait_b, EnumTraitB::ONE);
129 
130   EXPECT_EQ(c_one.enum_trait_a, EnumTraitA::C);
131   EXPECT_EQ(c_one.enum_trait_b, EnumTraitB::ONE);
132 
133   constexpr TestTraits a_two(EnumTraitA::A, EnumTraitB::TWO);
134   constexpr TestTraits b_two(EnumTraitA::B, EnumTraitB::TWO);
135   constexpr TestTraits c_two(EnumTraitA::C, EnumTraitB::TWO);
136 
137   EXPECT_EQ(a_two.enum_trait_a, EnumTraitA::A);
138   EXPECT_EQ(a_two.enum_trait_b, EnumTraitB::TWO);
139 
140   EXPECT_EQ(b_two.enum_trait_a, EnumTraitA::B);
141   EXPECT_EQ(b_two.enum_trait_b, EnumTraitB::TWO);
142 
143   EXPECT_EQ(c_two.enum_trait_a, EnumTraitA::C);
144   EXPECT_EQ(c_two.enum_trait_b, EnumTraitB::TWO);
145 }
146 
TEST(TraitsBagTest,RequiredEnum)147 TEST(TraitsBagTest, RequiredEnum) {
148   constexpr RequiredEnumTestTraits a(EnumTraitA::A);
149   constexpr RequiredEnumTestTraits b(EnumTraitA::B);
150   constexpr RequiredEnumTestTraits c(EnumTraitA::C);
151 
152   EXPECT_EQ(a.enum_trait_a, EnumTraitA::A);
153   EXPECT_EQ(b.enum_trait_a, EnumTraitA::B);
154   EXPECT_EQ(c.enum_trait_a, EnumTraitA::C);
155 }
156 
TEST(TraitsBagTest,OptionalEnum)157 TEST(TraitsBagTest, OptionalEnum) {
158   constexpr OptionalEnumTestTraits not_set;
159   constexpr OptionalEnumTestTraits set(EnumTraitA::B);
160 
161   EXPECT_FALSE(not_set.enum_trait_a.has_value());
162   ASSERT_TRUE(set.enum_trait_a.has_value());
163   EXPECT_EQ(*set.enum_trait_a, EnumTraitA::B);
164 }
165 
TEST(TraitsBagTest,ValidTraitInheritance)166 TEST(TraitsBagTest, ValidTraitInheritance) {
167   struct ValidTraitsA {
168     ValidTraitsA(EnumTraitA);
169   };
170 
171   struct ValidTraitsB {
172     ValidTraitsB(ValidTraitsA);
173     ValidTraitsB(EnumTraitB);
174   };
175 
176   static_assert(AreValidTraits<ValidTraitsA, EnumTraitA>(), "");
177   static_assert(AreValidTraits<ValidTraitsB, EnumTraitA, EnumTraitB>(), "");
178 }
179 
TEST(TraitsBagTest,Filtering)180 TEST(TraitsBagTest, Filtering) {
181   using Predicate = Exclude<ExampleTrait, EnumTraitA>;
182   static_assert(
183       std::is_same<ExampleTrait2,
184                    decltype(Predicate::Filter(ExampleTrait2{}))>::value,
185       "ExampleTrait2 should not be filtered");
186 
187   static_assert(
188       std::is_same<EmptyTrait,
189                    decltype(Predicate::Filter(ExampleTrait{}))>::value,
190       "ExampleTrait should be filtered");
191 
192   static_assert(std::is_same<EmptyTrait,
193                              decltype(Predicate::Filter(EnumTraitA::A))>::value,
194                 "EnumTraitA should be filtered");
195 
196   static_assert(
197       std::is_same<EnumTraitB,
198                    decltype(Predicate::Filter(EnumTraitB::TWO))>::value,
199       "EnumTraitB should not be filtered");
200 
201   static_assert(std::is_same<EmptyTrait,
202                              decltype(Predicate::Filter(EmptyTrait{}))>::value,
203                 "EmptyTrait should not be filtered");
204 }
205 
TEST(TraitsBagTest,FilteredTestTraits)206 TEST(TraitsBagTest, FilteredTestTraits) {
207   FilteredTestTraits filtered(ExampleTrait(), EnumTraitA::C, EnumTraitB::TWO);
208 
209   // ExampleTrait should have been filtered away.
210   EXPECT_FALSE(filtered.has_example_trait);
211 
212   // The other traits should have been set however.
213   EXPECT_EQ(filtered.enum_trait_a, EnumTraitA::C);
214   EXPECT_EQ(filtered.enum_trait_b, EnumTraitB::TWO);
215 }
216 
TEST(TraitsBagTest,EmptyTraitIsValid)217 TEST(TraitsBagTest, EmptyTraitIsValid) {
218   static_assert(IsValidTrait<TestTraits::ValidTrait, EmptyTrait>(), "");
219 }
220 
221 }  // namespace trait_helpers
222 }  // namespace base
223