// // object_test.cxx // reflection-test // // Created by Sam Jaffe on 7/4/22. // Copyright © 2022 Sam Jaffe. All rights reserved. // #include "reflection/object.h" #include "xcode_gtest_helper.h" using reflection::Object; using reflection::Reflection; using testing::Eq; using testing::NotNull; namespace { struct Example { int a; int get_c() const { return a + 4; } void set_c(int c) { a = c - 4; } }; struct Compound { Example ex; }; } namespace reflection { INSTANTIATE_REFLECTION(Example); INSTANTIATE_REFLECTION(Compound); } REFLECTION(Example) .bind("a", &Example::a) .bind("c", &Example::get_c, &Example::set_c); REFLECTION(Compound).bind("ex", &Compound::ex); TEST(ObjectTest, CanFetchData) { Example ex{.a = 1}; Object a = Object(ex).get("a"); EXPECT_NO_THROW((void)int(a)) << a.type().name(); EXPECT_THAT(int(a), 1); } TEST(ObjectTest, CanSetDataOnNonConst) { Example ex{.a = 1}; { Object a = Object(ex).get("a"); EXPECT_NO_THROW((void)static_cast(a)) << a.type().name(); static_cast(a) = 2; } EXPECT_THAT(ex.a, 2); } TEST(ObjectTest, ThrowsWhenTryingToEditConst) { Example const ex{.a = 1}; Object a = Object(ex).get("a"); EXPECT_THROW((void)static_cast(a), std::bad_cast); } TEST(ObjectTest, ThrowsWhenTryingToEditMoved) { Object a = Object(Example{.a = 1}).get("a"); EXPECT_THROW((void)static_cast(a), std::bad_cast); } TEST(ObjectTest, CanCoerceNumericTypes) { Example const ex{.a = 1}; Object a = Object(ex).get("a"); EXPECT_THAT(double(a), 1.0); } TEST(ObjectTest, ThrowsWhenPerformingIllegalTypeCoersion) { Example const ex{.a = 1}; Object a = Object(ex).get("a"); EXPECT_THROW((void)std::string(a), std::bad_cast); } TEST(ObjectTest, CanFetchWithGetter) { Example ex{.a = 1}; Object c = Object(ex).get("c"); EXPECT_NO_THROW((void)int(c)) << c.type().name(); EXPECT_THAT(int(c), Eq(5)); static_cast(c) = 4; } TEST(ObjectTest, CanModifyWithSetter) { Example ex{.a = 1}; { Object c = Object(ex).get("c"); EXPECT_NO_THROW((void)static_cast(c)) << c.type().name(); static_cast(c) = 4; // Notice that the setter is scoped on the Object expiring EXPECT_THAT(ex.a, Eq(1)); } EXPECT_THAT(ex.a, Eq(0)); } TEST(ObjectTest, ThrowsWhenUsingSetterOnConst) { Example const ex{.a = 1}; Object c = Object(ex).get("c"); EXPECT_THROW((void)static_cast(c), std::bad_cast); } TEST(ObjectTest, CanDive) { Compound cm{.ex = {.a = 2}}; Object a = Object(cm).get(std::vector{"ex", "a"}); EXPECT_NO_THROW((void)int(a)); EXPECT_THAT(int(a), Eq(2)); } TEST(ObjectTest, AttachesNameInfo) { Compound cm{.ex = {.a = 2}}; Object a = Object(cm).get(std::vector{"ex", "a"}); EXPECT_THAT(a.path(), Eq("this.ex.a")); EXPECT_THAT(a.name(), Eq("a")); } TEST(ObjectTest, CanReflectVariableName) { Compound cm{.ex = {.a = 2}}; Object a = reflect(cm).get(std::vector{"ex", "a"}); EXPECT_THAT(a.path(), Eq("cm.ex.a")); EXPECT_THAT(a.name(), Eq("a")); }