Re-sync with internal repository
[hiphop-php.git] / third-party / thrift / src / thrift / lib / cpp2 / test / ObjectTest.cpp
blobd28ce26d8f15c7a1fce0f316b2d87058756fa0b2
1 /*
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>
19 #include <set>
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 {
48 namespace {
50 namespace testset = apache::thrift::test::testset;
52 template <typename C>
53 decltype(auto) at(C& container, size_t i) {
54 auto itr = container.begin();
55 std::advance(itr, i);
56 return *itr;
59 TEST(ObjectTest, Example) {
60 using facebook::thrift::lib::test::Bar;
62 Bar 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);
76 // Test round-trip.
77 EXPECT_EQ(CompactSerializer::deserialize<Bar>(serialized.get()), bar);
79 // Test constructing the same Object manually
80 Object foo;
81 foo[FieldId{1}].emplace_i32() = 42;
82 foo[FieldId{2}].emplace_binary() = *folly::IOBuf::copyBuffer("Everything");
84 Object obj2;
85 obj2[FieldId{10}] = asValueStruct<type::list<type::binary_t>>(*bar.field_3());
86 obj2[FieldId{20}].emplace_object() = foo;
88 EXPECT_EQ(obj, obj2);
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);
196 EXPECT_EQ(s1, "");
197 EXPECT_EQ(s2, "hi");
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) {
227 EXPECT_EQ(
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) {
237 EXPECT_EQ(
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) {
248 auto itr =
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) {
260 auto itr =
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);
274 EXPECT_EQ(
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);
289 EXPECT_EQ(
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);
314 EXPECT_EQ(
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);
328 EXPECT_EQ(
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) {
346 EXPECT_EQ(
347 static_cast<type::BaseType>(Value::Type::boolValue),
348 type::BaseType::Bool);
349 EXPECT_EQ(
350 static_cast<type::BaseType>(Value::Type::byteValue),
351 type::BaseType::Byte);
352 EXPECT_EQ(
353 static_cast<type::BaseType>(Value::Type::i16Value), type::BaseType::I16);
354 EXPECT_EQ(
355 static_cast<type::BaseType>(Value::Type::i32Value), type::BaseType::I32);
356 EXPECT_EQ(
357 static_cast<type::BaseType>(Value::Type::i64Value), type::BaseType::I64);
358 EXPECT_EQ(
359 static_cast<type::BaseType>(Value::Type::floatValue),
360 type::BaseType::Float);
361 EXPECT_EQ(
362 static_cast<type::BaseType>(Value::Type::doubleValue),
363 type::BaseType::Double);
364 EXPECT_EQ(
365 static_cast<type::BaseType>(Value::Type::stringValue),
366 type::BaseType::String);
367 EXPECT_EQ(
368 static_cast<type::BaseType>(Value::Type::binaryValue),
369 type::BaseType::Binary);
370 EXPECT_EQ(
371 static_cast<type::BaseType>(Value::Type::listValue),
372 type::BaseType::List);
373 EXPECT_EQ(
374 static_cast<type::BaseType>(Value::Type::setValue), type::BaseType::Set);
375 EXPECT_EQ(
376 static_cast<type::BaseType>(Value::Type::mapValue), type::BaseType::Map);
377 EXPECT_EQ(
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);
390 s.write(&writer);
391 auto iobuf = iobufQueue.move();
392 return iobuf;
395 template <
396 ::apache::thrift::conformance::StandardProtocol Protocol,
397 typename Tag,
398 typename T>
399 void testParseObject() {
400 T testsetValue;
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) {
417 return true;
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) {
423 return true;
427 return false;
430 template <
431 ::apache::thrift::conformance::StandardProtocol Protocol,
432 typename Tag,
433 typename T>
434 void testSerializeObject() {
435 T testsetValue;
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)) {
448 continue;
450 EXPECT_TRUE(folly::IOBufEqualTo{}(
451 *schemalessSerializedData, *schemaBasedSerializedData));
455 template <
456 ::apache::thrift::conformance::StandardProtocol Protocol,
457 typename Tag,
458 typename T>
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;
467 T testsetValue;
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<
485 type::bool_t,
486 type::byte_t,
487 type::i16_t,
488 type::i32_t,
489 type::i64_t,
490 type::float_t,
491 type::double_t,
492 type::binary_t,
493 type::string_t,
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) {
505 testParseObject<
506 ::apache::thrift::conformance::StandardProtocol::Binary,
507 TypeParam,
508 testset::struct_with<TypeParam>>();
509 testParseObject<
510 ::apache::thrift::conformance::StandardProtocol::Compact,
511 TypeParam,
512 testset::struct_with<TypeParam>>();
513 testParseObject<
514 ::apache::thrift::conformance::StandardProtocol::Binary,
515 TypeParam,
516 testset::union_with<TypeParam>>();
517 testParseObject<
518 ::apache::thrift::conformance::StandardProtocol::Compact,
519 TypeParam,
520 testset::union_with<TypeParam>>();
523 TYPED_TEST(TypedParseObjectTest, SerializeObjectSameAsDirectSerialization) {
524 testSerializeObject<
525 ::apache::thrift::conformance::StandardProtocol::Binary,
526 TypeParam,
527 testset::struct_with<TypeParam>>();
528 testSerializeObject<
529 ::apache::thrift::conformance::StandardProtocol::Compact,
530 TypeParam,
531 testset::struct_with<TypeParam>>();
532 testSerializeObject<
533 ::apache::thrift::conformance::StandardProtocol::Binary,
534 TypeParam,
535 testset::union_with<TypeParam>>();
536 testSerializeObject<
537 ::apache::thrift::conformance::StandardProtocol::Compact,
538 TypeParam,
539 testset::union_with<TypeParam>>();
542 TYPED_TEST(TypedParseObjectTest, SerializeObjectSameAsDirectSerializationAny) {
543 testSerializeObjectAny<
544 ::apache::thrift::conformance::StandardProtocol::Binary,
545 TypeParam,
546 testset::struct_with<TypeParam>>();
547 testSerializeObjectAny<
548 ::apache::thrift::conformance::StandardProtocol::Compact,
549 TypeParam,
550 testset::struct_with<TypeParam>>();
551 testSerializeObjectAny<
552 ::apache::thrift::conformance::StandardProtocol::Binary,
553 TypeParam,
554 testset::union_with<TypeParam>>();
555 testSerializeObjectAny<
556 ::apache::thrift::conformance::StandardProtocol::Compact,
557 TypeParam,
558 testset::union_with<TypeParam>>();
561 TEST(Object, invalid_object) {
563 Object obj;
564 obj[FieldId{0}].emplace_list() = {
565 asValueStruct<type::i32_t>(1), asValueStruct<type::i64_t>(1)};
566 EXPECT_THROW(
567 serializeObject<CompactSerializer::ProtocolWriter>(obj),
568 TProtocolException);
571 Object obj;
572 obj[FieldId{0}].emplace_set() = {
573 asValueStruct<type::i32_t>(1), asValueStruct<type::i64_t>(1)};
574 EXPECT_THROW(
575 serializeObject<CompactSerializer::ProtocolWriter>(obj),
576 TProtocolException);
579 Object 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)}};
583 EXPECT_THROW(
584 serializeObject<CompactSerializer::ProtocolWriter>(obj),
585 TProtocolException);
588 Object 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)}};
592 EXPECT_THROW(
593 serializeObject<CompactSerializer::ProtocolWriter>(obj),
594 TProtocolException);
598 TEST(Object, uri) {
599 EXPECT_EQ(uri<Object>(), "facebook.com/thrift/protocol/Object");
600 EXPECT_EQ(uri<Value>(), "facebook.com/thrift/protocol/Value");
603 TEST(Object, Wrapper) {
604 Object object;
605 EXPECT_TRUE(object.empty());
606 object.members()[0];
607 EXPECT_FALSE(object.empty());
608 object.members()[2];
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) {
651 Object obj;
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)}};
664 Value value;
666 #define FBTHRIFT_TEST_THRIFT_VALUE_TYPE(TYPE, VALUE) \
667 do { \
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); \
678 } while (false)
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>{})));
723 EXPECT_TRUE(
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})));
748 EXPECT_FALSE(
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()));
757 } // namespace
758 } // namespace apache::thrift::protocol