xref: /aosp_15_r20/external/federated-compute/fcp/base/meta_test.cc (revision 14675a029014e728ec732f129a32e299b2da0601)
1 /*
2  * Copyright 2019 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "fcp/base/meta.h"
18 
19 #include <functional>
20 #include <memory>
21 #include <optional>
22 #include <type_traits>
23 #include <utility>
24 #include <vector>
25 
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 
29 namespace fcp {
30 
31 using ::testing::Eq;
32 using ::testing::Not;
33 
34 struct R {
35   virtual ~R() = default;
36   virtual bool Virt1(int i) = 0;
37   virtual bool Virt2(int i, int j) = 0;
38   virtual void Virt3() = 0;
NonVirt1fcp::R39   int NonVirt1() { return 1; }
NonVirt2fcp::R40   int NonVirt2() { return 2; }
41   char field;
42 };
43 
44 struct S {
45   virtual ~S() = default;
46   virtual bool Virt1(int i) = 0;
47   virtual bool Virt2(int i, int j) = 0;
48   virtual void Virt3() = 0;
NonVirt1fcp::S49   int NonVirt1() { return 1; }
NonVirt2fcp::S50   int NonVirt2() { return 2; }
51   char field;
52 };
53 
54 //
55 // Compile-time tests for MemberPointerTraits
56 //
57 #define STATIC_ASSERT_TARGET_TYPE(member, type)                           \
58   static_assert(                                                          \
59       std::is_same<MemberPointerTraits<decltype(&S::member)>::TargetType, \
60                    type>::value,                                          \
61       "Incorrect target type from MemberPointerTraits");
62 
63 // For some reason the linter otherwise thinks e.g. 'bool(int)' is an old-style
64 // cast.
65 template<typename T>
66 struct Id { using Type = T; };
67 
68 STATIC_ASSERT_TARGET_TYPE(Virt1, Id<bool(int)>::Type);
69 STATIC_ASSERT_TARGET_TYPE(Virt2, Id<bool(int, int)>::Type);
70 STATIC_ASSERT_TARGET_TYPE(Virt3, Id<void()>::Type);
71 STATIC_ASSERT_TARGET_TYPE(NonVirt1, Id<int()>::Type);
72 STATIC_ASSERT_TARGET_TYPE(NonVirt2, Id<int()>::Type);
73 STATIC_ASSERT_TARGET_TYPE(field, Id<char>::Type);
74 //
75 // End compile-time tests for MemberPointerTraits
76 //
77 
78 template<typename T>
79 struct TypeIdHolder {
80   static constexpr char kTarget = '\0';
81 };
82 template<typename T>
83 constexpr char TypeIdHolder<T>::kTarget;
84 
85 // This gives us a unique runtime value per unique type - with which its much
86 // easier to verify uniqueness (below).
87 template<typename T>
TypeId()88 static constexpr char const* TypeId() {
89   return &TypeIdHolder<T>::kTarget;
90 }
91 
92 //
93 // LIFT_MEMBER_TO_TYPE
94 //
95 
TEST(MetaTest,MemberTagUniqueness)96 TEST(MetaTest, MemberTagUniqueness) {
97   std::vector<char const*> type_ids = {
98     TypeId<LIFT_MEMBER_TO_TYPE(S, Virt1)>(),
99     TypeId<LIFT_MEMBER_TO_TYPE(S, Virt2)>(),
100     TypeId<LIFT_MEMBER_TO_TYPE(S, Virt3)>(),
101     TypeId<LIFT_MEMBER_TO_TYPE(S, NonVirt1)>(),
102     TypeId<LIFT_MEMBER_TO_TYPE(S, NonVirt2)>(),
103     TypeId<LIFT_MEMBER_TO_TYPE(S, field)>(),
104     TypeId<LIFT_MEMBER_TO_TYPE(R, Virt1)>(),
105     TypeId<LIFT_MEMBER_TO_TYPE(R, Virt2)>(),
106     TypeId<LIFT_MEMBER_TO_TYPE(R, Virt3)>(),
107     TypeId<LIFT_MEMBER_TO_TYPE(R, NonVirt1)>(),
108     TypeId<LIFT_MEMBER_TO_TYPE(R, NonVirt2)>(),
109     TypeId<LIFT_MEMBER_TO_TYPE(R, field)>(),
110   };
111 
112   for (int i = 0; i < type_ids.size(); i++) {
113     for (int j = 0; j < type_ids.size(); j++) {
114       if (i == j) {
115         continue;
116       }
117       EXPECT_THAT(type_ids[i], Not(Eq(type_ids[j])))
118           << "Member tags must be unique";
119     }
120   }
121 }
122 
PokeMemberCase(LIFT_MEMBER_TO_TYPE (S,Virt1))123 int PokeMemberCase(LIFT_MEMBER_TO_TYPE(S, Virt1)) {
124   return 1;
125 }
126 
PokeMemberCase(LIFT_MEMBER_TO_TYPE (S,Virt2))127 int PokeMemberCase(LIFT_MEMBER_TO_TYPE(S, Virt2)) {
128   return 2;
129 }
130 
131 template<typename R, R S::* M>
PokeMember()132 int PokeMember() {
133   return PokeMemberCase(LIFT_MEMBER_POINTER_TO_TYPE(M){});
134 }
135 
TEST(MetaTest,MemberTagDispatch)136 TEST(MetaTest, MemberTagDispatch) {
137   EXPECT_THAT((PokeMember<bool(int), &S::Virt1>()), Eq(1));
138   EXPECT_THAT((PokeMember<bool(int, int), &S::Virt2>()), Eq(2));
139 }
140 
141 //
142 // CastContainerElements
143 //
144 
145 struct X {
tagfcp::X146   static constexpr int tag() { return 1; }
147 };
148 
149 struct Y {
tagfcp::Y150   static constexpr int tag() { return 2; }
151 };
152 
153 template<typename T>
154 struct TypedVal {
155   int value;
156 
operator ==fcp::TypedVal157   bool operator==(TypedVal<T> const& other) const {
158     return other.value == value;
159   }
operator !=fcp::TypedVal160   bool operator!=(TypedVal<T> const& other) const { return !(*this == other); }
161 };
162 
163 struct UntypedVal {
164   int tag;
165   int value;
166 };
167 
168 struct CastValByTag {
169   template <typename T>
170   using TargetType = std::optional<TypedVal<T>>;
171 
172   template <typename T>
Castfcp::CastValByTag173   TargetType<T> Cast(UntypedVal const& val) const {
174     if (val.tag == T::tag()) {
175       return TypedVal<T>{val.value};
176     } else {
177       return std::nullopt;
178     }
179   }
180 };
181 
TEST(MetaTest,CastContainerElements_AllSuccess)182 TEST(MetaTest, CastContainerElements_AllSuccess) {
183   std::vector<UntypedVal> v{
184     {X::tag(), 123},
185     {Y::tag(), 456},
186     {X::tag(), 789}
187   };
188 
189   auto actual = CastContainerElements<X, Y, X>(v, CastValByTag{});
190   auto expected = std::make_tuple(absl::make_optional(TypedVal<X>{123}),
191                                   absl::make_optional(TypedVal<Y>{456}),
192                                   absl::make_optional(TypedVal<X>{789}));
193 
194   EXPECT_THAT(actual, Eq(expected));
195 }
196 
TEST(MetaTest,CastContainerElements_AllSuccess_Pack)197 TEST(MetaTest, CastContainerElements_AllSuccess_Pack) {
198   std::vector<UntypedVal> v{
199     {X::tag(), 123},
200     {Y::tag(), 456},
201     {X::tag(), 789}
202   };
203 
204   // This uses the Pack<> overload instead.
205   auto actual = CastContainerElements(Pack<X, Y, X>{}, v, CastValByTag{});
206   auto expected = std::make_tuple(absl::make_optional(TypedVal<X>{123}),
207                                   absl::make_optional(TypedVal<Y>{456}),
208                                   absl::make_optional(TypedVal<X>{789}));
209 
210   EXPECT_THAT(actual, Eq(expected));
211 }
212 
TEST(MetaTest,CastContainerElements_OneFails)213 TEST(MetaTest, CastContainerElements_OneFails) {
214   std::vector<UntypedVal> v{
215     {X::tag(), 123},
216     {X::tag(), 456},
217     {X::tag(), 789}
218   };
219 
220   // Second element does not have the tag for Y.
221   auto actual = CastContainerElements<X, Y, X>(v, CastValByTag{});
222   auto expected = std::make_tuple(absl::make_optional(TypedVal<X>{123}),
223                                   std::optional<TypedVal<Y>>(std::nullopt),
224                                   absl::make_optional(TypedVal<X>{789}));
225 
226   EXPECT_THAT(actual, Eq(expected));
227 }
228 
229 //
230 // MAKE_LINK and LinkedType<>
231 //
232 
233 namespace links {
234 
235 namespace a {
236 
237 struct A1 {};
238 struct A2 {};
239 struct A3 {};
240 
241 MAKE_LINK(A1, A2);
242 
243 }  // namespace a
244 
245 namespace b {
246 
247 struct B1 {};
248 
249 MAKE_LINK(B1, a::A3);
250 
251 }  // namespace b
252 
253 }  // namespace links
254 
255 static_assert(std::is_same<LinkedType<links::a::A1>, links::a::A2>::value,
256               "A1 -> A2");
257 static_assert(HasLinkedType<links::a::A1>(), "A1 -> A2");
258 static_assert(std::is_same<LinkedTypeOrVoid<links::a::A2>, void>::value,
259               "A2 -/>");
260 static_assert(!HasLinkedType<links::a::A2>(), "A2 -/>");
261 static_assert(std::is_same<LinkedTypeOrVoid<links::a::A3>, void>::value,
262               "A3 -/>");
263 static_assert(!HasLinkedType<links::a::A3>(), "A3 -/>");
264 static_assert(std::is_same<LinkedType<links::b::B1>, links::a::A3>::value,
265               "b::B1 -> a::A3");
266 static_assert(HasLinkedType<links::b::B1>(), "b::B1 -> a::A3");
267 
268 //
269 // Pack<>
270 //
271 
272 template<typename A1, typename A2, size_t I1, size_t I2>
CheckUnpack()273 constexpr Unit CheckUnpack() {
274   static_assert(std::is_same<A1, X>::value, "A1 == X");
275   static_assert(std::is_same<A2, Y>::value, "A2 == Y");
276   static_assert(I1 == 0, "I1 == 0");
277   static_assert(I2 == 1, "I2 == 0");
278   return {};
279 }
280 
281 template<typename... A, size_t... I>
UsePack(Pack<A...>,absl::index_sequence<I...>)282 constexpr Unit UsePack(Pack<A...>, absl::index_sequence<I...>) {
283   return CheckUnpack<A..., I...>();
284 }
285 
286 template<typename... A>
MakeAndUsePack()287 constexpr Unit MakeAndUsePack() {
288   return UsePack(Pack<A...>{}, Pack<A...>::MakeIndexSequence());
289 }
290 
291 static_assert(MakeAndUsePack<X, Y>().True(), "Pack<>");
292 
293 //
294 // LiftVoidReturn
295 //
296 
TEST(MetaTest,LiftVoidReturn_Void)297 TEST(MetaTest, LiftVoidReturn_Void) {
298   int counter = 0;
299   std::function<void()> f = [&counter]() { counter++; };
300 
301   f();
302   EXPECT_THAT(counter, Eq(1));
303   auto f_wrapped = LiftVoidReturn(f);
304   EXPECT_THAT(f_wrapped(), Eq(Unit{}));
305   EXPECT_THAT(counter, Eq(2));
306 }
307 
TEST(MetaTest,LiftVoidReturn_Void_Args)308 TEST(MetaTest, LiftVoidReturn_Void_Args) {
309   int counter = 0;
310   std::function<void(int)> f = [&counter](int i) { counter += i; };
311 
312   f(10);
313   EXPECT_THAT(counter, Eq(10));
314   auto f_wrapped = LiftVoidReturn(f);
315   EXPECT_THAT(f_wrapped(32), Eq(Unit{}));
316   EXPECT_THAT(counter, Eq(42));
317 }
318 
TEST(MetaTest,LiftVoidReturn_NonVoid)319 TEST(MetaTest, LiftVoidReturn_NonVoid) {
320   int counter = 0;
321   std::function<int(int)> f = [&counter](int i) {
322     counter += i;
323     return counter;
324   };
325 
326   EXPECT_THAT(f(10), Eq(10));
327   EXPECT_THAT(counter, Eq(10));
328   auto f_wrapped = LiftVoidReturn(f);
329   EXPECT_THAT(f_wrapped(32), Eq(42));
330   EXPECT_THAT(counter, Eq(42));
331 }
332 
TEST(MetaTest,LiftVoidReturn_Mutable)333 TEST(MetaTest, LiftVoidReturn_Mutable) {
334   int r = -1;
335   auto f = [&r, counter = 0]() mutable {
336     counter++;
337     r = counter;
338   };
339 
340   f();
341   EXPECT_THAT(r, Eq(1));
342   auto f_wrapped = LiftVoidReturn(f);
343   EXPECT_THAT(f_wrapped(), Eq(Unit{}));
344   EXPECT_THAT(r, Eq(2));
345 }
346 
TEST(MetaTest,LiftVoidReturn_MutableAndMoveOnly)347 TEST(MetaTest, LiftVoidReturn_MutableAndMoveOnly) {
348   int r = -1;
349   auto f = [&r, counter = std::make_unique<int>(0)]() mutable {
350     (*counter)++;
351     r = *counter;
352   };
353 
354   f();
355   EXPECT_THAT(r, Eq(1));
356   auto f_wrapped = LiftVoidReturn(std::move(f));
357   EXPECT_THAT(f_wrapped(), Eq(Unit{}));
358   EXPECT_THAT(r, Eq(2));
359 }
360 
361 //
362 // FunctionTraits
363 //
364 
365 #define STATIC_ASSERT_FUNCTION_TRAITS(fn, r, ...)                              \
366   static_assert(std::is_same<FunctionTraits<fn>::ResultType, r>::value,        \
367                 "Incorrect result type from FunctionTraits");                  \
368   static_assert(                                                               \
369       std::is_same<FunctionTraits<fn>::ArgPackType, Pack<__VA_ARGS__>>::value, \
370       "Incorrect arg pack from FunctionTraits")
371 
372 STATIC_ASSERT_FUNCTION_TRAITS(void(), void);
373 STATIC_ASSERT_FUNCTION_TRAITS(void(int, char), void, int, char);
374 STATIC_ASSERT_FUNCTION_TRAITS(Identity<bool(char const*, int)>, bool,
375                               char const*, int);
376 
TEST(MetaTest,IsTypeOneOf)377 TEST(MetaTest, IsTypeOneOf) {
378   static_assert(IsTypeOneOf<int, int>());
379   static_assert(IsTypeOneOf<int, int, double>());
380   static_assert(IsTypeOneOf<int, double, int>());
381   static_assert(!IsTypeOneOf<int, bool>());
382   static_assert(!IsTypeOneOf<int, double, char>());
383 }
384 
TEST(MetaTest,IsSubsetOf)385 TEST(MetaTest, IsSubsetOf) {
386   using T1 = Pack<int, double>;
387   using T2 = Pack<double, int>;
388   using T3 = Pack<int, double, char>;
389 
390   static_assert(IsSubsetOf<T1, T1>::value);
391   static_assert(IsSubsetOf<T1, T2>::value);
392   static_assert(IsSubsetOf<T2, T1>::value);
393   static_assert(IsSubsetOf<T2, T3>::value);
394   static_assert(!IsSubsetOf<T3, T2>::value);
395 }
396 
397 }  // namespace fcp
398