1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 #include <folly/portability/GMock.h>
18 #include <folly/portability/GTest.h>
19
20 #include <thrift/lib/cpp2/frozen/FrozenUtil.h>
21 #include <thrift/lib/cpp2/frozen/test/gen-cpp2/Example_layouts.h>
22 #include <thrift/lib/cpp2/frozen/test/gen-cpp2/Example_types_custom_protocol.h>
23 #include <thrift/lib/cpp2/protocol/DebugProtocol.h>
24 #include <thrift/lib/cpp2/protocol/Serializer.h>
25
26 namespace {
27 using namespace apache::thrift;
28 using namespace apache::thrift::frozen;
29 using namespace apache::thrift::test;
30 using namespace apache::thrift::util;
31 using namespace testing;
32 using Fixed8 = apache::thrift::frozen::FixedSizeString<8>;
33 using Fixed2 = apache::thrift::frozen::FixedSizeString<2>;
34
35 template <class T>
toString(const T & x)36 std::string toString(const T& x) {
37 return debugString(x);
38 }
39 template <class T>
toString(const Layout<T> & x)40 std::string toString(const Layout<T>& x) {
41 std::ostringstream xStr;
42 xStr << x;
43 return xStr.str();
44 }
45
46 #define EXPECT_PRINTED_EQ(a, b) EXPECT_EQ(toString(a), toString(b))
47
__anon45252c020202null48 EveryLayout stressValue2 = [] {
49 EveryLayout x;
50 *x.aBool_ref() = true;
51 *x.aInt_ref() = 2;
52 *x.aList_ref() = {3, 5};
53 *x.aSet_ref() = {7, 11};
54 *x.aHashSet_ref() = {13, 17};
55 *x.aMap_ref() = {{19, 23}, {29, 31}};
56 *x.aHashMap_ref() = {{37, 41}, {43, 47}};
57 x.optInt_ref() = 53;
58 *x.aFloat_ref() = 59.61;
59 x.optMap_ref() = {{2, 4}, {3, 9}};
60 return x;
61 }();
62
63 template <class T>
layout(const T & x,Layout<T> && layout=Layout<T> ())64 Layout<T>&& layout(const T& x, Layout<T>&& layout = Layout<T>()) {
65 size_t size = LayoutRoot::layout(x, layout);
66 (void)size;
67 return std::move(layout);
68 }
69
70 template <class T>
layout(const T & x,size_t & size)71 Layout<T> layout(const T& x, size_t& size) {
72 Layout<T> layout;
73 size = LayoutRoot::layout(x, layout);
74 return std::move(layout);
75 }
76
__anon45252c020302null77 auto tom1 = [] {
78 Pet1 max;
79 *max.name_ref() = "max";
80 Pet1 ed;
81 *ed.name_ref() = "ed";
82 Person1 tom;
83 *tom.name_ref() = "tom";
84 *tom.height_ref() = 1.82f;
85 tom.age_ref() = 30;
86 tom.pets_ref()->push_back(max);
87 tom.pets_ref()->push_back(ed);
88 return tom;
89 }();
__anon45252c020402null90 auto tom2 = [] {
91 Pet2 max;
92 *max.name_ref() = "max";
93 Pet2 ed;
94 *ed.name_ref() = "ed";
95 Person2 tom;
96 *tom.name_ref() = "tom";
97 *tom.weight_ref() = 169;
98 tom.age_ref() = 30;
99 tom.pets_ref()->push_back(max);
100 tom.pets_ref()->push_back(ed);
101 return tom;
102 }();
103
TEST(Frozen,EndToEnd)104 TEST(Frozen, EndToEnd) {
105 auto view = freeze(tom1);
106 EXPECT_EQ(*tom1.name_ref(), view.name());
107 ASSERT_TRUE(view.name_ref().has_value());
108 EXPECT_EQ(*tom1.name_ref(), *view.name_ref());
109 ASSERT_TRUE(view.age().has_value());
110 EXPECT_EQ(*tom1.age_ref(), view.age().value());
111 EXPECT_EQ(*tom1.height_ref(), view.height());
112 EXPECT_EQ(view.pets()[0].name(), *tom1.pets_ref()[0].name_ref());
113 auto& pets = *tom1.pets_ref();
114 auto fpets = view.pets();
115 ASSERT_EQ(pets.size(), fpets.size());
116 for (size_t i = 0; i < tom1.pets_ref()->size(); ++i) {
117 EXPECT_EQ(*pets[i].name_ref(), fpets[i].name());
118 }
119 Layout<Person1> layout;
120 LayoutRoot::layout(tom1, layout);
121 auto ttom = view.thaw();
122 EXPECT_TRUE(ttom.name_ref().has_value());
123 EXPECT_PRINTED_EQ(tom1, ttom);
124 }
125
TEST(Frozen,Comparison)126 TEST(Frozen, Comparison) {
127 EXPECT_EQ(frozenSize(tom1), frozenSize(tom2));
128 auto view1 = freeze(tom1);
129 auto view2 = freeze(tom2);
130 // name is optional now, and was __isset=true, so we don't write it
131 ASSERT_TRUE(view2.age().has_value());
132 EXPECT_EQ(*tom2.age_ref(), view2.age().value());
133 EXPECT_EQ(*tom2.name_ref(), view2.name());
134 EXPECT_EQ(tom2.name_ref()->size(), view2.name().size());
135 auto ttom2 = view2.thaw();
136 EXPECT_EQ(tom2, ttom2) << debugString(tom2) << debugString(ttom2);
137 }
138
TEST(Frozen,Compatibility)139 TEST(Frozen, Compatibility) {
140 // Make sure Person1 works well with Person2
141 schema::MemorySchema schema;
142 Layout<Person1> person1;
143 Layout<Person2> person2;
144
145 size_t size = LayoutRoot::layout(tom1, person1);
146
147 saveRoot(person1, schema);
148 loadRoot(person2, schema);
149
150 std::string storage(size, 'X');
151 folly::MutableStringPiece charRange(&storage.front(), size);
152 const folly::MutableByteRange bytes(charRange);
153 folly::MutableByteRange freezeRange = bytes;
154
155 ByteRangeFreezer::freeze(person1, tom1, freezeRange);
156 auto view12 = person1.view({bytes.begin(), 0});
157 auto view21 = person2.view({bytes.begin(), 0});
158 EXPECT_EQ(view12.name(), view21.name());
159 EXPECT_EQ(view12.age(), view21.age());
160 EXPECT_TRUE(view12.height());
161 EXPECT_FALSE(view21.weight());
162 ASSERT_GE(view12.pets().size(), 2);
163 EXPECT_EQ(view12.pets()[0].name(), view21.pets()[0].name());
164 EXPECT_EQ(view12.pets()[1].name(), view21.pets()[1].name());
165 }
166
167 // It's important to make sure the hash function not change, other wise the
168 // existing indexed data will be messed up.
TEST(Frozen,HashCompatibility)169 TEST(Frozen, HashCompatibility) {
170 // int
171 std::hash<int64_t> intHash;
172 for (int64_t i = -10000; i < 10000; ++i) {
173 EXPECT_EQ(Layout<int64_t>::hash(i), intHash(i));
174 }
175
176 // string
177 using StrLayout = Layout<std::string>;
178 using View = StrLayout::View;
179
180 auto follyHash = [](const View& v) {
181 return folly::hash::fnv64_buf(v.begin(), v.size());
182 };
183
184 std::vector<std::string> strs{
185 "hello", "WOrld", "luckylook", "facebook", "Let it go!!"};
186 for (auto&& s : strs) {
187 View v(s);
188 EXPECT_EQ(StrLayout::hash(v), follyHash(v));
189 }
190 }
191
TEST(Frozen,EmbeddedSchema)192 TEST(Frozen, EmbeddedSchema) {
193 std::string storage;
194 {
195 schema::Schema schema;
196 schema::MemorySchema memSchema;
197
198 Layout<Person1> person1a;
199
200 size_t size;
201 size = LayoutRoot::layout(tom1, person1a);
202 saveRoot(person1a, memSchema);
203
204 schema::convert(memSchema, schema);
205
206 CompactSerializer::serialize(schema, &storage);
207 size_t start = storage.size();
208 storage.resize(size + storage.size());
209
210 folly::MutableStringPiece charRange(&storage[start], size);
211 folly::MutableByteRange bytes(charRange);
212 ByteRangeFreezer::freeze(person1a, tom1, bytes);
213 }
214 {
215 schema::Schema schema;
216 schema::MemorySchema memSchema;
217 Layout<Person2> person2;
218
219 size_t start = CompactSerializer::deserialize(storage, schema);
220
221 schema::convert(std::move(schema), memSchema);
222
223 loadRoot(person2, memSchema);
224
225 folly::StringPiece charRange(&storage[start], storage.size() - start);
226 folly::ByteRange bytes(charRange);
227 auto view = person2.view({bytes.begin(), 0});
228 EXPECT_EQ(*tom1.name_ref(), view.name());
229 ASSERT_EQ(tom1.age_ref().has_value(), view.age().has_value());
230 if (auto age = tom1.age_ref()) {
231 EXPECT_EQ(*age, view.age().value());
232 }
233 EXPECT_EQ(*tom1.pets_ref()[0].name_ref(), view.pets()[0].name());
234 EXPECT_EQ(*tom1.pets_ref()[1].name_ref(), view.pets()[1].name());
235 }
236 }
237
TEST(Frozen,NoLayout)238 TEST(Frozen, NoLayout) {
239 ViewPosition null{nullptr, 0};
240
241 EXPECT_FALSE(Layout<bool>().view(null));
242 EXPECT_EQ(0, Layout<int>().view(null));
243 EXPECT_EQ(0.0f, Layout<float>().view(null));
244 EXPECT_EQ(
245 apache::thrift::frozen::OptionalFieldView<int>(),
246 Layout<folly::Optional<int>>().view(null));
247 EXPECT_EQ(std::string(), Layout<std::string>().view(null));
248 EXPECT_EQ(std::vector<int>(), Layout<std::vector<int>>().view(null).thaw());
249 EXPECT_EQ(Person1(), Layout<Person1>().view(null).thaw());
250 EXPECT_EQ(Pet1(), Layout<Pet1>().view(null).thaw());
251 EXPECT_EQ(std::set<int>(), Layout<std::set<int>>().view(null).thaw());
252 EXPECT_EQ(
253 (std::map<int, int>()), (Layout<std::map<int, int>>().view(null).thaw()));
254
255 Layout<Person1> emptyPersonLayout;
256 std::array<uint8_t, 100> storage;
257 folly::MutableByteRange bytes(storage.begin(), storage.end());
258 EXPECT_THROW(
259 ByteRangeFreezer::freeze(emptyPersonLayout, tom1, bytes),
260 LayoutException);
261 }
262
263 template <class T>
testMaxLayout(const T & value)264 void testMaxLayout(const T& value) {
265 auto minLayout = Layout<T>();
266 auto valLayout = minLayout;
267 auto maxLayout = maximumLayout<T>();
268 LayoutRoot::layout(value, valLayout);
269 EXPECT_GT(valLayout.size, 0);
270 ASSERT_GT(maxLayout.size, 0);
271 std::array<uint8_t, 1000> storage;
272 folly::MutableByteRange bytes(storage.begin(), storage.end());
273 EXPECT_THROW(
274 ByteRangeFreezer::freeze(minLayout, value, bytes), LayoutException);
275 auto f = ByteRangeFreezer::freeze(maxLayout, value, bytes);
276 auto check = f.thaw();
277 EXPECT_EQ(value, check);
278 }
279
TEST(Frozen,MaxLayoutVector)280 TEST(Frozen, MaxLayoutVector) {
281 testMaxLayout(std::vector<int>{99, 24});
282 }
283
TEST(Frozen,MaxLayoutPairTree)284 TEST(Frozen, MaxLayoutPairTree) {
285 using std::make_pair;
286 auto p1 = make_pair(5, 2.3);
287 auto p2 = make_pair(4, p1);
288 auto p3 = make_pair(3, p2);
289 auto p4 = make_pair(2, p3);
290 auto p5 = make_pair(1, p4);
291 auto p6 = make_pair(0, p5);
292 testMaxLayout(p6);
293 }
294
TEST(Frozen,MaxLayoutStress)295 TEST(Frozen, MaxLayoutStress) {
296 testMaxLayout(stressValue2);
297 }
298
TEST(Frozen,String)299 TEST(Frozen, String) {
300 std::string str = "Hello";
301 auto fstr = freeze(str);
302 EXPECT_EQ(str, folly::StringPiece(fstr));
303 EXPECT_EQ(std::string(), folly::StringPiece(freeze(std::string())));
304 }
305
TEST(Frozen,VectorString)306 TEST(Frozen, VectorString) {
307 std::vector<std::string> strs{"hello", "sara"};
308 auto fstrs = freeze(strs);
309 EXPECT_EQ(strs[0], fstrs[0]);
310 EXPECT_EQ(strs[1], fstrs[1]);
311 EXPECT_EQ(strs.size(), fstrs.size());
312 std::vector<std::string> check;
313 }
314
TEST(Frozen,BigMap)315 TEST(Frozen, BigMap) {
316 PlaceTest t;
317 for (int i = 0; i < 1000; ++i) {
318 auto& place = t.places_ref()[i * i * i % 757368944];
319 *place.name_ref() = folly::to<std::string>(i);
320 for (int j = 0; j < 200; ++j) {
321 ++place.popularityByHour_ref()[rand() % (24 * 7)];
322 }
323 }
324 folly::IOBufQueue bq(folly::IOBufQueue::cacheChainLength());
325 CompactSerializer::serialize(t, &bq);
326 auto compactSize = bq.chainLength();
327 auto frozenSize = ::frozenSize(t);
328 EXPECT_EQ(t, freeze(t).thaw());
329 EXPECT_LT(frozenSize, compactSize * 0.7);
330 }
__anon45252c020602null331 Tiny tiny1 = [] {
332 Tiny obj;
333 *obj.a_ref() = "just a";
334 return obj;
335 }();
__anon45252c020702null336 Tiny tiny2 = [] {
337 Tiny obj;
338 *obj.a_ref() = "two";
339 *obj.b_ref() = "set";
340 return obj;
341 }();
__anon45252c020802null342 Tiny tiny4 = [] {
343 Tiny obj;
344 *obj.a_ref() = "four";
345 *obj.b_ref() = "itty";
346 *obj.c_ref() = "bitty";
347 *obj.d_ref() = "strings";
348 return obj;
349 }();
350
TEST(Frozen,Tiny)351 TEST(Frozen, Tiny) {
352 EXPECT_EQ(tiny4, freeze(tiny4).thaw());
353 EXPECT_EQ(24, frozenSize(tiny4));
354 }
355
TEST(Frozen,SchemaSaving)356 TEST(Frozen, SchemaSaving) {
357 // calculate a layout
358 Layout<EveryLayout> stressLayoutCalculated;
359 CHECK(LayoutRoot::layout(stressValue2, stressLayoutCalculated));
360
361 // save it
362 schema::MemorySchema schemaSaved;
363 saveRoot(stressLayoutCalculated, schemaSaved);
364
365 // reload it
366 Layout<EveryLayout> stressLayoutLoaded;
367 loadRoot(stressLayoutLoaded, schemaSaved);
368
369 // make sure the two layouts are identical (via printing)
370 EXPECT_PRINTED_EQ(stressLayoutCalculated, stressLayoutLoaded);
371
372 // make sure layouts round-trip
373 schema::MemorySchema schemaLoaded;
374 saveRoot(stressLayoutLoaded, schemaLoaded);
375 EXPECT_EQ(schemaSaved, schemaLoaded);
376 }
377
TEST(Frozen,Enum)378 TEST(Frozen, Enum) {
379 Person1 he, she;
380 *he.gender_ref() = Gender::Male;
381 *she.gender_ref() = Gender::Female;
382 EXPECT_EQ(he, freeze(he).thaw());
383 EXPECT_EQ(she, freeze(she).thaw());
384 }
385
TEST(Frozen,EnumAsKey)386 TEST(Frozen, EnumAsKey) {
387 EnumAsKeyTest thriftObj;
388 thriftObj.enumSet_ref()->insert(Gender::Male);
389 thriftObj.enumMap_ref()->emplace(Gender::Female, 1219);
390 thriftObj.outsideEnumSet_ref()->insert(Animal::DOG);
391 thriftObj.outsideEnumMap_ref()->emplace(Animal::CAT, 7779);
392
393 auto frozenObj = freeze(thriftObj);
394 EXPECT_THAT(frozenObj.enumSet(), Contains(Gender::Male));
395 EXPECT_THAT(frozenObj.outsideEnumSet(), Contains(Animal::DOG));
396 EXPECT_EQ(frozenObj.enumMap().at(Gender::Female), 1219);
397 EXPECT_EQ(frozenObj.outsideEnumMap().at(Animal::CAT), 7779);
398 }
399
400 template <class T>
frozenBits(const T & value)401 size_t frozenBits(const T& value) {
402 Layout<T> layout;
403 LayoutRoot::layout(value, layout);
404 return layout.bits;
405 }
406
TEST(Frozen,Bool)407 TEST(Frozen, Bool) {
408 Pet1 meat, vegan, dunno;
409 meat.vegan_ref() = false;
410 vegan.vegan_ref() = true;
411 // Always-empty optionals take 0 bits.
412 // Sometimes-full optionals take >=1 bits.
413 // Always-false bools take 0 bits.
414 // Sometimes-true bools take 1 bits.
415 // dunno => Nothing => 0 bits.
416 // meat => Just(False) => 1 bit.
417 // vegan => Just(True) => 2 bits.
418 EXPECT_LT(frozenBits(dunno), frozenBits(meat));
419 EXPECT_LT(frozenBits(meat), frozenBits(vegan));
420 EXPECT_FALSE(*freeze(meat).vegan());
421 EXPECT_TRUE(*freeze(vegan).vegan());
422 EXPECT_FALSE(freeze(dunno).vegan().has_value());
423 EXPECT_EQ(meat, freeze(meat).thaw());
424 EXPECT_EQ(vegan, freeze(vegan).thaw());
425 EXPECT_EQ(dunno, freeze(dunno).thaw());
426 }
427
TEST(Frozen,ThawPart)428 TEST(Frozen, ThawPart) {
429 auto f = freeze(tom1);
430 EXPECT_EQ(f.pets()[0].name(), "max");
431 EXPECT_EQ(f.pets()[1].name(), "ed");
432
433 auto max = f.pets()[0].thaw();
434 auto ed = f.pets()[1].thaw();
435 EXPECT_EQ(typeid(max), typeid(Pet1));
436 EXPECT_EQ(typeid(ed), typeid(Pet1));
437 EXPECT_EQ(*max.name_ref(), "max");
438 EXPECT_EQ(*ed.name_ref(), "ed");
439 }
440
TEST(Frozen,SchemaConversion)441 TEST(Frozen, SchemaConversion) {
442 schema::MemorySchema memSchema;
443 schema::Schema schema;
444
445 Layout<EveryLayout> stressLayoutCalculated;
446 CHECK(LayoutRoot::layout(stressValue2, stressLayoutCalculated));
447
448 schema::MemorySchema schemaSaved;
449 saveRoot(stressLayoutCalculated, schemaSaved);
450
451 schema::convert(schemaSaved, schema);
452 schema::convert(std::move(schema), memSchema);
453
454 EXPECT_EQ(memSchema, schemaSaved);
455 }
456
TEST(Frozen,SparseSchema)457 TEST(Frozen, SparseSchema) {
458 {
459 auto l = layout(tiny1);
460 schema::MemorySchema schema;
461 saveRoot(l, schema);
462 EXPECT_LE(schema.getLayouts().size(), 4);
463 }
464 {
465 auto l = layout(tiny2);
466 schema::MemorySchema schema;
467 saveRoot(l, schema);
468 EXPECT_LE(schema.getLayouts().size(), 7);
469 }
470 }
471
TEST(Frozen,DedupedSchema)472 TEST(Frozen, DedupedSchema) {
473 {
474 auto l = layout(tiny4);
475 schema::MemorySchema schema;
476 saveRoot(l, schema);
477 EXPECT_LE(schema.getLayouts().size(), 7); // 13 layouts originally
478 }
479 {
480 auto l = layout(stressValue2);
481 schema::MemorySchema schema;
482 saveRoot(l, schema);
483 EXPECT_LE(schema.getLayouts().size(), 24); // 49 layouts originally
484 }
485 }
486
TEST(Frozen,TypeHelpers)487 TEST(Frozen, TypeHelpers) {
488 auto f = freeze(tom1);
489 View<Pet1> m = f.pets()[0];
490 EXPECT_EQ(m.name(), "max");
491 }
492
TEST(Frozen,RangeTrivialRange)493 TEST(Frozen, RangeTrivialRange) {
494 auto data = std::vector<float>{3.0, 4.0, 5.0};
495 auto view = freeze(data);
496 auto r = folly::Range<const float*>(view.range());
497 EXPECT_EQ(data, std::vector<float>(r.begin(), r.end()));
498 }
499
TEST(Frozen,PaddingLayout)500 TEST(Frozen, PaddingLayout) {
501 using std::pair;
502 using std::vector;
503 // The 'distance' field of the vector<double> is small and sensitive to
504 // padding adjustments. If actual distances are returned in
505 // layoutBytesDistance instead of worst-case distances, the below structure
506 // will successfully freeze at offset zero but fail at later offsets.
507 vector<vector<vector<double>>> test(10);
508 test.push_back({{1.0}});
509 size_t size;
510 auto testLayout = layout(test, size);
511 for (size_t offset = 0; offset < 8; ++offset) {
512 std::unique_ptr<byte[]> store(new byte[size + offset + 16]);
513 folly::MutableByteRange bytes(store.get() + offset, size + 16);
514
515 auto view = ByteRangeFreezer::freeze(testLayout, test, bytes);
516 auto range = view[10][0].range();
517 EXPECT_EQ(range[0], 1.0);
518 EXPECT_EQ(reinterpret_cast<intptr_t>(range.begin()) % alignof(double), 0);
519 }
520 }
521
TEST(Frozen,Bundled)522 TEST(Frozen, Bundled) {
523 using String = Bundled<std::string>;
524 String s("Hello");
525
526 EXPECT_EQ("Hello", s);
527 EXPECT_FALSE(s.empty());
528 EXPECT_EQ(nullptr, s.findFirstOfType<int>());
529
530 s.hold(47);
531 s.hold(11);
532
533 EXPECT_EQ(47, *s.findFirstOfType<int>());
534 EXPECT_EQ(nullptr, s.findFirstOfType<std::string>());
535 }
536
TEST(Frozen,TrivialCopyable)537 TEST(Frozen, TrivialCopyable) {
538 TriviallyCopyableStruct s;
539 s.field_ref() = 42;
540 auto view = freeze(s);
541 EXPECT_EQ(view.field(), 42);
542 }
543
544 MATCHER(PairStrEq, "") {
545 *result_listener << std::get<0>(arg).first << ", " << std::get<0>(arg).second
546 << ", vs " << std::get<1>(arg).first << ", "
547 << std::get<1>(arg).second;
548 return std::get<0>(arg).first == std::get<1>(arg).first &&
549 std::get<0>(arg).second == std::get<1>(arg).second;
550 }
551
TEST(Frozen,FixedSizeString)552 TEST(Frozen, FixedSizeString) {
553 // Good example.
554 {
555 TestFixedSizeString s;
556 s.bytes8_ref() = "01234567";
557 // bytes4 field is optional and can be unset.
558 auto view = freeze(s);
559 ASSERT_TRUE(view.bytes8_ref().has_value());
560 EXPECT_EQ(view.bytes8_ref()->toString(), "01234567");
561 }
562 // Good example.
563 {
564 TestFixedSizeString s;
565 s.bytes8_ref() = "01234567";
566 s.bytes4_ref() = "0123";
567 auto view = freeze(s);
568 ASSERT_TRUE(view.bytes8_ref().has_value());
569 EXPECT_EQ(view.bytes8_ref()->toString(), "01234567");
570 ASSERT_TRUE(view.bytes4().has_value());
571 EXPECT_EQ(view.bytes4()->toString(), "0123");
572 }
573 // Throws if an unqualified FixedSizeString field is unset.
574 {
575 TestFixedSizeString s;
576 s.bytes4_ref() = "0123";
577 // bytes8 field is unqualified and must be set.
578 EXPECT_THROW(
579 [&s]() { auto view = freeze(s); }(),
580 apache::thrift::frozen::detail::FixedSizeMismatchException);
581 }
582 // Throws if a FixedSizeString field doesn't have the expected size.
583 {
584 EXPECT_THROW(
585 []() {
586 TestFixedSizeString s;
587 s.bytes8_ref() = "01234567";
588 s.bytes4_ref() = "0";
589 auto view = freeze(s);
590 }(),
591 apache::thrift::frozen::detail::FixedSizeMismatchException);
592 EXPECT_THROW(
593 []() {
594 TestFixedSizeString s;
595 s.bytes8_ref() = "0";
596 s.bytes4_ref() = "0123";
597 auto view = freeze(s);
598 }(),
599 apache::thrift::frozen::detail::FixedSizeMismatchException);
600 }
601 // Tests FixedSizeString as both the key_type and the value_type in a hashmap.
602 {
603 TestFixedSizeString s;
604 s.bytes8_ref() = "01234567";
605 std::unordered_map<std::string, std::string> rawMap = {
606 {"76543210", "ab"},
607 {"12345670", "cd"},
608 {"hellosnw", "ef"},
609 {"facebook", "hi"},
610 };
611 for (const auto& [key, val] : rawMap) {
612 s.aMapToFreeze_ref()[apache::thrift::frozen::FixedSizeString<8>(key)] =
613 val;
614 s.aMap_ref()[apache::thrift::frozen::FixedSizeString<8>(key)] = val;
615 }
616
617 // Tests aMap before serialization.
618 {
619 EXPECT_THAT(
620 *s.aMap_ref(), testing::UnorderedPointwise(PairStrEq(), rawMap));
621 auto iter = s.aMap_ref()->find(Fixed8("hellosnw"));
622 ASSERT_NE(iter, s.aMap_ref()->end());
623 EXPECT_THAT(
624 *iter,
625 testing::Pair(
626 testing::Eq(Fixed8("hellosnw")), testing::Eq(Fixed2("ef"))));
627 }
628
629 auto view = freeze(s);
630
631 auto extractToUnorderedMap = [](const auto& frozenMap) {
632 std::unordered_map<std::string, std::string> result;
633 for (auto iter = frozenMap.begin(); iter != frozenMap.end(); ++iter) {
634 result[iter->first().toString()] = iter->second().toString();
635 }
636 return result;
637 };
638
639 ASSERT_TRUE(view.aMap_ref().has_value());
640 EXPECT_THAT(
641 extractToUnorderedMap(*view.aMap_ref()),
642 testing::UnorderedElementsAreArray(rawMap.begin(), rawMap.end()));
643 ASSERT_TRUE(view.aMapToFreeze_ref().has_value());
644 EXPECT_THAT(
645 extractToUnorderedMap(*view.aMapToFreeze_ref()),
646 testing::UnorderedElementsAreArray(rawMap.begin(), rawMap.end()));
647
648 {
649 std::string key = "hellosnw";
650 auto iter = view.aMap_ref()->find(folly::ByteRange{
651 reinterpret_cast<const uint8_t*>(key.data()), key.size()});
652 ASSERT_NE(iter, view.aMap_ref()->end());
653 EXPECT_EQ(iter->first().toString(), "hellosnw");
654 EXPECT_EQ(iter->second().toString(), "ef");
655 }
656 }
657 }
658
TEST(Frozen,Empty)659 TEST(Frozen, Empty) {
660 Empty s;
661 auto view = freeze(s);
662 (void)view;
663 }
664
TEST(Frozen,Excluded)665 TEST(Frozen, Excluded) {
666 ContainsExcluded excludedUnset;
667 ContainsExcluded excludedSet;
668 excludedSet.excluded_ref().ensure();
669 (void)freeze(excludedUnset);
670 EXPECT_THROW(freeze(excludedSet), LayoutExcludedException);
671 }
672
673 } // namespace
674