/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * 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 #include #include #include #include #include #include #include #include #include #include namespace apache::thrift::type { using apache::thrift::detail::st::private_access; TEST(FieldsTest, Get) { test_cpp2::cpp_reflection::struct3 s; using Struct = test_cpp2::cpp_reflection::struct3; EXPECT_EQ(&(*op::get, Struct>(s)), &*s.fieldA()); s.fieldA() = 10; EXPECT_EQ((op::get, Struct>(s)), 10); op::get, Struct>(s) = 20; EXPECT_EQ(*s.fieldA(), 20); test:: same_tag, Struct>(s))>; s.fieldE()->ui_ref() = 10; EXPECT_EQ((op::get, Struct>(s)->ui_ref()), 10); op::get, Struct>(s)->us_ref() = "20"; EXPECT_EQ(s.fieldE()->us_ref(), "20"); test:: same_tag, Struct>(s))>; } TEST(FieldsTest, field_id_by_ordinal) { EXPECT_EQ(op::size_v, 19); } TEST(UnionFieldsTest, Get) { test_cpp2::cpp_reflection::union1 u; using Union = test_cpp2::cpp_reflection::union1; EXPECT_THROW((*op::get, Union>(u)), bad_field_access); u.ui_ref() = 10; EXPECT_EQ((op::get, Union>(u)), 10); EXPECT_THROW((*op::get, Union>(u)), bad_field_access); test:: same_tag, Union>(u))>; EXPECT_EQ(&(*op::get, Union>(u)), &*u.ui_ref()); op::get, Union>(u) = 20; EXPECT_EQ(u.ui_ref(), 20); EXPECT_EQ((op::get, Union>(u)), 20); u.us_ref() = "foo"; EXPECT_EQ((*op::get, Union>(u)), "foo"); test:: same_tag, Union>(u))>; EXPECT_THROW((*op::get, Union>(u)), bad_field_access); } template < class Struct, class Ordinal, class Id, class Ident, bool is_type_tag_unique, class TypeTag, class FieldTag> void checkField(const char* identName) { if constexpr (Ordinal::value != Ordinal{}) { test::same_tag>; } test::same_tag>; test::same_tag>; test::same_tag>; test::same_tag>; if constexpr (is_type_tag_unique) { test::same_tag>; } test::same_tag, Ordinal>; test::same_tag, Id>; test::same_tag, TypeTag>; test::same_tag, Ident>; test::same_tag, FieldTag>; EXPECT_EQ((op::get_name_v), identName); test::same_tag, Ordinal>; test::same_tag, Id>; test::same_tag, TypeTag>; test::same_tag, Ident>; test::same_tag, FieldTag>; EXPECT_EQ((op::get_name_v), identName); if constexpr (is_type_tag_unique && !std::is_void_v) { test::same_tag, Ordinal>; test::same_tag, Id>; test::same_tag, TypeTag>; test::same_tag, Ident>; test::same_tag, FieldTag>; EXPECT_EQ((op::get_name_v), identName); } if constexpr (!std::is_void_v) { test::same_tag, Ordinal>; test::same_tag, Id>; test::same_tag, TypeTag>; test::same_tag, Ident>; test::same_tag, FieldTag>; EXPECT_EQ((op::get_name_v), identName); } if constexpr (!std::is_void_v) { test::same_tag, Ordinal>; test::same_tag, Id>; test::same_tag, TypeTag>; test::same_tag, Ident>; test::same_tag, FieldTag>; EXPECT_EQ((op::get_name_v), identName); } } TEST(FieldsTest, UnifiedAPIs) { using test_cpp2::cpp_reflection::struct3; using TypeTag1 = i32_t; using TypeTag2 = string_t; using TypeTag3 = enum_t<::test_cpp2::cpp_reflection::enum1>; using TypeTag4 = enum_t<::test_cpp2::cpp_reflection::enum2>; using TypeTag5 = union_t<::test_cpp2::cpp_reflection::union1>; using TypeTag6 = union_t<::test_cpp2::cpp_reflection::union2>; using TypeTag7 = struct_t<::test_cpp2::cpp_reflection::struct1>; using TypeTag8 = union_t<::test_cpp2::cpp_reflection::union2>; using TypeTag9 = list; using TypeTag10 = list; using TypeTag11 = cpp_type, list>; using TypeTag12 = list>; using TypeTag13 = set; using TypeTag14 = set; using TypeTag15 = set; using TypeTag16 = set>; using TypeTag17 = map>; using TypeTag18 = cpp_type< std::unordered_map, map>>; using TypeTag19 = map; using FieldTag1 = type::field>; using FieldTag2 = type::field>; using FieldTag3 = type::field>; using FieldTag4 = type::field>; using FieldTag5 = type::field>; using FieldTag6 = type::field>; using FieldTag7 = type::field>; using FieldTag8 = type::field>; using FieldTag9 = type::field>; using FieldTag10 = type::field>; using FieldTag11 = type::field>; using FieldTag12 = type::field>; using FieldTag13 = type::field>; using FieldTag14 = type::field>; using FieldTag15 = type::field>; using FieldTag16 = type::field>; using FieldTag17 = type::field>; using FieldTag18 = type::field>; using FieldTag19 = type::field>; // clang-format off checkField, field_id<0>, void, true, void, void>(""); checkField, field_id<2>, ident::fieldA, true, TypeTag1, FieldTag1>("fieldA"); checkField, field_id<1>, ident::fieldB, true, TypeTag2, FieldTag2>("fieldB"); checkField, field_id<3>, ident::fieldC, true, TypeTag3, FieldTag3>("fieldC"); checkField, field_id<4>, ident::fieldD, true, TypeTag4, FieldTag4>("fieldD"); checkField, field_id<5>, ident::fieldE, true, TypeTag5, FieldTag5>("fieldE"); checkField, field_id<6>, ident::fieldF, false, TypeTag6, FieldTag6>("fieldF"); checkField, field_id<7>, ident::fieldG, true, TypeTag7, FieldTag7>("fieldG"); checkField, field_id<8>, ident::fieldH, false, TypeTag8, FieldTag8>("fieldH"); checkField, field_id<9>, ident::fieldI, true, TypeTag9, FieldTag9>("fieldI"); checkField, field_id<10>, ident::fieldJ, true, TypeTag10, FieldTag10>("fieldJ"); checkField, field_id<11>, ident::fieldK, true, TypeTag11, FieldTag11>("fieldK"); checkField, field_id<12>, ident::fieldL, true, TypeTag12, FieldTag12>("fieldL"); checkField, field_id<13>, ident::fieldM, true, TypeTag13, FieldTag13>("fieldM"); checkField, field_id<14>, ident::fieldN, false, TypeTag14, FieldTag14>("fieldN"); checkField, field_id<15>, ident::fieldO, false, TypeTag15, FieldTag15>("fieldO"); checkField, field_id<16>, ident::fieldP, true, TypeTag16, FieldTag16>("fieldP"); checkField, field_id<17>, ident::fieldQ, true, TypeTag17, FieldTag17>("fieldQ"); checkField, field_id<18>, ident::fieldR, true, TypeTag18, FieldTag18>("fieldR"); checkField, field_id<20>, ident::fieldS, true, TypeTag19, FieldTag19>("fieldS"); // clang-format on } struct IncompleteType; TEST(FieldsTest, IsReflectionMetadata) { using namespace apache::thrift::type::detail; static_assert(is_type_tag_v); static_assert(is_type_tag_v>); static_assert(is_type_tag_v, FieldContext>>); static_assert(!is_type_tag_v); static_assert(!is_type_tag_v); static_assert(!is_type_tag_v>); static_assert(!is_type_tag_v); static_assert(!is_type_tag_v); static_assert(is_field_id_v>); static_assert(is_field_id_v>); static_assert(!is_field_id_v>); static_assert(!is_field_id_v); static_assert(!is_field_id_v); static_assert(is_ordinal_v>); static_assert(is_ordinal_v>); static_assert(!is_ordinal_v>); static_assert(!is_ordinal_v); static_assert(!is_ordinal_v); static_assert(is_ident_v); static_assert(!is_ident_v); static_assert(!is_ident_v); static_assert(!is_ident_v); static_assert(is_id_v); static_assert(is_id_v>); static_assert(is_id_v, FieldContext>>); static_assert(is_id_v>); static_assert(is_id_v>); static_assert(is_id_v>); static_assert(is_id_v>); static_assert(is_id_v); static_assert(!is_id_v); static_assert(!is_id_v); static_assert(!is_id_v>); static_assert(!is_id_v); static_assert(!is_id_v); static_assert(!is_id_v); using Struct = test_cpp2::cpp_reflection::struct3; // TODO(ytj): We need to figure out a way to test compile error // op::get_field_id{}; // compile error // op::get_ordinal{}; // compile error // op::get_type_tag{}; // compile error // op::get_ident{}; // compile error // op::get_native_type{}; // compile error } TEST(FieldsTest, NotFoundFieldInfo) { using Struct = test_cpp2::cpp_reflection::struct3; test::same_tag>, field_ordinal<0>>; test::same_tag>, field_id<0>>; test::same_tag>, void>; test::same_tag>, void>; test::same_tag>, void>; test::same_tag>, field_ordinal<0>>; test::same_tag>, field_id<0>>; test::same_tag>, void>; test::same_tag>, void>; test::same_tag>, void>; test::same_tag, field_ordinal<0>>; test::same_tag, field_id<0>>; test::same_tag, void>; test::same_tag, void>; test::same_tag, void>; test::same_tag, field_ordinal<0>>; test::same_tag, field_id<0>>; test::same_tag, void>; test::same_tag, void>; test::same_tag, void>; } template bool isIdentTag(folly::tag_t) { return is_ident_v; } TEST(FieldsTest, HelperAPIs) { using Struct = test_cpp2::cpp_reflection::struct3; test::same_tag>, std::int32_t>; test::same_tag, std::int32_t>; test::same_tag< op::get_native_type>, std::deque>; EXPECT_EQ((op::get_field_id_v>), FieldId{2}); EXPECT_EQ((op::get_ordinal_v>), FieldOrdinal{1}); int count = 0; op::for_each_field_id([&](auto id) { EXPECT_TRUE(is_field_id_v); count++; }); EXPECT_EQ(count, op::size_v); count = 0; op::for_each_ident([&](auto id) { EXPECT_TRUE(isIdentTag(id)); count++; }); EXPECT_EQ(count, op::size_v); count = 0; op::for_each_ordinal([&](auto id) { EXPECT_TRUE(is_ordinal_v); count++; }); EXPECT_EQ(count, op::size_v); } TEST(FieldsTest, GetFieldNameCppName) { EXPECT_EQ( (op::get_name_v< test_cpp2::cpp_reflection::struct_with_renamed_field, field_ordinal<1>>), "fancy.idl.name"); } TEST(FieldsTest, GetClassName) { EXPECT_EQ(op::get_class_name_v, "MyStruct"); EXPECT_EQ(op::get_class_name_v, "MyUnion"); EXPECT_EQ(op::get_class_name_v, "MyException"); EXPECT_EQ(op::get_class_name_v, "MyStruct2"); EXPECT_EQ(op::get_class_name_v, "MyUnion2"); EXPECT_EQ(op::get_class_name_v, "MyException2"); } TEST(FieldsTest, InvokeByFieldId) { using namespace test_cpp2::cpp_reflection; constexpr auto toValue = [](auto id) { return id.value; }; constexpr auto notFound = [] { return FieldId{0}; }; static_assert( op::invoke_by_field_id(FieldId{18}, toValue, notFound) == FieldId{18}); static_assert( op::invoke_by_field_id(FieldId{19}, toValue, notFound) == FieldId{0}); static_assert( op::invoke_by_field_id(FieldId{20}, toValue, notFound) == FieldId{20}); static_assert( op::invoke_by_field_id(FieldId{21}, toValue, notFound) == FieldId{0}); static_assert( op::invoke_by_field_id(FieldId{0}, toValue, notFound) == FieldId{0}); for (std::int16_t id = 0; id <= 21; id++) { if (std::unordered_set{0, 19, 21}.contains(id)) { EXPECT_EQ( op::invoke_by_field_id(FieldId{id}, toValue, notFound), FieldId{0}); } else { EXPECT_EQ( op::invoke_by_field_id(FieldId{id}, toValue, notFound), FieldId{id}); } } for (std::int16_t id = 0; id <= 10; id++) { if (std::unordered_set{1, 2, 3, 6}.contains(id)) { EXPECT_EQ( op::invoke_by_field_id(FieldId{id}, toValue, notFound), FieldId{id}); } else { EXPECT_EQ( op::invoke_by_field_id(FieldId{id}, toValue, notFound), FieldId{0}); } } } TEST(FieldsTest, GetFieldRefType) { using test_cpp2::cpp_reflection::enum1; using test_cpp2::cpp_reflection::struct4; using test_cpp2::cpp_reflection::structA; static_assert(test::same_type< op::get_field_ref, required_field_ref>); static_assert(test::same_type< op::get_field_ref, optional_field_ref>); static_assert(test::same_type< op::get_field_ref, field_ref>); static_assert(test::same_type< op::get_field_ref, std::unique_ptr>); } } // namespace apache::thrift::type