/* * 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 #include namespace apache::thrift::op { namespace { // TODO(afuller): Use testset instead. using test::FieldRefStruct; using test::SmartPointerStruct; using namespace apache::thrift::type; namespace testset = apache::thrift::test::testset; // Wrapper with default constructor deleted. template struct Wrapper { T value; Wrapper() = delete; explicit Wrapper(T val) : value(std::move(val)) {} bool operator==(const Wrapper& other) const { return value == other.value; } bool operator<(const Wrapper& other) const { return value < other.value; } }; // Wrapper with context with default constructor deleted. template struct WrapperWithContext { T value; std::string* meta = nullptr; WrapperWithContext() = delete; explicit WrapperWithContext(T val, std::string& m) : value(std::move(val)), meta(&m) {} bool operator==(const WrapperWithContext& other) const { return value == other.value; } bool operator<(const WrapperWithContext& other) const { return value < other.value; } }; struct TestTypeAdapter { template static Wrapper fromThrift(T value) { return Wrapper{value}; } template static T toThrift(Wrapper wrapper) { return wrapper.value; } }; struct TestFieldAdapter { template static WrapperWithContext fromThriftField( T value, apache::thrift::FieldContext&& ctx) { return WrapperWithContext{value, ctx.object.meta}; } template static T toThrift(WrapperWithContext wrapper) { return wrapper.value; } template static void construct( WrapperWithContext& field, FieldContext&& ctx) { field.meta = &ctx.object.meta; } }; template using adapted_tag = adapted; template using field_adapted_tag = adapted; struct TestThriftType { std::string meta; }; template void testCreateWithTag() { using tag = Tag; using ctx = FieldContext; using field_tag = type::field; using type_adapted_field_tag = type::field, ctx>; using field_adapted_field_tag = type::field, ctx>; using double_type_adapted_field_tag = type::field>, ctx>; using field_and_type_adapted_field_tag = type::field>, ctx>; TestThriftType object; auto type_created = create(); auto adapted_created = create>(); auto field_created = create(); auto type_adapted_field_created = create(object); auto field_adapted_field_created = create(object); auto double_type_adapted_field_created = create(object); auto field_and_type_adapted_field_created = create(object); test::same_type>; test::same_type>>; test::same_type>; test::same_type< decltype(type_adapted_field_created), Wrapper>>; test::same_type< decltype(field_adapted_field_created), WrapperWithContext, TestThriftType, 0>>; test::same_type< decltype(double_type_adapted_field_created), Wrapper>>>; test::same_type< decltype(field_and_type_adapted_field_created), WrapperWithContext>, TestThriftType, 0>>; // Check if the context is correctly populated. EXPECT_EQ(&object.meta, field_adapted_field_created.meta); EXPECT_EQ(&object.meta, field_and_type_adapted_field_created.meta); } template void testCreateStructured() { testCreateWithTag>>(); testCreateWithTag< struct_t>>(); testCreateWithTag< struct_t>>(); testCreateWithTag< struct_t>>(); testCreateWithTag>>(); testCreateWithTag>>(); testCreateWithTag>>(); testCreateWithTag>>(); testCreateWithTag>>(); } template void testCreate() { testCreateWithTag(); testCreateStructured(); } TEST(CreateTest, Integral) { testCreate(); testCreate(); testCreate(); testCreate(); testCreate(); // testset does not include structured with Enum. testCreateWithTag>(); } TEST(CreateTest, FloatingPoint) { testCreate(); testCreate(); } TEST(CreateTest, String) { testCreate(); testCreate(); } TEST(CreateTest, Container) { testCreate>(); testCreate>(); testCreate>(); } template void testEnsure(Obj obj, Ord ord) { auto field = op::get<>(ord, obj); EXPECT_EQ(op::ensure<>(ord, obj), 0); EXPECT_EQ(field, 0); field = 2; EXPECT_EQ(op::ensure<>(ord, obj), 2); EXPECT_EQ(field, 2); } template void testEnsurePtr(Obj obj, Ord ord) { auto& field = op::get<>(ord, obj); EXPECT_EQ(op::ensure<>(ord, obj), 0); EXPECT_EQ(*field, 0); if constexpr (thrift::detail::is_unique_ptr_v< std::remove_reference_t>) { field = std::make_unique(2); } else { field = std::make_shared(2); } EXPECT_EQ(op::ensure<>(ord, obj), 2); EXPECT_EQ(*field, 2); } TEST(EnsureTest, FieldRef) { FieldRefStruct obj; op::for_each_ordinal([&](auto ord) { testEnsure(obj, ord); }); } TEST(EnsureTest, SmartPointer) { SmartPointerStruct obj; op::for_each_ordinal( [&](auto ord) { testEnsurePtr(obj, ord); }); } TEST(EnsureTest, IsAbsentAndEnsureValue) { using detail::ensureValue; using detail::isAbsent; { auto obj = op::create>>(); EXPECT_FALSE(isAbsent(obj.field_1())); ensureValue(obj.field_1()) = 1; EXPECT_FALSE(isAbsent(obj.field_1())); } { auto obj = op::create>>(); EXPECT_TRUE(isAbsent(obj.field_1())); ensureValue(obj.field_1()) = 1; EXPECT_FALSE(isAbsent(obj.field_1())); } { auto obj = op::create>>(); EXPECT_FALSE(isAbsent(obj.field_1())); ensureValue(obj.field_1()) = 1; EXPECT_FALSE(isAbsent(obj.field_1())); } { auto obj = op::create< struct_t>>(); EXPECT_FALSE(isAbsent(obj.field_1())); ensureValue(obj.field_1()) = 1; EXPECT_FALSE(isAbsent(obj.field_1())); } } } // namespace } // namespace apache::thrift::op