/* * 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::test { TYPED_TEST(LazyDeserialization, Copyable) { auto base = this->genLazyStruct(); auto bar = base; // copy constructor typename TypeParam::LazyStruct baz; baz = base; // copy assignment EXPECT_EQ(bar.field1_ref(), baz.field1_ref()); EXPECT_EQ(bar.field2_ref(), baz.field2_ref()); EXPECT_EQ(bar.field3_ref(), baz.field3_ref()); EXPECT_EQ(bar.field4_ref(), baz.field4_ref()); auto foo = this->genStruct(); EXPECT_EQ(foo.field1_ref(), bar.field1_ref()); EXPECT_EQ(foo.field2_ref(), bar.field2_ref()); EXPECT_EQ(foo.field3_ref(), bar.field3_ref()); EXPECT_EQ(foo.field4_ref(), bar.field4_ref()); } TYPED_TEST(LazyDeserialization, Moveable) { auto temp = this->genLazyStruct(); auto bar = std::move(temp); // move constructor typename TypeParam::LazyStruct baz; baz = this->genLazyStruct(); // move assignment EXPECT_EQ(bar.field1_ref(), baz.field1_ref()); EXPECT_EQ(bar.field2_ref(), baz.field2_ref()); EXPECT_EQ(bar.field3_ref(), baz.field3_ref()); EXPECT_EQ(bar.field4_ref(), baz.field4_ref()); auto foo = this->genStruct(); EXPECT_EQ(foo.field1_ref(), bar.field1_ref()); EXPECT_EQ(foo.field2_ref(), bar.field2_ref()); EXPECT_EQ(foo.field3_ref(), bar.field3_ref()); EXPECT_EQ(foo.field4_ref(), bar.field4_ref()); } TYPED_TEST(LazyDeserialization, Swap) { auto temp = this->genLazyStruct(); typename TypeParam::LazyStruct bar, baz; swap(bar, temp); EXPECT_EQ(temp.field1_ref(), baz.field1_ref()); EXPECT_EQ(temp.field2_ref(), baz.field2_ref()); EXPECT_EQ(temp.field3_ref(), baz.field3_ref()); EXPECT_EQ(temp.field4_ref(), baz.field4_ref()); baz = this->genLazyStruct(); EXPECT_EQ(bar.field1_ref(), baz.field1_ref()); EXPECT_EQ(bar.field2_ref(), baz.field2_ref()); EXPECT_EQ(bar.field3_ref(), baz.field3_ref()); EXPECT_EQ(bar.field4_ref(), baz.field4_ref()); } template class Serialization : public testing::Test {}; using Serializers = ::testing::Types; TYPED_TEST_SUITE(Serialization, Serializers); TYPED_TEST(LazyDeserialization, FooToLazyFoo) { using LazyStruct = typename TypeParam::LazyStruct; auto foo = this->genStruct(); auto s = this->serialize(foo); auto lazyFoo = this->template deserialize(s); EXPECT_EQ(foo.field1_ref(), lazyFoo.field1_ref()); EXPECT_EQ(foo.field2_ref(), lazyFoo.field2_ref()); EXPECT_EQ(foo.field3_ref(), lazyFoo.field3_ref()); EXPECT_EQ(foo.field4_ref(), lazyFoo.field4_ref()); } TYPED_TEST(LazyDeserialization, LazyFooToFoo) { using Struct = typename TypeParam::Struct; auto lazyFoo = this->genLazyStruct(); auto s = this->serialize(lazyFoo); auto foo = this->template deserialize(s); EXPECT_EQ(foo.field1_ref(), lazyFoo.field1_ref()); EXPECT_EQ(foo.field2_ref(), lazyFoo.field2_ref()); EXPECT_EQ(foo.field3_ref(), lazyFoo.field3_ref()); EXPECT_EQ(foo.field4_ref(), lazyFoo.field4_ref()); } FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field1, LazyFoo, field1); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field2, LazyFoo, field2); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field3, LazyFoo, field3); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field4, LazyFoo, field4); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field1, OptionalLazyFoo, field1); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field2, OptionalLazyFoo, field2); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field3, OptionalLazyFoo, field3); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field4, OptionalLazyFoo, field4); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field1, TerseLazyFoo, field1); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field2, TerseLazyFoo, field2); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field3, TerseLazyFoo, field3); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field4, TerseLazyFoo, field4); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field1, TerseOptionalLazyFoo, field1); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field2, TerseOptionalLazyFoo, field2); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field3, TerseOptionalLazyFoo, field3); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field4, TerseOptionalLazyFoo, field4); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field1, FooNoChecksum, field1); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field2, FooNoChecksum, field2); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field3, FooNoChecksum, field3); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field4, FooNoChecksum, field4); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field1, LazyFooNoChecksum, field1); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field2, LazyFooNoChecksum, field2); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field3, LazyFooNoChecksum, field3); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field4, LazyFooNoChecksum, field4); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field1, LazyCppRef, field1); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field2, LazyCppRef, field2); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field3, LazyCppRef, field3); FBTHRIFT_DEFINE_MEMBER_ACCESSOR(get_field4, LazyCppRef, field4); TYPED_TEST(LazyDeserialization, CheckDataMember) { using LazyStruct = typename TypeParam::LazyStruct; auto foo = this->genLazyStruct(); auto s = this->serialize(foo); auto lazyFoo = this->template deserialize(s); EXPECT_EQ(get_field1(lazyFoo), foo.field1_ref()); EXPECT_EQ(get_field2(lazyFoo), foo.field2_ref()); EXPECT_TRUE(get_field3(lazyFoo).empty()); EXPECT_TRUE(get_field4(lazyFoo).empty()); EXPECT_EQ(lazyFoo.field1_ref(), foo.field1_ref()); EXPECT_EQ(lazyFoo.field2_ref(), foo.field2_ref()); EXPECT_EQ(lazyFoo.field3_ref(), foo.field3_ref()); EXPECT_EQ(lazyFoo.field4_ref(), foo.field4_ref()); EXPECT_EQ(get_field1(lazyFoo), foo.field1_ref()); EXPECT_EQ(get_field2(lazyFoo), foo.field2_ref()); EXPECT_EQ(get_field3(lazyFoo), foo.field3_ref()); EXPECT_EQ(get_field4(lazyFoo), foo.field4_ref()); } // Lazy deserialization does not support virtual protocol, but deserialization // of a struct with lazy fields should still be possible. TYPED_TEST(LazyDeserialization, VirtualProtocol) { using LazyStruct = typename TypeParam::LazyStruct; using Serializer = typename TypeParam::Serializer; using VirtualProtocolReader = VirtualReader; auto foo = this->genLazyStruct(); auto s = this->serialize(foo); auto buf = folly::IOBuf::wrapBuffer(folly::StringPiece(s)); // Test both concrete and abstract readers. const auto test = [&](auto&& reader) { LazyStruct lazyFoo; reader.setInput(buf.get()); lazyFoo.read(&reader); EXPECT_EQ(foo, lazyFoo); }; test(VirtualProtocolReader{}); test(static_cast(VirtualProtocolReader{})); } TYPED_TEST(Serialization, CppRef) { { LazyCppRef foo; foo.field1_ref() = std::make_unique>(10, 10); foo.field2_ref() = std::make_shared>(20, 20); foo.field3_ref() = std::make_shared>(30, 30); foo.field4_ref() = std::make_unique>(40, 40); auto s = TypeParam::template serialize(foo); auto bar = TypeParam::template deserialize(s); EXPECT_EQ(*bar.field1_ref(), std::vector(10, 10)); EXPECT_EQ(*bar.field2_ref(), std::vector(20, 20)); EXPECT_EQ(*bar.field3_ref(), std::vector(30, 30)); EXPECT_EQ(*bar.field4_ref(), std::vector(40, 40)); } { LazyCppRef foo; auto s = TypeParam::template serialize(foo); auto bar = TypeParam::template deserialize(s); EXPECT_FALSE(bar.field1_ref()); EXPECT_FALSE(bar.field2_ref()); EXPECT_FALSE(bar.field3_ref()); EXPECT_TRUE(bar.field4_ref()); // non-optional field always has value } } TYPED_TEST(Serialization, LazyDeserializeCppRef) { LazyCppRef foo; foo.field1_ref() = std::make_unique>(10, 10); foo.field2_ref() = std::make_shared>(20, 20); foo.field3_ref() = std::make_shared>(30, 30); foo.field4_ref() = std::make_unique>(40, 40); auto bar = TypeParam::template deserialize( TypeParam::template serialize(foo)); auto baz = TypeParam::template deserialize( TypeParam::template serialize(bar)); // all fields are not deserialized EXPECT_FALSE(get_field1(bar)); EXPECT_FALSE(get_field2(bar)); EXPECT_FALSE(get_field3(bar)); EXPECT_TRUE(get_field4(bar)->empty()); // non-optional field always has value EXPECT_FALSE(get_field1(baz)); EXPECT_FALSE(get_field2(baz)); EXPECT_FALSE(get_field3(baz)); EXPECT_TRUE(get_field4(baz)->empty()); // access all fields EXPECT_EQ(*bar.field1_ref(), std::vector(10, 10)); EXPECT_EQ(*bar.field2_ref(), std::vector(20, 20)); EXPECT_EQ(*bar.field3_ref(), std::vector(30, 30)); EXPECT_EQ(*bar.field4_ref(), std::vector(40, 40)); EXPECT_EQ(*baz.field1_ref(), std::vector(10, 10)); EXPECT_EQ(*baz.field2_ref(), std::vector(20, 20)); EXPECT_EQ(*baz.field3_ref(), std::vector(30, 30)); EXPECT_EQ(*baz.field4_ref(), std::vector(40, 40)); // now all fields are deserialized EXPECT_TRUE(get_field1(bar)); EXPECT_TRUE(get_field2(bar)); EXPECT_TRUE(get_field3(bar)); EXPECT_EQ(*get_field4(bar), std::vector(40, 40)); EXPECT_TRUE(get_field1(baz)); EXPECT_TRUE(get_field2(baz)); EXPECT_TRUE(get_field3(baz)); EXPECT_EQ(*get_field4(baz), std::vector(40, 40)); } TYPED_TEST(LazyDeserialization, Comparison) { using LazyStruct = typename TypeParam::LazyStruct; { auto foo1 = this->genLazyStruct(); auto s = this->serialize(foo1); auto foo2 = this->template deserialize(s); EXPECT_FALSE(get_field1(foo1).empty()); EXPECT_FALSE(get_field2(foo1).empty()); EXPECT_FALSE(get_field3(foo1).empty()); EXPECT_FALSE(get_field4(foo1).empty()); // field3 and field4 are lazy field, thus they are empty EXPECT_FALSE(get_field1(foo2).empty()); EXPECT_FALSE(get_field2(foo2).empty()); EXPECT_TRUE(get_field3(foo2).empty()); EXPECT_TRUE(get_field4(foo2).empty()); EXPECT_EQ(foo1, foo2); foo1.field4_ref()->clear(); EXPECT_NE(foo1, foo2); foo2.field4_ref()->clear(); EXPECT_EQ(foo1, foo2); } { auto foo1 = this->genLazyStruct(); auto s = this->serialize(foo1); auto foo2 = this->template deserialize(s); foo1.field4_ref()->clear(); EXPECT_LT(foo1, foo2); } { auto foo1 = this->genLazyStruct(); foo1.field1_ref()->clear(); foo1.field2_ref()->clear(); // Only lazy fields left set. auto s = this->serialize(foo1); auto foo2 = this->template deserialize(s); foo2.field4_ref()->clear(); EXPECT_LT(foo2, foo1); foo2 = this->template deserialize(s); foo2 = {}; EXPECT_LT(foo2, foo1); } } // The intention of this unit-test is testing if serialized data doesn't have // index, we want to ensure it can be still deserialized to struct with lazy // field with behavior that identical to non-lazy field. i.e. deserialization // should overwrite fields and clear IOBuf. This can happen during schema // evolution when user turned off lazy deserialization only on writer side. TYPED_TEST(LazyDeserialization, DeserializeLazyStructWithoutIndex) { using Struct = typename TypeParam::Struct; using LazyStruct = typename TypeParam::LazyStruct; Struct foo; foo.field1_ref().emplace(); foo.field2_ref().emplace(); foo.field3_ref().emplace(); foo.field4_ref().emplace(); // We need first deserialization since even though `this->genLazyStruct()` // returns lazy struct, all lazy fields are already deserialized. LazyStruct lazyFoo; this->deserialize(this->serialize(this->genLazyStruct()), lazyFoo); this->deserialize(this->serialize(foo), lazyFoo); if (std::is_same_v) { // If struct enabled terse writes, we won't serialize fields that has // default value, thus we won't change lazyFoo after deserialization EXPECT_EQ(lazyFoo, this->genLazyStruct()); } else { EXPECT_TRUE(lazyFoo.field1_ref()->empty()); EXPECT_TRUE(lazyFoo.field2_ref()->empty()); EXPECT_TRUE(lazyFoo.field3_ref()->empty()); EXPECT_TRUE(lazyFoo.field4_ref()->empty()); } } TYPED_TEST( LazyDeserialization, DeserializeLazyStructWithoutIndexOrTerseWrites) { using LazyStruct = typename TypeParam::LazyStruct; Foo foo; LazyStruct lazyFoo; this->deserialize(this->serialize(this->genLazyStruct()), lazyFoo); this->deserialize(this->serialize(foo), lazyFoo); EXPECT_TRUE(lazyFoo.field1_ref()->empty()); EXPECT_TRUE(lazyFoo.field2_ref()->empty()); EXPECT_TRUE(lazyFoo.field3_ref()->empty()); EXPECT_TRUE(lazyFoo.field4_ref()->empty()); } TYPED_TEST(LazyDeserialization, OptionalField) { using Struct = typename TypeParam::Struct; using LazyStruct = typename TypeParam::LazyStruct; auto s = this->serialize(this->genStruct()); auto foo = this->template deserialize(s); auto lazyFoo = this->template deserialize(s); EXPECT_EQ(foo.field1_ref(), lazyFoo.field1_ref()); EXPECT_EQ(foo.field2_ref(), lazyFoo.field2_ref()); EXPECT_EQ(foo.field3_ref(), lazyFoo.field3_ref()); EXPECT_EQ(foo.field4_ref(), lazyFoo.field4_ref()); } TYPED_TEST(LazyDeserialization, ReserializeLazyField) { using LazyStruct = typename TypeParam::LazyStruct; auto foo1 = this->template deserialize( this->serialize(this->genLazyStruct())); auto foo2 = this->template deserialize(this->serialize(foo1)); EXPECT_EQ(foo1.field1_ref(), foo2.field1_ref()); EXPECT_EQ(foo1.field2_ref(), foo2.field2_ref()); EXPECT_EQ(foo1.field3_ref(), foo2.field3_ref()); EXPECT_EQ(foo1.field4_ref(), foo2.field4_ref()); } TYPED_TEST(LazyDeserialization, SupportedToUnsupportedProtocol) { using LazyStruct = typename TypeParam::LazyStruct; auto foo1 = this->template deserialize( this->serialize(this->genLazyStruct())); EXPECT_FALSE(get_field1(foo1).empty()); EXPECT_FALSE(get_field2(foo1).empty()); EXPECT_TRUE(get_field3(foo1).empty()); EXPECT_TRUE(get_field4(foo1).empty()); // Simple JSON doesn't support lazy deserialization // All fields will be deserialized SimpleJSONSerializer::deserialize( SimpleJSONSerializer::serialize(Empty{}), foo1); EXPECT_FALSE(get_field1(foo1).empty()); EXPECT_FALSE(get_field2(foo1).empty()); EXPECT_FALSE(get_field3(foo1).empty()); EXPECT_FALSE(get_field4(foo1).empty()); auto foo2 = this->genLazyStruct(); EXPECT_EQ(foo1.field1_ref(), foo2.field1_ref()); EXPECT_EQ(foo1.field2_ref(), foo2.field2_ref()); EXPECT_EQ(foo1.field3_ref(), foo2.field3_ref()); EXPECT_EQ(foo1.field4_ref(), foo2.field4_ref()); } TYPED_TEST(LazyDeserialization, ReserializeSameStruct) { using LazyStruct = typename TypeParam::LazyStruct; auto foo1 = this->template deserialize( this->serialize(this->genLazyStruct())); EXPECT_FALSE(get_field1(foo1).empty()); EXPECT_FALSE(get_field2(foo1).empty()); EXPECT_TRUE(get_field3(foo1).empty()); EXPECT_TRUE(get_field4(foo1).empty()); // Lazy fields remain undeserialized this->deserialize(this->serialize(OptionalFoo{}), foo1); EXPECT_FALSE(get_field1(foo1).empty()); EXPECT_FALSE(get_field2(foo1).empty()); EXPECT_TRUE(get_field3(foo1).empty()); EXPECT_TRUE(get_field4(foo1).empty()); auto foo2 = this->genLazyStruct(); EXPECT_EQ(foo1.field1_ref(), foo2.field1_ref()); EXPECT_EQ(foo1.field2_ref(), foo2.field2_ref()); EXPECT_EQ(foo1.field3_ref(), foo2.field3_ref()); EXPECT_EQ(foo1.field4_ref(), foo2.field4_ref()); } TYPED_TEST(LazyDeserialization, DeserializationWithDifferentProtocol) { using Serializer = typename TypeParam::Serializer; using LazyStruct = typename TypeParam::LazyStruct; using Serializer2 = std::conditional_t< std::is_same_v, BinarySerializer, CompactSerializer>; auto foo = this->template deserialize( this->serialize(this->genLazyStruct())); EXPECT_FALSE(get_field1(foo).empty()); EXPECT_FALSE(get_field2(foo).empty()); EXPECT_TRUE(get_field3(foo).empty()); EXPECT_TRUE(get_field4(foo).empty()); // Deserialize with same protocol, all fields are untouched Serializer::deserialize( Serializer::template serialize(Empty{}), foo); EXPECT_FALSE(get_field1(foo).empty()); EXPECT_FALSE(get_field2(foo).empty()); EXPECT_TRUE(get_field3(foo).empty()); EXPECT_TRUE(get_field4(foo).empty()); // Deserialize with different protocol, all fields are deserialized Serializer2::deserialize( Serializer2::template serialize(Empty{}), foo); EXPECT_FALSE(get_field1(foo).empty()); EXPECT_FALSE(get_field2(foo).empty()); EXPECT_FALSE(get_field3(foo).empty()); EXPECT_FALSE(get_field4(foo).empty()); EXPECT_EQ(foo, this->genLazyStruct()); } TEST(Serialization, SerializeWithDifferentProtocolSimple) { auto foo = CompactSerializer::deserialize( CompactSerializer::serialize(gen())); auto foo1 = BinarySerializer::deserialize( BinarySerializer::serialize(foo)); EXPECT_EQ(foo1, gen()); } TYPED_TEST(LazyDeserialization, SerializationWithSameProtocol) { using Serializer = typename TypeParam::Serializer; using LazyStruct = typename TypeParam::LazyStruct; auto foo = Serializer::template deserialize( Serializer::template serialize(gen())); auto foo1 = Serializer::template deserialize( Serializer::template serialize(foo)); // Serialize with same protocol, lazy fields won't be deserialized EXPECT_FALSE(get_field1(foo).empty()); EXPECT_FALSE(get_field2(foo).empty()); EXPECT_TRUE(get_field3(foo).empty()); EXPECT_TRUE(get_field4(foo).empty()); EXPECT_EQ(foo1, gen()); } TYPED_TEST(LazyDeserialization, SerializationWithDifferentProtocol) { using Serializer = typename TypeParam::Serializer; using LazyStruct = typename TypeParam::LazyStruct; using Serializer2 = std::conditional_t< std::is_same_v, BinarySerializer, CompactSerializer>; auto foo = Serializer::template deserialize( Serializer::template serialize(gen())); auto foo1 = Serializer2::template deserialize( Serializer2::template serialize(foo)); EXPECT_FALSE(get_field1(foo).empty()); EXPECT_FALSE(get_field2(foo).empty()); EXPECT_FALSE(get_field3(foo).empty()); EXPECT_FALSE(get_field4(foo).empty()); EXPECT_EQ(foo1, gen()); } constexpr int kThreadCount = 100; TYPED_TEST(LazyDeserialization, MultithreadAccess) { using LazyStruct = typename TypeParam::LazyStruct; auto bar = this->genLazyStruct(); auto readonlyAccesses = std::make_tuple( [&bar](auto& foo) { EXPECT_EQ(foo.field1_ref(), bar.field1_ref()); EXPECT_EQ(foo.field2_ref(), bar.field2_ref()); EXPECT_EQ(foo.field3_ref(), bar.field3_ref()); EXPECT_EQ(foo.field4_ref(), bar.field4_ref()); }, [&bar](auto& foo) { EXPECT_EQ(foo, bar); }, [](auto& foo) { LazyStruct baz{foo}; }, [](auto& foo) { LazyStruct baz; baz = foo; }, [](auto& foo) { LazyStruct baz{std::move(std::as_const(foo))}; }, [](auto& foo) { LazyStruct baz; baz = std::move(std::as_const(foo)); }); folly::for_each(readonlyAccesses, [&](auto func) { for (int n = 0; n < 2; n++) { const bool castToConst = (n == 0); auto foo = this->template deserialize( this->serialize(this->genLazyStruct())); std::array threads; for (auto& i : threads) { i = std::thread([&] { if (castToConst) { func(std::as_const(foo)); } else { func(foo); } }); } for (auto& i : threads) { i.join(); } EXPECT_EQ(foo, bar); } }); } TYPED_TEST(LazyDeserialization, TestGFlags) { using LazyStruct = typename TypeParam::LazyStruct; gflags::FlagSaver saver; FLAGS_thrift_enable_lazy_deserialization = false; auto foo = this->genLazyStruct(); auto s = this->serialize(foo); auto lazyFoo = this->template deserialize(s); EXPECT_EQ(get_field1(lazyFoo), foo.field1_ref()); EXPECT_EQ(get_field2(lazyFoo), foo.field2_ref()); EXPECT_FALSE(get_field3(lazyFoo).empty()); EXPECT_FALSE(get_field4(lazyFoo).empty()); } TEST(LazyDeserialization, BoxedLazyFoo) { auto foo = gen(); auto s = CompactSerializer::serialize(foo); OptionalBoxedLazyFoo lazyFoo; CompactSerializer::deserialize(s, lazyFoo); EXPECT_EQ(foo.field1_ref().has_value(), lazyFoo.field1_ref().has_value()); EXPECT_EQ(foo.field2_ref().has_value(), lazyFoo.field2_ref().has_value()); EXPECT_EQ(foo.field3_ref().has_value(), lazyFoo.field3_ref().has_value()); EXPECT_EQ(foo.field4_ref().has_value(), lazyFoo.field4_ref().has_value()); if (foo.field1_ref()) { EXPECT_EQ(*foo.field1_ref(), lazyFoo.field1_ref()); } if (foo.field2_ref()) { EXPECT_EQ(*foo.field2_ref(), lazyFoo.field2_ref()); } if (foo.field3_ref()) { EXPECT_EQ(*foo.field3_ref(), lazyFoo.field3_ref()); } if (foo.field4_ref()) { EXPECT_EQ(*foo.field4_ref(), lazyFoo.field4_ref()); } } TEST(LazyDeserialization, BoxedLazyFooSerializedSize) { auto foo = gen(); auto s = CompactSerializer::serialize(foo); OptionalBoxedLazyFoo lazyFoo; CompactSerializer::deserialize(s, lazyFoo); CompactProtocolWriter w; // The estimation should be larger than actual size EXPECT_LE(s.size(), lazyFoo.serializedSize(&w)); // With zero-copy, we don't need to allocate memory for undeserialized fields. // Thus the serialized size would be smaller. EXPECT_LT(lazyFoo.serializedSizeZC(&w), lazyFoo.serializedSize(&w)); // Sinze lazy field can have more accurate estimation, the size should be more // accurate than non-lazy field. EXPECT_GE(foo.serializedSize(&w), lazyFoo.serializedSize(&w)); } } // namespace apache::thrift::test