普通文本  |  189行  |  5.19 KB

/*
 * 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