blob: 551d0b7c2e511793adfb66bbbc7bd265054642e9 [file] [log] [blame]
/*
* 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