2 * Copyright (c) Meta Platforms, Inc. and affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <thrift/lib/cpp2/protocol/Object.h>
21 #include <folly/io/IOBufQueue.h>
22 #include <folly/portability/GMock.h>
23 #include <folly/portability/GTest.h>
24 #include <thrift/conformance/cpp2/AnyRegistry.h>
25 #include <thrift/conformance/cpp2/AnyStructSerializer.h>
26 #include <thrift/conformance/cpp2/Protocol.h>
27 #include <thrift/conformance/cpp2/Testing.h>
28 #include <thrift/conformance/cpp2/internal/AnyStructSerializer.h>
29 #include <thrift/conformance/data/ValueGenerator.h>
30 #include <thrift/conformance/if/gen-cpp2/conformance_types_custom_protocol.h>
31 #include <thrift/conformance/if/gen-cpp2/protocol_types_custom_protocol.h>
32 #include <thrift/lib/cpp2/BadFieldAccess.h>
33 #include <thrift/lib/cpp2/protocol/BinaryProtocol.h>
34 #include <thrift/lib/cpp2/protocol/Serializer.h>
35 #include <thrift/lib/cpp2/test/gen-cpp2/ObjectTest_types.h>
36 #include <thrift/lib/cpp2/type/Tag.h>
37 #include <thrift/lib/cpp2/type/ThriftType.h>
38 #include <thrift/test/testset/Testset.h>
39 #include <thrift/test/testset/gen-cpp2/testset_types_custom_protocol.h>
41 // TODO: Remove this. Specify namespace explicitly instead.
42 using namespace ::apache::thrift::conformance
;
44 using detail::protocol_reader_t
;
45 using detail::protocol_writer_t
;
47 namespace apache::thrift::protocol
{
50 namespace testset
= apache::thrift::test::testset
;
53 decltype(auto) at(C
& container
, size_t i
) {
54 auto itr
= container
.begin();
59 TEST(ObjectTest
, Example
) {
60 using facebook::thrift::lib::test::Bar
;
63 bar
.field_3() = {"foo", "bar", "baz"};
64 bar
.field_4()->field_1() = 42;
65 bar
.field_4()->field_2() = "Everything";
67 auto serialized
= CompactSerializer::serialize
<folly::IOBufQueue
>(bar
).move();
69 // We can parse arbitrary serialized thrift blob into Protocol Object
70 Object obj
= parseObject
<CompactSerializer::ProtocolReader
>(*serialized
);
72 // We can re-serialize it back to Protocol Object
73 // Note: there is no guarantee that serialized data is byte-wise idential.
74 serialized
= serializeObject
<CompactSerializer::ProtocolWriter
>(obj
);
77 EXPECT_EQ(CompactSerializer::deserialize
<Bar
>(serialized
.get()), bar
);
79 // Test constructing the same Object manually
81 foo
[FieldId
{1}].emplace_i32() = 42;
82 foo
[FieldId
{2}].emplace_binary() = *folly::IOBuf::copyBuffer("Everything");
85 obj2
[FieldId
{10}] = asValueStruct
<type::list
<type::binary_t
>>(*bar
.field_3());
86 obj2
[FieldId
{20}].emplace_object() = foo
;
91 TEST(ObjectTest
, TypeEnforced
) {
92 // Always a bool when bool_t is used, without ambiguity.
93 // Pointers implicitly converts to bools.
94 Value value
= asValueStruct
<type::bool_t
>("");
95 ASSERT_EQ(value
.getType(), Value::Type::boolValue
);
96 EXPECT_TRUE(value
.get_boolValue());
99 TEST(ObjectTest
, Bool
) {
100 Value value
= asValueStruct
<type::bool_t
>(20);
101 ASSERT_EQ(value
.getType(), Value::Type::boolValue
);
102 EXPECT_TRUE(value
.get_boolValue());
104 value
= asValueStruct
<type::bool_t
>(0);
105 ASSERT_EQ(value
.getType(), Value::Type::boolValue
);
106 EXPECT_FALSE(value
.get_boolValue());
109 TEST(ObjectTest
, Byte
) {
110 Value value
= asValueStruct
<type::byte_t
>(7u);
111 ASSERT_EQ(value
.getType(), Value::Type::byteValue
);
112 EXPECT_EQ(value
.get_byteValue(), 7);
115 TEST(ObjectTest
, I16
) {
116 Value value
= asValueStruct
<type::i16_t
>(7u);
117 ASSERT_EQ(value
.getType(), Value::Type::i16Value
);
118 EXPECT_EQ(value
.get_i16Value(), 7);
121 TEST(ObjectTest
, I32
) {
122 Value value
= asValueStruct
<type::i32_t
>(7u);
123 ASSERT_EQ(value
.getType(), Value::Type::i32Value
);
124 EXPECT_EQ(value
.get_i32Value(), 7);
127 TEST(ObjectTest
, I64
) {
128 Value value
= asValueStruct
<type::i64_t
>(7u);
129 ASSERT_EQ(value
.getType(), Value::Type::i64Value
);
130 EXPECT_EQ(value
.get_i64Value(), 7);
133 TEST(ObjectTest
, Enum
) {
134 enum class MyEnum
{ kValue
= 7 };
135 Value value
= asValueStruct
<type::enum_c
>(MyEnum::kValue
);
136 ASSERT_EQ(value
.getType(), Value::Type::i32Value
);
137 EXPECT_EQ(value
.get_i32Value(), 7);
139 value
= asValueStruct
<type::enum_c
>(static_cast<MyEnum
>(2));
140 ASSERT_EQ(value
.getType(), Value::Type::i32Value
);
141 EXPECT_EQ(value
.get_i32Value(), 2);
143 value
= asValueStruct
<type::enum_c
>(21u);
144 ASSERT_EQ(value
.getType(), Value::Type::i32Value
);
145 EXPECT_EQ(value
.get_i32Value(), 21);
148 TEST(ObjectTest
, Float
) {
149 Value value
= asValueStruct
<type::float_t
>(1.5);
150 ASSERT_EQ(value
.getType(), Value::Type::floatValue
);
151 EXPECT_EQ(value
.get_floatValue(), 1.5f
);
154 TEST(ObjectTest
, Double
) {
155 Value value
= asValueStruct
<type::double_t
>(1.5f
);
156 ASSERT_EQ(value
.getType(), Value::Type::doubleValue
);
157 EXPECT_EQ(value
.get_doubleValue(), 1.5);
160 TEST(ObjectTest
, String
) {
161 Value value
= asValueStruct
<type::string_t
>("hi");
162 ASSERT_EQ(value
.getType(), Value::Type::stringValue
);
163 EXPECT_EQ(value
.get_stringValue(), "hi");
166 TEST(ObjectTest
, Binary
) {
167 Value value
= asValueStruct
<type::binary_t
>("hi");
168 ASSERT_EQ(value
.getType(), Value::Type::binaryValue
);
169 EXPECT_EQ(toString(value
.get_binaryValue()), "hi");
172 TEST(ObjectTest
, List
) {
173 std::vector
<int> data
= {1, 4, 2};
174 Value value
= asValueStruct
<type::list
<type::i16_t
>>(data
);
175 ASSERT_EQ(value
.getType(), Value::Type::listValue
);
176 ASSERT_EQ(value
.get_listValue().size(), data
.size());
177 for (size_t i
= 0; i
< data
.size(); ++i
) {
178 EXPECT_EQ(value
.get_listValue()[i
], asValueStruct
<type::i16_t
>(data
[i
]));
181 // Works with other containers
182 value
= asValueStruct
<type::list
<type::i16_t
>>(
183 std::set
<int>(data
.begin(), data
.end()));
184 std::sort(data
.begin(), data
.end());
185 ASSERT_EQ(value
.getType(), Value::Type::listValue
);
186 ASSERT_EQ(value
.get_listValue().size(), data
.size());
187 for (size_t i
= 0; i
< data
.size(); ++i
) {
188 EXPECT_EQ(value
.get_listValue()[i
], asValueStruct
<type::i16_t
>(data
[i
]));
192 TEST(ObjectTest
, List_Move
) {
193 // Validate the premise of the test.
194 std::string s1
= "hi";
195 std::string s2
= std::move(s1
);
199 std::vector
<std::string
> data
;
200 data
.emplace_back("hi");
201 data
.emplace_back("bye");
203 Value value
= asValueStruct
<type::list
<type::string_t
>>(data
);
204 // The strings are unchanged
205 EXPECT_THAT(data
, ::testing::ElementsAre("hi", "bye"));
206 ASSERT_EQ(value
.getType(), Value::Type::listValue
);
207 ASSERT_EQ(value
.get_listValue().size(), 2);
208 EXPECT_EQ(value
.get_listValue()[0].get_stringValue(), "hi");
209 EXPECT_EQ(value
.get_listValue()[1].get_stringValue(), "bye");
211 value
= asValueStruct
<type::list
<type::string_t
>>(std::move(data
));
213 // The strings have been moved.
214 EXPECT_THAT(data
, ::testing::ElementsAre("", ""));
215 ASSERT_EQ(value
.getType(), Value::Type::listValue
);
216 ASSERT_EQ(value
.get_listValue().size(), 2);
217 EXPECT_EQ(value
.get_listValue()[0].get_stringValue(), "hi");
218 EXPECT_EQ(value
.get_listValue()[1].get_stringValue(), "bye");
221 TEST(ObjectTest
, Set
) {
222 std::set
<int> data
= {1, 4, 2};
223 Value value
= asValueStruct
<type::set
<type::i16_t
>>(data
);
224 ASSERT_EQ(value
.getType(), Value::Type::setValue
);
225 ASSERT_EQ(value
.get_setValue().size(), data
.size());
226 for (size_t i
= 0; i
< data
.size(); ++i
) {
228 at(value
.get_setValue(), i
), asValueStruct
<type::i16_t
>(at(data
, i
)));
231 // Works with other containers
232 value
= asValueStruct
<type::set
<type::i16_t
>>(
233 std::vector
<int>(data
.begin(), data
.end()));
234 ASSERT_EQ(value
.getType(), Value::Type::setValue
);
235 ASSERT_EQ(value
.get_setValue().size(), data
.size());
236 for (size_t i
= 0; i
< data
.size(); ++i
) {
238 at(value
.get_setValue(), i
), asValueStruct
<type::i16_t
>(at(data
, i
)));
242 TEST(ObjectTest
, Map
) {
243 std::map
<std::string
, int> data
= {{"one", 1}, {"four", 4}, {"two", 2}};
244 Value value
= asValueStruct
<type::map
<type::string_t
, type::byte_t
>>(data
);
245 ASSERT_EQ(value
.getType(), Value::Type::mapValue
);
246 ASSERT_EQ(value
.get_mapValue().size(), data
.size());
247 for (const auto& entry
: data
) {
249 value
.get_mapValue().find(asValueStruct
<type::string_t
>(entry
.first
));
250 ASSERT_NE(itr
, value
.get_mapValue().end());
251 EXPECT_EQ(itr
->second
, asValueStruct
<type::byte_t
>(entry
.second
));
254 // Works with other containers.
255 std::vector
<std::pair
<std::string
, int>> otherData(data
.begin(), data
.end());
256 value
= asValueStruct
<type::map
<type::string_t
, type::byte_t
>>(otherData
);
257 ASSERT_EQ(value
.getType(), Value::Type::mapValue
);
258 ASSERT_EQ(value
.get_mapValue().size(), data
.size());
259 for (const auto& entry
: data
) {
261 value
.get_mapValue().find(asValueStruct
<type::string_t
>(entry
.first
));
262 ASSERT_NE(itr
, value
.get_mapValue().end());
263 EXPECT_EQ(itr
->second
, asValueStruct
<type::byte_t
>(entry
.second
));
267 TEST(ObjectTest
, Struct
) {
268 // TODO(afuller): Use a struct that covers more cases.
269 auto protocol
= ::apache::thrift::conformance::Protocol("hi").asStruct();
270 Value value
= asValueStruct
<type::union_c
>(protocol
);
271 ASSERT_EQ(value
.getType(), Value::Type::objectValue
);
272 const Object
& object
= value
.get_objectValue();
273 EXPECT_EQ(object
.members_ref()->size(), 2);
275 object
.members_ref()->at(1),
276 asValueStruct
<type::enum_c
>(
277 ::apache::thrift::conformance::StandardProtocol::Custom
));
278 EXPECT_EQ(object
.members_ref()->at(2), asValueStruct
<type::binary_t
>("hi"));
281 TEST(ObjectTest
, StructWithList
) {
282 testset::struct_with
<type::list
<type::i32_t
>> s
;
283 std::vector
<int> listValues
= {1, 2, 3};
284 s
.field_1_ref() = listValues
;
285 Value value
= asValueStruct
<type::struct_c
>(s
);
286 ASSERT_EQ(value
.getType(), Value::Type::objectValue
);
287 const Object
& object
= value
.get_objectValue();
288 EXPECT_EQ(object
.members_ref()->size(), 1);
290 object
.members_ref()->at(1),
291 asValueStruct
<type::list
<type::i32_t
>>(listValues
));
294 TEST(ObjectTest
, StructWithMap
) {
295 testset::struct_with
<type::map
<type::string_t
, type::i32_t
>> s
;
296 std::map
<std::string
, int> mapValues
= {{"one", 1}, {"four", 4}, {"two", 2}};
297 s
.field_1_ref() = mapValues
;
298 Value value
= asValueStruct
<type::struct_c
>(s
);
299 ASSERT_EQ(value
.getType(), Value::Type::objectValue
);
300 const Object
& object
= value
.get_objectValue();
301 EXPECT_EQ(object
.members_ref()->size(), 1);
302 auto val
= asValueStruct
<type::map
<type::binary_t
, type::i32_t
>>(mapValues
);
303 EXPECT_EQ(object
.members_ref()->at(1), val
);
306 TEST(ObjectTest
, StructWithSet
) {
307 testset::struct_with
<type::set
<type::i64_t
>> s
;
308 std::set
<long> setValues
= {1, 2, 3};
309 s
.field_1_ref() = setValues
;
310 Value value
= asValueStruct
<type::struct_c
>(s
);
311 ASSERT_EQ(value
.getType(), Value::Type::objectValue
);
312 const Object
& object
= value
.get_objectValue();
313 EXPECT_EQ(object
.members_ref()->size(), 1);
315 object
.members_ref()->at(1),
316 asValueStruct
<type::set
<type::i64_t
>>(setValues
));
319 TEST(ObjectTest
, parseObject
) {
320 folly::IOBufQueue iobufQueue
;
321 testset::struct_with
<type::set
<type::i64_t
>> thriftStruct
;
322 std::set
<long> setValues
= {1, 2, 3};
323 thriftStruct
.field_1_ref() = setValues
;
324 BinarySerializer::serialize(thriftStruct
, &iobufQueue
);
325 auto serialized
= iobufQueue
.move();
326 auto object
= parseObject
<BinarySerializer::ProtocolReader
>(*serialized
);
327 EXPECT_EQ(object
.members_ref()->size(), 1);
329 object
.members_ref()->at(1),
330 asValueStruct
<type::set
<type::i64_t
>>(setValues
));
333 TEST(ObjectTest
, serializeObject
) {
334 folly::IOBufQueue iobufQueue
;
335 testset::struct_with
<type::set
<type::i64_t
>> thriftStruct
;
336 std::set
<long> setValues
= {1, 2, 3};
337 thriftStruct
.field_1_ref() = setValues
;
338 BinarySerializer::serialize(thriftStruct
, &iobufQueue
);
339 auto expected
= iobufQueue
.move();
340 auto object
= parseObject
<BinarySerializer::ProtocolReader
>(*expected
);
341 auto actual
= serializeObject
<BinarySerializer::ProtocolWriter
>(object
);
342 EXPECT_TRUE(folly::IOBufEqualTo
{}(*actual
, *expected
));
345 TEST(ObjectTest
, ValueUnionTypeMatch
) {
347 static_cast<type::BaseType
>(Value::Type::boolValue
),
348 type::BaseType::Bool
);
350 static_cast<type::BaseType
>(Value::Type::byteValue
),
351 type::BaseType::Byte
);
353 static_cast<type::BaseType
>(Value::Type::i16Value
), type::BaseType::I16
);
355 static_cast<type::BaseType
>(Value::Type::i32Value
), type::BaseType::I32
);
357 static_cast<type::BaseType
>(Value::Type::i64Value
), type::BaseType::I64
);
359 static_cast<type::BaseType
>(Value::Type::floatValue
),
360 type::BaseType::Float
);
362 static_cast<type::BaseType
>(Value::Type::doubleValue
),
363 type::BaseType::Double
);
365 static_cast<type::BaseType
>(Value::Type::stringValue
),
366 type::BaseType::String
);
368 static_cast<type::BaseType
>(Value::Type::binaryValue
),
369 type::BaseType::Binary
);
371 static_cast<type::BaseType
>(Value::Type::listValue
),
372 type::BaseType::List
);
374 static_cast<type::BaseType
>(Value::Type::setValue
), type::BaseType::Set
);
376 static_cast<type::BaseType
>(Value::Type::mapValue
), type::BaseType::Map
);
378 static_cast<type::BaseType
>(Value::Type::objectValue
),
379 type::BaseType::Struct
);
382 template <typename ParseObjectTestCase
>
383 class TypedParseObjectTest
: public testing::Test
{};
385 template <::apache::thrift::conformance::StandardProtocol Protocol
, typename T
>
386 std::unique_ptr
<folly::IOBuf
> serialize(T
& s
) {
387 folly::IOBufQueue iobufQueue
;
388 protocol_writer_t
<Protocol
> writer
;
389 writer
.setOutput(&iobufQueue
);
391 auto iobuf
= iobufQueue
.move();
396 ::apache::thrift::conformance::StandardProtocol Protocol
,
399 void testParseObject() {
401 for (const auto& val
: data::ValueGenerator
<Tag
>::getKeyValues()) {
402 SCOPED_TRACE(val
.name
);
403 testsetValue
.field_1_ref() = val
.value
;
404 auto valueStruct
= asValueStruct
<type::struct_c
>(testsetValue
);
405 const Object
& object
= valueStruct
.get_objectValue();
407 auto iobuf
= serialize
<Protocol
, T
>(testsetValue
);
408 auto objFromParseObject
= parseObject
<protocol_reader_t
<Protocol
>>(*iobuf
);
409 EXPECT_EQ(objFromParseObject
, object
);
413 template <typename Tag
>
414 bool hasEmptyContainer(const type::standard_type
<Tag
>& value
) {
415 if constexpr (type::is_a_v
<Tag
, type::container_c
>) {
416 if (value
.size() == 0) {
420 if constexpr (type::is_a_v
<Tag
, type::map
<type::all_c
, type::container_c
>>) {
421 for (const auto& [mapkey
, mapval
] : value
) {
422 if (mapval
.size() == 0) {
431 ::apache::thrift::conformance::StandardProtocol Protocol
,
434 void testSerializeObject() {
436 for (const auto& val
: data::ValueGenerator
<Tag
>::getKeyValues()) {
437 SCOPED_TRACE(val
.name
);
438 testsetValue
.field_1_ref() = val
.value
;
439 auto valueStruct
= asValueStruct
<type::struct_c
>(testsetValue
);
440 const Object
& object
= valueStruct
.get_objectValue();
441 auto schemalessSerializedData
=
442 serializeObject
<protocol_writer_t
<Protocol
>>(object
);
444 auto schemaBasedSerializedData
= serialize
<Protocol
, T
>(testsetValue
);
446 // FIXME: skip validation for structs with empty list, set, map.
447 if (hasEmptyContainer
<Tag
>(val
.value
)) {
450 EXPECT_TRUE(folly::IOBufEqualTo
{}(
451 *schemalessSerializedData
, *schemaBasedSerializedData
));
456 ::apache::thrift::conformance::StandardProtocol Protocol
,
459 void testSerializeObjectAny() {
460 AnyRegistry registry
;
461 registry
.registerType
<T
>(
462 createThriftTypeInfo({"facebook.com/thrift/conformance/struct_with"}));
463 registry
.registerSerializer
<T
>(&getAnyStandardSerializer
<T
, Protocol
>());
464 for (const auto& val
: data::ValueGenerator
<Tag
>::getKeyValues()) {
465 SCOPED_TRACE(val
.name
);
466 RoundTripResponse anyValue
;
468 testsetValue
.field_1_ref() = val
.value
;
469 anyValue
.value_ref() = registry
.store
<Protocol
>(testsetValue
);
471 auto schemaBasedSerializedData
= serialize
<Protocol
>(anyValue
);
472 auto objFromParseObject
=
473 parseObject
<protocol_reader_t
<Protocol
>>(*schemaBasedSerializedData
);
475 auto schemalessSerializedData
=
476 serializeObject
<protocol_writer_t
<Protocol
>>(objFromParseObject
);
478 EXPECT_TRUE(folly::IOBufEqualTo
{}(
479 *schemalessSerializedData
, *schemaBasedSerializedData
));
483 // The tests cases to run.
484 using ParseObjectTestCases
= ::testing::Types
<
494 type::list
<type::i64_t
>,
495 type::list
<type::string_t
>,
496 type::set
<type::i64_t
>,
497 type::set
<type::string_t
>,
498 type::map
<type::string_t
, type::i64_t
>,
499 type::map
<type::i64_t
, type::double_t
>,
500 type::map
<type::i64_t
, type::set
<type::string_t
>>>;
502 TYPED_TEST_SUITE(TypedParseObjectTest
, ParseObjectTestCases
);
504 TYPED_TEST(TypedParseObjectTest
, ParseSerializedSameAsDirectObject
) {
506 ::apache::thrift::conformance::StandardProtocol::Binary
,
508 testset::struct_with
<TypeParam
>>();
510 ::apache::thrift::conformance::StandardProtocol::Compact
,
512 testset::struct_with
<TypeParam
>>();
514 ::apache::thrift::conformance::StandardProtocol::Binary
,
516 testset::union_with
<TypeParam
>>();
518 ::apache::thrift::conformance::StandardProtocol::Compact
,
520 testset::union_with
<TypeParam
>>();
523 TYPED_TEST(TypedParseObjectTest
, SerializeObjectSameAsDirectSerialization
) {
525 ::apache::thrift::conformance::StandardProtocol::Binary
,
527 testset::struct_with
<TypeParam
>>();
529 ::apache::thrift::conformance::StandardProtocol::Compact
,
531 testset::struct_with
<TypeParam
>>();
533 ::apache::thrift::conformance::StandardProtocol::Binary
,
535 testset::union_with
<TypeParam
>>();
537 ::apache::thrift::conformance::StandardProtocol::Compact
,
539 testset::union_with
<TypeParam
>>();
542 TYPED_TEST(TypedParseObjectTest
, SerializeObjectSameAsDirectSerializationAny
) {
543 testSerializeObjectAny
<
544 ::apache::thrift::conformance::StandardProtocol::Binary
,
546 testset::struct_with
<TypeParam
>>();
547 testSerializeObjectAny
<
548 ::apache::thrift::conformance::StandardProtocol::Compact
,
550 testset::struct_with
<TypeParam
>>();
551 testSerializeObjectAny
<
552 ::apache::thrift::conformance::StandardProtocol::Binary
,
554 testset::union_with
<TypeParam
>>();
555 testSerializeObjectAny
<
556 ::apache::thrift::conformance::StandardProtocol::Compact
,
558 testset::union_with
<TypeParam
>>();
561 TEST(Object
, invalid_object
) {
564 obj
[FieldId
{0}].emplace_list() = {
565 asValueStruct
<type::i32_t
>(1), asValueStruct
<type::i64_t
>(1)};
567 serializeObject
<CompactSerializer::ProtocolWriter
>(obj
),
572 obj
[FieldId
{0}].emplace_set() = {
573 asValueStruct
<type::i32_t
>(1), asValueStruct
<type::i64_t
>(1)};
575 serializeObject
<CompactSerializer::ProtocolWriter
>(obj
),
580 obj
[FieldId
{0}].emplace_map() = {
581 {asValueStruct
<type::i32_t
>(1), asValueStruct
<type::i32_t
>(1)},
582 {asValueStruct
<type::i32_t
>(2), asValueStruct
<type::i64_t
>(1)}};
584 serializeObject
<CompactSerializer::ProtocolWriter
>(obj
),
589 obj
[FieldId
{0}].emplace_map() = {
590 {asValueStruct
<type::i32_t
>(1), asValueStruct
<type::i32_t
>(1)},
591 {asValueStruct
<type::i64_t
>(1), asValueStruct
<type::i32_t
>(1)}};
593 serializeObject
<CompactSerializer::ProtocolWriter
>(obj
),
599 EXPECT_EQ(uri
<Object
>(), "facebook.com/thrift/protocol/Object");
600 EXPECT_EQ(uri
<Value
>(), "facebook.com/thrift/protocol/Value");
603 TEST(Object
, Wrapper
) {
605 EXPECT_TRUE(object
.empty());
607 EXPECT_FALSE(object
.empty());
609 EXPECT_EQ(object
.size(), 2);
610 EXPECT_EQ(&object
[FieldId
{0}], &object
.members()[0]);
611 EXPECT_EQ(&object
[FieldId
{2}], &object
.members()[2]);
612 EXPECT_EQ(&object
.at(FieldId
{0}), &object
.members()[0]);
613 EXPECT_EQ(&object
.at(FieldId
{2}), &object
.members()[2]);
614 EXPECT_EQ(object
.if_contains(FieldId
{0}), &object
.members()[0]);
615 EXPECT_EQ(object
.if_contains(FieldId
{2}), &object
.members()[2]);
617 EXPECT_EQ(object
.contains(FieldId
{0}), true);
618 EXPECT_EQ(object
.contains(FieldId
{1}), false);
619 EXPECT_EQ(object
.contains(FieldId
{2}), true);
620 EXPECT_THROW(object
.at(FieldId
{1}), std::out_of_range
);
622 std::vector
<int16_t> ids
;
623 std::vector
<Value
*> values
;
624 for (auto&& i
: object
) {
625 ids
.push_back(i
.first
);
626 values
.push_back(&i
.second
);
629 EXPECT_EQ(ids
.size(), 2);
630 EXPECT_EQ(ids
[0], 0);
631 EXPECT_EQ(ids
[1], 2);
632 EXPECT_EQ(values
.size(), 2);
633 EXPECT_EQ(values
[0], &object
.members()[0]);
634 EXPECT_EQ(values
[1], &object
.members()[2]);
636 EXPECT_EQ(object
.erase(FieldId
{0}), 1);
637 EXPECT_EQ(object
.contains(FieldId
{0}), false);
638 EXPECT_EQ(object
.contains(FieldId
{2}), true);
639 EXPECT_EQ(object
.size(), 1);
641 EXPECT_EQ(object
.erase(FieldId
{1}), 0);
642 EXPECT_EQ(object
.size(), 1);
643 EXPECT_FALSE(object
.empty());
645 EXPECT_EQ(object
.erase(FieldId
{2}), 1);
646 EXPECT_EQ(object
.size(), 0);
647 EXPECT_TRUE(object
.empty());
650 TEST(Value
, Wrapper
) {
652 obj
.members()[100] = asValueStruct
<type::string_t
>("200");
654 const std::vector
<Value
> l
= {
655 asValueStruct
<type::i32_t
>(10), asValueStruct
<type::i32_t
>(20)};
657 const std::set
<Value
> s
= {
658 asValueStruct
<type::i32_t
>(30), asValueStruct
<type::i32_t
>(40)};
660 const std::map
<Value
, Value
> m
= {
661 {asValueStruct
<type::i32_t
>(50), asValueStruct
<type::i32_t
>(60)},
662 {asValueStruct
<type::i32_t
>(70), asValueStruct
<type::i32_t
>(80)}};
666 #define FBTHRIFT_TEST_THRIFT_VALUE_TYPE(TYPE, VALUE) \
668 EXPECT_FALSE(value.is_##TYPE()); \
669 EXPECT_FALSE(value.TYPE##Value_ref()); \
670 EXPECT_THROW(value.as_##TYPE(), apache::thrift::bad_field_access); \
671 EXPECT_EQ(value.if_##TYPE(), nullptr); \
672 EXPECT_FALSE(value.TYPE##Value_ref()); \
673 value.emplace_##TYPE() = VALUE; \
674 EXPECT_TRUE(value.is_##TYPE()); \
675 EXPECT_EQ(value.as_##TYPE(), VALUE); \
676 EXPECT_EQ(*value.if_##TYPE(), VALUE); \
677 EXPECT_EQ(value.TYPE##Value_ref(), VALUE); \
680 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(bool, true);
681 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(byte
, 1);
682 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(i16
, 2);
683 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(i32
, 3);
684 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(i64
, 4);
685 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(float, 5);
686 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(double, 6);
687 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(string
, "7");
688 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(object
, obj
);
689 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(list
, l
);
690 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(set
, s
);
691 FBTHRIFT_TEST_THRIFT_VALUE_TYPE(map
, m
);
693 #undef FBTHRIFT_VALUE_TEST_TYPE
695 // `binary` type requires special code since IOBuf doesn't have operator==
696 const auto buf
= *folly::IOBuf::copyBuffer("90");
697 EXPECT_FALSE(value
.is_binary());
698 EXPECT_FALSE(value
.binaryValue_ref());
699 EXPECT_THROW(value
.as_binary(), apache::thrift::bad_field_access
);
700 EXPECT_EQ(value
.if_binary(), nullptr);
701 EXPECT_FALSE(value
.binaryValue_ref());
702 value
.emplace_binary() = buf
;
703 EXPECT_TRUE(value
.is_binary());
704 EXPECT_TRUE(folly::IOBufEqualTo
{}(value
.as_binary(), buf
));
705 EXPECT_TRUE(folly::IOBufEqualTo
{}(*value
.if_binary(), buf
));
706 EXPECT_TRUE(folly::IOBufEqualTo
{}(value
.binaryValue_ref().value(), buf
));
709 TEST(Value
, IsIntrinsicDefaultTrue
) {
710 EXPECT_TRUE(isIntrinsicDefault(asValueStruct
<type::bool_t
>(false)));
711 EXPECT_TRUE(isIntrinsicDefault(asValueStruct
<type::byte_t
>(0)));
712 EXPECT_TRUE(isIntrinsicDefault(asValueStruct
<type::i16_t
>(0)));
713 EXPECT_TRUE(isIntrinsicDefault(asValueStruct
<type::i32_t
>(0)));
714 EXPECT_TRUE(isIntrinsicDefault(asValueStruct
<type::i64_t
>(0)));
715 EXPECT_TRUE(isIntrinsicDefault(asValueStruct
<type::float_t
>(0.0)));
716 EXPECT_TRUE(isIntrinsicDefault(asValueStruct
<type::double_t
>(0.0)));
717 EXPECT_TRUE(isIntrinsicDefault(asValueStruct
<type::string_t
>("")));
718 EXPECT_TRUE(isIntrinsicDefault(asValueStruct
<type::binary_t
>("")));
719 EXPECT_TRUE(isIntrinsicDefault(
720 asValueStruct
<type::list
<type::string_t
>>(std::vector
<std::string
>{})));
721 EXPECT_TRUE(isIntrinsicDefault(
722 asValueStruct
<type::set
<type::i64_t
>>(std::set
<int>{})));
724 isIntrinsicDefault(asValueStruct
<type::map
<type::i32_t
, type::string_t
>>(
725 std::map
<int, std::string
>{})));
726 testset::struct_with
<type::map
<type::string_t
, type::i32_t
>> s
;
727 s
.field_1_ref() = std::map
<std::string
, int>{};
728 Value objectValue
= asValueStruct
<type::struct_c
>(s
);
729 EXPECT_TRUE(isIntrinsicDefault(objectValue
));
730 EXPECT_TRUE(isIntrinsicDefault(objectValue
.as_object()));
731 EXPECT_TRUE(isIntrinsicDefault(Value
{}));
734 TEST(Value
, IsIntrinsicDefaultFalse
) {
735 EXPECT_FALSE(isIntrinsicDefault(asValueStruct
<type::bool_t
>(true)));
736 EXPECT_FALSE(isIntrinsicDefault(asValueStruct
<type::byte_t
>(1)));
737 EXPECT_FALSE(isIntrinsicDefault(asValueStruct
<type::i16_t
>(1)));
738 EXPECT_FALSE(isIntrinsicDefault(asValueStruct
<type::i32_t
>(1)));
739 EXPECT_FALSE(isIntrinsicDefault(asValueStruct
<type::i64_t
>(1)));
740 EXPECT_FALSE(isIntrinsicDefault(asValueStruct
<type::float_t
>(0.5)));
741 EXPECT_FALSE(isIntrinsicDefault(asValueStruct
<type::double_t
>(0.5)));
742 EXPECT_FALSE(isIntrinsicDefault(asValueStruct
<type::string_t
>("foo")));
743 EXPECT_FALSE(isIntrinsicDefault(asValueStruct
<type::binary_t
>("foo")));
744 EXPECT_FALSE(isIntrinsicDefault(asValueStruct
<type::list
<type::string_t
>>(
745 std::vector
<std::string
>{"foo"})));
746 EXPECT_FALSE(isIntrinsicDefault(
747 asValueStruct
<type::set
<type::i64_t
>>(std::set
<int>{1, 2, 3})));
749 isIntrinsicDefault(asValueStruct
<type::map
<type::i32_t
, type::string_t
>>(
750 std::map
<int, std::string
>{{1, "foo"}, {2, "bar"}})));
751 testset::struct_with
<type::map
<type::string_t
, type::i32_t
>> s
;
752 s
.field_1_ref() = std::map
<std::string
, int>{{"foo", 1}, {"bar", 2}};
753 Value objectValue
= asValueStruct
<type::struct_c
>(s
);
754 EXPECT_FALSE(isIntrinsicDefault(objectValue
));
755 EXPECT_FALSE(isIntrinsicDefault(objectValue
.as_object()));
758 } // namespace apache::thrift::protocol