1 /*
2  * Copyright (C) 2018 The Android Open Source Project
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 <common/introspection.h>
18 
19 #include <ostream>
20 #include <sstream>
21 #include <string>
22 
23 #include <gtest/gtest.h>
24 
25 struct TestStructXyz {
26   int x;
27   double y;
28   char z;
29 };
30 
31 IORAP_INTROSPECT_ADAPT_STRUCT(TestStructXyz, x, y, z);
32 
33 namespace iorap {
34 namespace introspect {
35 
TEST(Introspection,ReadValues)36 TEST(Introspection, ReadValues) {
37   TestStructXyz xyz = {1,2.1,'x'};
38 
39   std::stringstream ss;
40 
41   for_each_member_field_value(xyz, [&](auto&& value) {
42     ss << value << ",";
43   });
44 
45   EXPECT_EQ(std::string("1,2.1,x,"), ss.str());
46 }
47 
48 template <typename TestType, typename TargetType>
is_same_after_decay()49 constexpr bool is_same_after_decay() {
50   return std::is_same_v<TestType, std::decay_t<TargetType>>;
51 }
52 #define IS_SAME_AFTER_DECAY(test, target_variable) \
53   is_same_after_decay<test, decltype(target_variable)>()
54 
55 template <typename TestType, typename TargetType>
is_type_same_after_decay(basic_type<TargetType>)56 constexpr bool is_type_same_after_decay(basic_type<TargetType>) {
57   return std::is_same_v<TestType, std::decay_t<TargetType>>;
58 }
59 
60 #define IS_TYPE_SAME_AFTER_DECAY(test, target_type_variable) \
61   is_type_same_after_decay<test>(CONSTEXPRIFY_TYPE(target_type_variable))
62 
63 #define CONSTEXPRIFY_TYPE(type_var) \
64   decltype(type_var){}
65 
TEST(Introspection,ForEachmemberFieldSetValues)66 TEST(Introspection, ForEachmemberFieldSetValues) {
67   TestStructXyz xyz{};
68   TestStructXyz xyz_expected = {1,2.1,'x'};
69 
70   std::stringstream ss;
71 
72   for_each_member_field_set_value(xyz, [&ss](auto&& value) {
73     // This is really confusing, value is type<?>.
74     // It should probably be merely the old value.
75     //
76     // The way the functions works now is more like an inplace_map.
77 
78     if constexpr (IS_TYPE_SAME_AFTER_DECAY(int, value)) {
79       // value = 1;
80       ss << "int,";
81       return 1;
82     } else if constexpr (IS_TYPE_SAME_AFTER_DECAY(double, value)) {
83       // value = 2.1;
84       ss << "double,";
85       return 2.1;
86     } else if constexpr (IS_TYPE_SAME_AFTER_DECAY(char, value)) {
87       // value = 'x';
88       ss << "char,";
89       return 'x';
90     } else {
91       STATIC_FAIL_DT(value, "Unhandled type");
92     }
93   });
94 
95   EXPECT_EQ(std::string("int,double,char,"), ss.str());
96 
97   EXPECT_EQ(xyz_expected.x, xyz.x);
98   EXPECT_EQ(xyz_expected.y, xyz.y);
99   EXPECT_EQ(xyz_expected.z, xyz.z);
100 }
101 
TEST(Introspection,MemberFieldSetValue)102 TEST(Introspection, MemberFieldSetValue) {
103   TestStructXyz xyz{};
104   TestStructXyz xyz_expected = {1,2.1,'x'};
105 
106   std::stringstream ss;
107 
108   auto&& [member_x, member_y, member_z]  = introspect_members(type_c<TestStructXyz>);
109   member_x.set_value(xyz, 1);
110   member_y.set_value(xyz, 2.1);
111   member_z.set_value(xyz, 'x');
112 
113   EXPECT_EQ(xyz_expected.x, xyz.x);
114   EXPECT_EQ(xyz_expected.y, xyz.y);
115   EXPECT_EQ(xyz_expected.z, xyz.z);
116 }
117 
118 template <typename M, typename T, typename V>
call_set_value(M member_type,T && self,V && value)119 constexpr void call_set_value(M member_type, T&& self, V&& value) {
120   member_type.set_value(std::forward<T>(self), std::forward<V>(value));
121 }
122 
TEST(Introspection,MemberFieldSetValueIndirect)123 TEST(Introspection, MemberFieldSetValueIndirect) {
124   TestStructXyz xyz{};
125   TestStructXyz xyz_expected = {1,2.1,'x'};
126 
127   std::stringstream ss;
128 
129   auto&& [member_x, member_y, member_z]  = introspect_members(type_c<TestStructXyz>);
130   call_set_value(member_x, xyz, 1);
131   call_set_value(member_y, xyz, 2.1);
132   call_set_value(member_z, xyz, 'x');
133 
134   EXPECT_EQ(xyz_expected.x, xyz.x);
135   EXPECT_EQ(xyz_expected.y, xyz.y);
136   EXPECT_EQ(xyz_expected.z, xyz.z);
137 }
138 
139 template <typename M, typename T, typename V>
call_set_value_lambda(M member_type,T && self,V && value)140 constexpr void call_set_value_lambda(M member_type, T&& self, V&& value) {
141   ([member_type, &self](auto&& value) mutable {
142     member_type.set_value(std::forward<T>(self), std::forward<V>(value));
143   })(std::forward<V>(value));
144 }
145 
TEST(Introspection,MemberFieldSetValueIndirectLambda)146 TEST(Introspection, MemberFieldSetValueIndirectLambda) {
147   TestStructXyz xyz{};
148   TestStructXyz xyz_expected = {1,2.1,'x'};
149 
150   std::stringstream ss;
151 
152   auto&& [member_x, member_y, member_z]  = introspect_members(type_c<TestStructXyz>);
153   call_set_value_lambda(member_x, xyz, 1);
154   call_set_value_lambda(member_y, xyz, 2.1);
155   call_set_value_lambda(member_z, xyz, 'x');
156 
157   EXPECT_EQ(xyz_expected.x, xyz.x);
158   EXPECT_EQ(xyz_expected.y, xyz.y);
159   EXPECT_EQ(xyz_expected.z, xyz.z);
160 }
161 
162 struct Simple {
163   int x;
164 };
165 
166 template <typename T, typename V>
call_set_simple_value_with_lambda(T && self,V && value)167 constexpr void call_set_simple_value_with_lambda(T&& self, V&& value) {
168   // DON'T DO THIS:
169   //  This captures by value, so we always get a copy of self.
170   //([self = std::forward<T>(self)](auto&& value) mutable {
171   ([&self](auto&& value) mutable {
172     // &self is not ideal since prvalues are captured-by-reference instead of by-value.
173     // this appears to be good enough for our use-case.
174     self.x = value;
175   })(std::forward<V>(value));
176 }
177 
TEST(Introspection,SetSimpleValue)178 TEST(Introspection, SetSimpleValue) {
179   Simple x{};
180   Simple x_expected{123};
181 
182   call_set_simple_value_with_lambda(x, 123);
183   EXPECT_EQ(x_expected.x, x.x);
184 }
185 
186 
187 }  // namespace introspect
188 }  // namespace iorap
189