/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <common/introspection.h> #include <ostream> #include <sstream> #include <string> #include <gtest/gtest.h> struct TestStructXyz { int x; double y; char z; }; IORAP_INTROSPECT_ADAPT_STRUCT(TestStructXyz, x, y, z); namespace iorap { namespace introspect { TEST(Introspection, ReadValues) { TestStructXyz xyz = {1,2.1,'x'}; std::stringstream ss; for_each_member_field_value(xyz, [&](auto&& value) { ss << value << ","; }); EXPECT_EQ(std::string("1,2.1,x,"), ss.str()); } template <typename TestType, typename TargetType> constexpr bool is_same_after_decay() { return std::is_same_v<TestType, std::decay_t<TargetType>>; } #define IS_SAME_AFTER_DECAY(test, target_variable) \ is_same_after_decay<test, decltype(target_variable)>() template <typename TestType, typename TargetType> constexpr bool is_type_same_after_decay(basic_type<TargetType>) { return std::is_same_v<TestType, std::decay_t<TargetType>>; } #define IS_TYPE_SAME_AFTER_DECAY(test, target_type_variable) \ is_type_same_after_decay<test>(CONSTEXPRIFY_TYPE(target_type_variable)) #define CONSTEXPRIFY_TYPE(type_var) \ decltype(type_var){} TEST(Introspection, ForEachmemberFieldSetValues) { TestStructXyz xyz{}; TestStructXyz xyz_expected = {1,2.1,'x'}; std::stringstream ss; for_each_member_field_set_value(xyz, [&ss](auto&& value) { // This is really confusing, value is type<?>. // It should probably be merely the old value. // // The way the functions works now is more like an inplace_map. if constexpr (IS_TYPE_SAME_AFTER_DECAY(int, value)) { // value = 1; ss << "int,"; return 1; } else if constexpr (IS_TYPE_SAME_AFTER_DECAY(double, value)) { // value = 2.1; ss << "double,"; return 2.1; } else if constexpr (IS_TYPE_SAME_AFTER_DECAY(char, value)) { // value = 'x'; ss << "char,"; return 'x'; } else { STATIC_FAIL_DT(value, "Unhandled type"); } }); EXPECT_EQ(std::string("int,double,char,"), ss.str()); EXPECT_EQ(xyz_expected.x, xyz.x); EXPECT_EQ(xyz_expected.y, xyz.y); EXPECT_EQ(xyz_expected.z, xyz.z); } TEST(Introspection, MemberFieldSetValue) { TestStructXyz xyz{}; TestStructXyz xyz_expected = {1,2.1,'x'}; std::stringstream ss; auto&& [member_x, member_y, member_z] = introspect_members(type_c<TestStructXyz>); member_x.set_value(xyz, 1); member_y.set_value(xyz, 2.1); member_z.set_value(xyz, 'x'); EXPECT_EQ(xyz_expected.x, xyz.x); EXPECT_EQ(xyz_expected.y, xyz.y); EXPECT_EQ(xyz_expected.z, xyz.z); } template <typename M, typename T, typename V> constexpr void call_set_value(M member_type, T&& self, V&& value) { member_type.set_value(std::forward<T>(self), std::forward<V>(value)); } TEST(Introspection, MemberFieldSetValueIndirect) { TestStructXyz xyz{}; TestStructXyz xyz_expected = {1,2.1,'x'}; std::stringstream ss; auto&& [member_x, member_y, member_z] = introspect_members(type_c<TestStructXyz>); call_set_value(member_x, xyz, 1); call_set_value(member_y, xyz, 2.1); call_set_value(member_z, xyz, 'x'); EXPECT_EQ(xyz_expected.x, xyz.x); EXPECT_EQ(xyz_expected.y, xyz.y); EXPECT_EQ(xyz_expected.z, xyz.z); } template <typename M, typename T, typename V> constexpr void call_set_value_lambda(M member_type, T&& self, V&& value) { ([member_type, &self](auto&& value) mutable { member_type.set_value(std::forward<T>(self), std::forward<V>(value)); })(std::forward<V>(value)); } TEST(Introspection, MemberFieldSetValueIndirectLambda) { TestStructXyz xyz{}; TestStructXyz xyz_expected = {1,2.1,'x'}; std::stringstream ss; auto&& [member_x, member_y, member_z] = introspect_members(type_c<TestStructXyz>); call_set_value_lambda(member_x, xyz, 1); call_set_value_lambda(member_y, xyz, 2.1); call_set_value_lambda(member_z, xyz, 'x'); EXPECT_EQ(xyz_expected.x, xyz.x); EXPECT_EQ(xyz_expected.y, xyz.y); EXPECT_EQ(xyz_expected.z, xyz.z); } struct Simple { int x; }; template <typename T, typename V> constexpr void call_set_simple_value_with_lambda(T&& self, V&& value) { // DON'T DO THIS: // This captures by value, so we always get a copy of self. //([self = std::forward<T>(self)](auto&& value) mutable { ([&self](auto&& value) mutable { // &self is not ideal since prvalues are captured-by-reference instead of by-value. // this appears to be good enough for our use-case. self.x = value; })(std::forward<V>(value)); } TEST(Introspection, SetSimpleValue) { Simple x{}; Simple x_expected{123}; call_set_simple_value_with_lambda(x, 123); EXPECT_EQ(x_expected.x, x.x); } } // namespace introspect } // namespace iorap