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