1 #include "monster_test.h"
2
3 #include <limits>
4 #include <vector>
5
6 #include "flatbuffers/base.h"
7 #include "flatbuffers/flatbuffer_builder.h"
8 #include "flatbuffers/flatbuffers.h"
9 #include "flatbuffers/idl.h"
10 #include "flatbuffers/registry.h"
11 #include "flatbuffers/verifier.h"
12 #include "is_quiet_nan.h"
13 #include "monster_extra_generated.h"
14 #include "monster_test_generated.h"
15 #include "test_assert.h"
16
17 namespace flatbuffers {
18 namespace tests {
19
20 // Shortcuts for the infinity.
21 static const auto infinity_f = std::numeric_limits<float>::infinity();
22 static const auto infinity_d = std::numeric_limits<double>::infinity();
23
24 using namespace MyGame::Example;
25
26 // example of how to build up a serialized buffer algorithmically:
CreateFlatBufferTest(std::string & buffer)27 flatbuffers::DetachedBuffer CreateFlatBufferTest(std::string &buffer) {
28 flatbuffers::FlatBufferBuilder builder;
29
30 auto vec = Vec3(1, 2, 3, 0, Color_Red, Test(10, 20));
31
32 auto name = builder.CreateString("MyMonster");
33
34 // Use the initializer_list specialization of CreateVector.
35 auto inventory =
36 builder.CreateVector<uint8_t>({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
37
38 // Alternatively, create the vector first, and fill in data later:
39 // unsigned char *inv_buf = nullptr;
40 // auto inventory = builder.CreateUninitializedVector<unsigned char>(
41 // 10, &inv_buf);
42 // memcpy(inv_buf, inv_data, 10);
43
44 Test tests[] = { Test(10, 20), Test(30, 40) };
45 auto testv = builder.CreateVectorOfStructs(tests, 2);
46
47 // Create a vector of structures from a lambda.
48 auto testv2 = builder.CreateVectorOfStructs<Test>(
49 2, [&](size_t i, Test *s) -> void { *s = tests[i]; });
50
51 // create monster with very few fields set:
52 // (same functionality as CreateMonster below, but sets fields manually)
53 flatbuffers::Offset<Monster> mlocs[3];
54 auto fred = builder.CreateString("Fred");
55 auto barney = builder.CreateString("Barney");
56 auto wilma = builder.CreateString("Wilma");
57 MonsterBuilder mb1(builder);
58 mb1.add_name(fred);
59 mlocs[0] = mb1.Finish();
60 MonsterBuilder mb2(builder);
61 mb2.add_name(barney);
62 mb2.add_hp(1000);
63 mlocs[1] = mb2.Finish();
64 MonsterBuilder mb3(builder);
65 mb3.add_name(wilma);
66 mlocs[2] = mb3.Finish();
67
68 // Create an array of strings. Also test string pooling, and lambdas.
69 auto vecofstrings =
70 builder.CreateVector<flatbuffers::Offset<flatbuffers::String>>(
71 4,
72 [](size_t i, flatbuffers::FlatBufferBuilder *b)
73 -> flatbuffers::Offset<flatbuffers::String> {
74 static const char *names[] = { "bob", "fred", "bob", "fred" };
75 return b->CreateSharedString(names[i]);
76 },
77 &builder);
78
79 // Creating vectors of strings in one convenient call.
80 std::vector<std::string> names2;
81 names2.push_back("jane");
82 names2.push_back("mary");
83 auto vecofstrings2 = builder.CreateVectorOfStrings(names2);
84
85 // Creating vectors from types that are different from std::string
86 std::vector<const char *> names3;
87 names3.push_back("foo");
88 names3.push_back("bar");
89 builder.CreateVectorOfStrings(names3); // Also an accepted type
90
91 #ifdef FLATBUFFERS_HAS_STRING_VIEW
92 std::vector<flatbuffers::string_view> names4;
93 names3.push_back("baz");
94 names3.push_back("quux");
95 builder.CreateVectorOfStrings(names4); // Also an accepted type
96 #endif
97
98 // Make sure the template deduces an initializer as std::vector<std::string>
99 builder.CreateVectorOfStrings({ "hello", "world" });
100
101 // Create many vectors of strings
102 std::vector<std::string> manyNames;
103 for (auto i = 0; i < 100; i++) { manyNames.push_back("john_doe"); }
104 auto manyNamesVec = builder.CreateVectorOfStrings(manyNames);
105 TEST_EQ(false, manyNamesVec.IsNull());
106 auto manyNamesVec2 =
107 builder.CreateVectorOfStrings(manyNames.cbegin(), manyNames.cend());
108 TEST_EQ(false, manyNamesVec2.IsNull());
109
110 // Create an array of sorted tables, can be used with binary search when read:
111 auto vecoftables = builder.CreateVectorOfSortedTables(mlocs, 3);
112
113 // Create an array of sorted structs,
114 // can be used with binary search when read:
115 std::vector<Ability> abilities;
116 abilities.push_back(Ability(4, 40));
117 abilities.push_back(Ability(3, 30));
118 abilities.push_back(Ability(2, 20));
119 abilities.push_back(Ability(0, 0));
120 auto vecofstructs = builder.CreateVectorOfSortedStructs(&abilities);
121
122 flatbuffers::Offset<Stat> mlocs_stats[1];
123 auto miss = builder.CreateString("miss");
124 StatBuilder mb_miss(builder);
125 mb_miss.add_id(miss);
126 mb_miss.add_val(0);
127 mb_miss.add_count(0); // key
128 mlocs_stats[0] = mb_miss.Finish();
129 auto vec_of_stats = builder.CreateVectorOfSortedTables(mlocs_stats, 1);
130
131 // Create a nested FlatBuffer.
132 // Nested FlatBuffers are stored in a ubyte vector, which can be convenient
133 // since they can be memcpy'd around much easier than other FlatBuffer
134 // values. They have little overhead compared to storing the table directly.
135 // As a test, create a mostly empty Monster buffer:
136 flatbuffers::FlatBufferBuilder nested_builder;
137 auto nmloc = CreateMonster(nested_builder, nullptr, 0, 0,
138 nested_builder.CreateString("NestedMonster"));
139 FinishMonsterBuffer(nested_builder, nmloc);
140 // Now we can store the buffer in the parent. Note that by default, vectors
141 // are only aligned to their elements or size field, so in this case if the
142 // buffer contains 64-bit elements, they may not be correctly aligned. We fix
143 // that with:
144 builder.ForceVectorAlignment(nested_builder.GetSize(), sizeof(uint8_t),
145 nested_builder.GetBufferMinAlignment());
146 // If for whatever reason you don't have the nested_builder available, you
147 // can substitute flatbuffers::largest_scalar_t (64-bit) for the alignment, or
148 // the largest force_align value in your schema if you're using it.
149 auto nested_flatbuffer_vector = builder.CreateVector(
150 nested_builder.GetBufferPointer(), nested_builder.GetSize());
151
152 // Test a nested FlexBuffer:
153 flexbuffers::Builder flexbuild;
154 flexbuild.Int(1234);
155 flexbuild.Finish();
156 auto flex = builder.CreateVector(flexbuild.GetBuffer());
157 // Test vector of enums.
158 Color colors[] = { Color_Blue, Color_Green };
159 // We use this special creation function because we have an array of
160 // pre-C++11 (enum class) enums whose size likely is int, yet its declared
161 // type in the schema is byte.
162 auto vecofcolors = builder.CreateVectorScalarCast<uint8_t, Color>(colors, 2);
163
164 // shortcut for creating monster with all fields set:
165 auto mloc = CreateMonster(
166 builder, &vec, 150, 80, name, inventory, Color_Blue, Any_Monster,
167 mlocs[1].Union(), // Store a union.
168 testv, vecofstrings, vecoftables, 0, nested_flatbuffer_vector, 0, false,
169 0, 0, 0, 0, 0, 0, 0, 0, 0, 3.14159f, 3.0f, 0.0f, vecofstrings2,
170 vecofstructs, flex, testv2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 AnyUniqueAliases_NONE, 0, AnyAmbiguousAliases_NONE, 0, vecofcolors,
172 MyGame::Example::Race_None, 0, vec_of_stats);
173
174 FinishMonsterBuffer(builder, mloc);
175
176 // clang-format off
177 #ifdef FLATBUFFERS_TEST_VERBOSE
178 // print byte data for debugging:
179 auto p = builder.GetBufferPointer();
180 for (flatbuffers::uoffset_t i = 0; i < builder.GetSize(); i++)
181 printf("%d ", p[i]);
182 #endif
183 // clang-format on
184
185 // return the buffer for the caller to use.
186 auto bufferpointer =
187 reinterpret_cast<const char *>(builder.GetBufferPointer());
188 buffer.assign(bufferpointer, bufferpointer + builder.GetSize());
189
190 return builder.Release();
191 }
192
193 // example of accessing a buffer loaded in memory:
AccessFlatBufferTest(const uint8_t * flatbuf,size_t length,bool pooled)194 void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length, bool pooled) {
195 // First, verify the buffers integrity (optional)
196 flatbuffers::Verifier verifier(flatbuf, length);
197 std::vector<uint8_t> flex_reuse_tracker;
198 verifier.SetFlexReuseTracker(&flex_reuse_tracker);
199 TEST_EQ(VerifyMonsterBuffer(verifier), true);
200
201 // clang-format off
202 #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
203 std::vector<uint8_t> test_buff;
204 test_buff.resize(length * 2);
205 std::memcpy(&test_buff[0], flatbuf, length);
206 std::memcpy(&test_buff[length], flatbuf, length);
207
208 flatbuffers::Verifier verifier1(&test_buff[0], length);
209 TEST_EQ(VerifyMonsterBuffer(verifier1), true);
210 TEST_EQ(verifier1.GetComputedSize(), length);
211
212 flatbuffers::Verifier verifier2(&test_buff[length], length);
213 TEST_EQ(VerifyMonsterBuffer(verifier2), true);
214 TEST_EQ(verifier2.GetComputedSize(), length);
215 #endif
216 // clang-format on
217
218 TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0);
219 TEST_EQ(MonsterBufferHasIdentifier(flatbuf), true);
220 TEST_EQ(strcmp(MonsterExtension(), "mon"), 0);
221
222 // Access the buffer from the root.
223 auto monster = GetMonster(flatbuf);
224
225 TEST_EQ(monster->hp(), 80);
226 TEST_EQ(monster->mana(), 150); // default
227 TEST_EQ_STR(monster->name()->c_str(), "MyMonster");
228 // Can't access the following field, it is deprecated in the schema,
229 // which means accessors are not generated:
230 // monster.friendly()
231
232 auto pos = monster->pos();
233 TEST_NOTNULL(pos);
234 TEST_EQ(pos->z(), 3);
235 TEST_EQ(pos->test3().a(), 10);
236 TEST_EQ(pos->test3().b(), 20);
237
238 auto inventory = monster->inventory();
239 TEST_EQ(VectorLength(inventory), 10UL); // Works even if inventory is null.
240 TEST_NOTNULL(inventory);
241 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
242 // Check compatibilty of iterators with STL.
243 std::vector<unsigned char> inv_vec(inventory->begin(), inventory->end());
244 size_t n = 0;
245 for (auto it = inventory->begin(); it != inventory->end(); ++it, ++n) {
246 auto indx = it - inventory->begin();
247 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
248 TEST_EQ(*it, inv_data[indx]);
249 }
250 TEST_EQ(n, inv_vec.size());
251
252 n = 0;
253 for (auto it = inventory->cbegin(); it != inventory->cend(); ++it, ++n) {
254 auto indx = it - inventory->cbegin();
255 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
256 TEST_EQ(*it, inv_data[indx]);
257 }
258 TEST_EQ(n, inv_vec.size());
259
260 n = 0;
261 for (auto it = inventory->rbegin(); it != inventory->rend(); ++it, ++n) {
262 auto indx = inventory->rend() - it - 1;
263 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
264 TEST_EQ(*it, inv_data[indx]);
265 }
266 TEST_EQ(n, inv_vec.size());
267
268 n = 0;
269 for (auto it = inventory->crbegin(); it != inventory->crend(); ++it, ++n) {
270 auto indx = inventory->crend() - it - 1;
271 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
272 TEST_EQ(*it, inv_data[indx]);
273 }
274 TEST_EQ(n, inv_vec.size());
275
276 TEST_EQ(monster->color(), Color_Blue);
277
278 // Example of accessing a union:
279 TEST_EQ(monster->test_type(), Any_Monster); // First make sure which it is.
280 auto monster2 = reinterpret_cast<const Monster *>(monster->test());
281 TEST_NOTNULL(monster2);
282 TEST_EQ_STR(monster2->name()->c_str(), "Fred");
283
284 // Example of accessing a vector of strings:
285 auto vecofstrings = monster->testarrayofstring();
286 TEST_EQ(vecofstrings->size(), 4U);
287 TEST_EQ_STR(vecofstrings->Get(0)->c_str(), "bob");
288 TEST_EQ_STR(vecofstrings->Get(1)->c_str(), "fred");
289 if (pooled) {
290 // These should have pointer equality because of string pooling.
291 TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str());
292 TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str());
293 }
294
295 auto vecofstrings2 = monster->testarrayofstring2();
296 if (vecofstrings2) {
297 TEST_EQ(vecofstrings2->size(), 2U);
298 TEST_EQ_STR(vecofstrings2->Get(0)->c_str(), "jane");
299 TEST_EQ_STR(vecofstrings2->Get(1)->c_str(), "mary");
300 }
301
302 // Example of accessing a vector of tables:
303 auto vecoftables = monster->testarrayoftables();
304 TEST_EQ(vecoftables->size(), 3U);
305 for (auto it = vecoftables->begin(); it != vecoftables->end(); ++it) {
306 TEST_EQ(strlen(it->name()->c_str()) >= 4, true);
307 }
308 TEST_EQ_STR(vecoftables->Get(0)->name()->c_str(), "Barney");
309 TEST_EQ(vecoftables->Get(0)->hp(), 1000);
310 TEST_EQ_STR(vecoftables->Get(1)->name()->c_str(), "Fred");
311 TEST_EQ_STR(vecoftables->Get(2)->name()->c_str(), "Wilma");
312 TEST_NOTNULL(vecoftables->LookupByKey("Barney"));
313 TEST_NOTNULL(vecoftables->LookupByKey("Fred"));
314 TEST_NOTNULL(vecoftables->LookupByKey("Wilma"));
315
316 // Verify the same objects are returned for char*-based and string-based
317 // lookups.
318 TEST_EQ(vecoftables->LookupByKey("Barney"),
319 vecoftables->LookupByKey(std::string("Barney")));
320 TEST_EQ(vecoftables->LookupByKey("Fred"),
321 vecoftables->LookupByKey(std::string("Fred")));
322 TEST_EQ(vecoftables->LookupByKey("Wilma"),
323 vecoftables->LookupByKey(std::string("Wilma")));
324
325 #ifdef FLATBUFFERS_HAS_STRING_VIEW
326 // Tests for LookupByKey with a key that is a truncated
327 // version of a longer, invalid key.
328 const std::string invalid_key = "Barney123";
329 std::string_view valid_truncated_key = invalid_key;
330 valid_truncated_key.remove_suffix(3); // "Barney"
331 TEST_NOTNULL(vecoftables->LookupByKey(valid_truncated_key));
332 TEST_EQ(vecoftables->LookupByKey("Barney"),
333 vecoftables->LookupByKey(valid_truncated_key));
334
335 // Tests for LookupByKey with a key that is a truncated
336 // version of a longer, valid key.
337 const std::string valid_key = "Barney";
338 std::string_view invalid_truncated_key = valid_key;
339 invalid_truncated_key.remove_suffix(3); // "Bar"
340 TEST_NULL(vecoftables->LookupByKey(invalid_truncated_key));
341 #endif // FLATBUFFERS_HAS_STRING_VIEW
342
343 // Test accessing a vector of sorted structs
344 auto vecofstructs = monster->testarrayofsortedstruct();
345 if (vecofstructs) { // not filled in monster_test.bfbs
346 for (flatbuffers::uoffset_t i = 0; i < vecofstructs->size() - 1; i++) {
347 auto left = vecofstructs->Get(i);
348 auto right = vecofstructs->Get(i + 1);
349 TEST_EQ(true, (left->KeyCompareLessThan(right)));
350 }
351 TEST_NOTNULL(vecofstructs->LookupByKey(0)); // test default value
352 TEST_NOTNULL(vecofstructs->LookupByKey(3));
353 TEST_EQ(static_cast<const Ability *>(nullptr),
354 vecofstructs->LookupByKey(5));
355 }
356
357 if (auto vec_of_stat = monster->scalar_key_sorted_tables()) {
358 auto stat_0 = vec_of_stat->LookupByKey(static_cast<uint16_t>(0u));
359 TEST_NOTNULL(stat_0);
360 TEST_NOTNULL(stat_0->id());
361 TEST_EQ(0, stat_0->count());
362 TEST_EQ_STR("miss", stat_0->id()->c_str());
363 }
364
365 // Test nested FlatBuffers if available:
366 auto nested_buffer = monster->testnestedflatbuffer();
367 if (nested_buffer) {
368 // nested_buffer is a vector of bytes you can memcpy. However, if you
369 // actually want to access the nested data, this is a convenient
370 // accessor that directly gives you the root table:
371 auto nested_monster = monster->testnestedflatbuffer_nested_root();
372 TEST_EQ_STR(nested_monster->name()->c_str(), "NestedMonster");
373 }
374
375 // Test flexbuffer if available:
376 auto flex = monster->flex();
377 // flex is a vector of bytes you can memcpy etc.
378 TEST_EQ(flex->size(), 4); // Encoded FlexBuffer bytes.
379 // However, if you actually want to access the nested data, this is a
380 // convenient accessor that directly gives you the root value:
381 TEST_EQ(monster->flex_flexbuffer_root().AsInt16(), 1234);
382
383 // Test vector of enums:
384 auto colors = monster->vector_of_enums();
385 if (colors) {
386 TEST_EQ(colors->size(), 2);
387 TEST_EQ(colors->Get(0), Color_Blue);
388 TEST_EQ(colors->Get(1), Color_Green);
389 }
390
391 // Since Flatbuffers uses explicit mechanisms to override the default
392 // compiler alignment, double check that the compiler indeed obeys them:
393 // (Test consists of a short and byte):
394 TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
395 TEST_EQ(sizeof(Test), 4UL);
396
397 const flatbuffers::Vector<const Test *> *tests_array[] = {
398 monster->test4(),
399 monster->test5(),
400 };
401 for (size_t i = 0; i < sizeof(tests_array) / sizeof(tests_array[0]); ++i) {
402 auto tests = tests_array[i];
403 TEST_NOTNULL(tests);
404 auto test_0 = tests->Get(0);
405 auto test_1 = tests->Get(1);
406 TEST_EQ(test_0->a(), 10);
407 TEST_EQ(test_0->b(), 20);
408 TEST_EQ(test_1->a(), 30);
409 TEST_EQ(test_1->b(), 40);
410 for (auto it = tests->begin(); it != tests->end(); ++it) {
411 TEST_EQ(it->a() == 10 || it->a() == 30, true); // Just testing iterators.
412 }
413 }
414
415 // Checking for presence of fields:
416 TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_HP), true);
417 TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_MANA), false);
418
419 // Obtaining a buffer from a root:
420 TEST_EQ(GetBufferStartFromRootPointer(monster), flatbuf);
421 }
422
423 // Change a FlatBuffer in-place, after it has been constructed.
MutateFlatBuffersTest(uint8_t * flatbuf,std::size_t length)424 void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
425 // Get non-const pointer to root.
426 auto monster = GetMutableMonster(flatbuf);
427
428 // Each of these tests mutates, then tests, then set back to the original,
429 // so we can test that the buffer in the end still passes our original test.
430 auto hp_ok = monster->mutate_hp(10);
431 TEST_EQ(hp_ok, true); // Field was present.
432 TEST_EQ(monster->hp(), 10);
433 // Mutate to default value
434 auto hp_ok_default = monster->mutate_hp(100);
435 TEST_EQ(hp_ok_default, true); // Field was present.
436 TEST_EQ(monster->hp(), 100);
437 // Test that mutate to default above keeps field valid for further mutations
438 auto hp_ok_2 = monster->mutate_hp(20);
439 TEST_EQ(hp_ok_2, true);
440 TEST_EQ(monster->hp(), 20);
441 monster->mutate_hp(80);
442
443 // Monster originally at 150 mana (default value)
444 auto mana_default_ok = monster->mutate_mana(150); // Mutate to default value.
445 TEST_EQ(mana_default_ok,
446 true); // Mutation should succeed, because default value.
447 TEST_EQ(monster->mana(), 150);
448 auto mana_ok = monster->mutate_mana(10);
449 TEST_EQ(mana_ok, false); // Field was NOT present, because default value.
450 TEST_EQ(monster->mana(), 150);
451
452 // Mutate structs.
453 auto pos = monster->mutable_pos();
454 auto &test3 = pos->mutable_test3(); // Struct inside a struct.
455 test3.mutate_a(50); // Struct fields never fail.
456 TEST_EQ(test3.a(), 50);
457 test3.mutate_a(10);
458
459 // Mutate vectors.
460 auto inventory = monster->mutable_inventory();
461 inventory->Mutate(9, 100);
462 TEST_EQ(inventory->Get(9), 100);
463 inventory->Mutate(9, 9);
464
465 auto tables = monster->mutable_testarrayoftables();
466 auto first = tables->GetMutableObject(0);
467 TEST_EQ(first->hp(), 1000);
468 first->mutate_hp(0);
469 TEST_EQ(first->hp(), 0);
470 first->mutate_hp(1000);
471
472 // Test for each loop over mutable entries
473 for (auto item : *tables) {
474 TEST_EQ(item->hp(), 1000);
475 item->mutate_hp(0);
476 TEST_EQ(item->hp(), 0);
477 item->mutate_hp(1000);
478 break; // one iteration is enough, just testing compilation
479 }
480
481 // Mutate via LookupByKey
482 TEST_NOTNULL(tables->MutableLookupByKey("Barney"));
483 TEST_EQ(static_cast<Monster *>(nullptr),
484 tables->MutableLookupByKey("DoesntExist"));
485 TEST_EQ(tables->MutableLookupByKey("Barney")->hp(), 1000);
486 TEST_EQ(tables->MutableLookupByKey("Barney")->mutate_hp(0), true);
487 TEST_EQ(tables->LookupByKey("Barney")->hp(), 0);
488 TEST_EQ(tables->MutableLookupByKey("Barney")->mutate_hp(1000), true);
489
490 // Run the verifier and the regular test to make sure we didn't trample on
491 // anything.
492 AccessFlatBufferTest(flatbuf, length);
493 }
494
495 // Unpack a FlatBuffer into objects.
ObjectFlatBuffersTest(uint8_t * flatbuf)496 void ObjectFlatBuffersTest(uint8_t *flatbuf) {
497 // Optional: we can specify resolver and rehasher functions to turn hashed
498 // strings into object pointers and back, to implement remote references
499 // and such.
500 auto resolver = flatbuffers::resolver_function_t(
501 [](void **pointer_adr, flatbuffers::hash_value_t hash) {
502 (void)pointer_adr;
503 (void)hash;
504 // Don't actually do anything, leave variable null.
505 });
506 auto rehasher = flatbuffers::rehasher_function_t(
507 [](void *pointer) -> flatbuffers::hash_value_t {
508 (void)pointer;
509 return 0;
510 });
511
512 // Turn a buffer into C++ objects.
513 auto monster1 = UnPackMonster(flatbuf, &resolver);
514
515 // Re-serialize the data.
516 flatbuffers::FlatBufferBuilder fbb1;
517 fbb1.Finish(CreateMonster(fbb1, monster1.get(), &rehasher),
518 MonsterIdentifier());
519
520 // Unpack again, and re-serialize again.
521 auto monster2 = UnPackMonster(fbb1.GetBufferPointer(), &resolver);
522 flatbuffers::FlatBufferBuilder fbb2;
523 fbb2.Finish(CreateMonster(fbb2, monster2.get(), &rehasher),
524 MonsterIdentifier());
525
526 // Now we've gone full round-trip, the two buffers should match.
527 const auto len1 = fbb1.GetSize();
528 const auto len2 = fbb2.GetSize();
529 TEST_EQ(len1, len2);
530 TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(), len1), 0);
531
532 // Test it with the original buffer test to make sure all data survived.
533 AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false);
534
535 // Test accessing fields, similar to AccessFlatBufferTest above.
536 CheckMonsterObject(monster2.get());
537
538 // Test object copy.
539 MonsterT monster3 = *monster2;
540 flatbuffers::FlatBufferBuilder fbb3;
541 fbb3.Finish(CreateMonster(fbb3, &monster3, &rehasher), MonsterIdentifier());
542 const auto len3 = fbb3.GetSize();
543 TEST_EQ(len2, len3);
544 TEST_EQ(memcmp(fbb2.GetBufferPointer(), fbb3.GetBufferPointer(), len2), 0);
545 // Delete monster1 and monster2, then test accessing fields in monster3.
546 monster1.reset();
547 monster2.reset();
548 CheckMonsterObject(&monster3);
549 }
550
551 // Utility function to check a Monster object.
CheckMonsterObject(MonsterT * monster2)552 void CheckMonsterObject(MonsterT *monster2) {
553 TEST_EQ(monster2->hp, 80);
554 TEST_EQ(monster2->mana, 150); // default
555 TEST_EQ_STR(monster2->name.c_str(), "MyMonster");
556
557 auto &pos = monster2->pos;
558 TEST_NOTNULL(pos);
559 TEST_EQ(pos->z(), 3);
560 TEST_EQ(pos->test3().a(), 10);
561 TEST_EQ(pos->test3().b(), 20);
562
563 auto &inventory = monster2->inventory;
564 TEST_EQ(inventory.size(), 10UL);
565 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
566 for (auto it = inventory.begin(); it != inventory.end(); ++it)
567 TEST_EQ(*it, inv_data[it - inventory.begin()]);
568
569 TEST_EQ(monster2->color, Color_Blue);
570
571 auto monster3 = monster2->test.AsMonster();
572 TEST_NOTNULL(monster3);
573 TEST_EQ_STR(monster3->name.c_str(), "Fred");
574
575 auto &vecofstrings = monster2->testarrayofstring;
576 TEST_EQ(vecofstrings.size(), 4U);
577 TEST_EQ_STR(vecofstrings[0].c_str(), "bob");
578 TEST_EQ_STR(vecofstrings[1].c_str(), "fred");
579
580 auto &vecofstrings2 = monster2->testarrayofstring2;
581 TEST_EQ(vecofstrings2.size(), 2U);
582 TEST_EQ_STR(vecofstrings2[0].c_str(), "jane");
583 TEST_EQ_STR(vecofstrings2[1].c_str(), "mary");
584
585 auto &vecoftables = monster2->testarrayoftables;
586 TEST_EQ(vecoftables.size(), 3U);
587 TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney");
588 TEST_EQ(vecoftables[0]->hp, 1000);
589 TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred");
590 TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma");
591
592 auto &tests = monster2->test4;
593 TEST_EQ(tests[0].a(), 10);
594 TEST_EQ(tests[0].b(), 20);
595 TEST_EQ(tests[1].a(), 30);
596 TEST_EQ(tests[1].b(), 40);
597 }
598
599 // Prefix a FlatBuffer with a size field.
SizePrefixedTest()600 void SizePrefixedTest() {
601 // Create size prefixed buffer.
602 flatbuffers::FlatBufferBuilder fbb;
603 FinishSizePrefixedMonsterBuffer(
604 fbb, CreateMonster(fbb, nullptr, 200, 300, fbb.CreateString("bob")));
605
606 // Verify it.
607 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
608 TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier), true);
609
610 // The prefixed size doesn't include itself, so substract the size of the
611 // prefix
612 TEST_EQ(GetPrefixedSize(fbb.GetBufferPointer()),
613 fbb.GetSize() - sizeof(uoffset_t));
614
615 // Getting the buffer length does include the prefix size, so it should be the
616 // full lenght.
617 TEST_EQ(GetSizePrefixedBufferLength(fbb.GetBufferPointer()), fbb.GetSize());
618
619 // Access it.
620 auto m = GetSizePrefixedMonster(fbb.GetBufferPointer());
621 TEST_EQ(m->mana(), 200);
622 TEST_EQ(m->hp(), 300);
623 TEST_EQ_STR(m->name()->c_str(), "bob");
624
625 {
626 // Verify that passing a larger size is OK, but not a smaller
627 flatbuffers::Verifier verifier_larger(fbb.GetBufferPointer(),
628 fbb.GetSize() + 10);
629 TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_larger), true);
630
631 flatbuffers::Verifier verifier_smaller(fbb.GetBufferPointer(),
632 fbb.GetSize() - 10);
633 TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_smaller), false);
634 }
635 }
636
TestMonsterExtraFloats(const std::string & tests_data_path)637 void TestMonsterExtraFloats(const std::string &tests_data_path) {
638 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
639 TEST_EQ(is_quiet_nan(1.0), false);
640 TEST_EQ(is_quiet_nan(infinity_d), false);
641 TEST_EQ(is_quiet_nan(-infinity_f), false);
642 TEST_EQ(is_quiet_nan(std::numeric_limits<float>::quiet_NaN()), true);
643 TEST_EQ(is_quiet_nan(std::numeric_limits<double>::quiet_NaN()), true);
644
645 using namespace flatbuffers;
646 using namespace MyGame;
647 // Load FlatBuffer schema (.fbs) from disk.
648 std::string schemafile;
649 TEST_EQ(LoadFile((tests_data_path + "monster_extra.fbs").c_str(), false,
650 &schemafile),
651 true);
652 // Parse schema first, so we can use it to parse the data after.
653 Parser parser;
654 auto include_test_path = ConCatPathFileName(tests_data_path, "include_test");
655 const char *include_directories[] = { tests_data_path.c_str(),
656 include_test_path.c_str(), nullptr };
657 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
658 // Create empty extra and store to json.
659 parser.opts.output_default_scalars_in_json = true;
660 parser.opts.output_enum_identifiers = true;
661 FlatBufferBuilder builder;
662 const auto def_root = MonsterExtraBuilder(builder).Finish();
663 FinishMonsterExtraBuffer(builder, def_root);
664 const auto def_obj = builder.GetBufferPointer();
665 const auto def_extra = GetMonsterExtra(def_obj);
666 TEST_NOTNULL(def_extra);
667 TEST_EQ(is_quiet_nan(def_extra->f0()), true);
668 TEST_EQ(is_quiet_nan(def_extra->f1()), true);
669 TEST_EQ(def_extra->f2(), +infinity_f);
670 TEST_EQ(def_extra->f3(), -infinity_f);
671 TEST_EQ(is_quiet_nan(def_extra->d0()), true);
672 TEST_EQ(is_quiet_nan(def_extra->d1()), true);
673 TEST_EQ(def_extra->d2(), +infinity_d);
674 TEST_EQ(def_extra->d3(), -infinity_d);
675 std::string jsongen;
676 auto result = GenText(parser, def_obj, &jsongen);
677 TEST_NULL(result);
678 // Check expected default values.
679 TEST_EQ(std::string::npos != jsongen.find("f0: nan"), true);
680 TEST_EQ(std::string::npos != jsongen.find("f1: nan"), true);
681 TEST_EQ(std::string::npos != jsongen.find("f2: inf"), true);
682 TEST_EQ(std::string::npos != jsongen.find("f3: -inf"), true);
683 TEST_EQ(std::string::npos != jsongen.find("d0: nan"), true);
684 TEST_EQ(std::string::npos != jsongen.find("d1: nan"), true);
685 TEST_EQ(std::string::npos != jsongen.find("d2: inf"), true);
686 TEST_EQ(std::string::npos != jsongen.find("d3: -inf"), true);
687 // Parse 'mosterdata_extra.json'.
688 const auto extra_base = tests_data_path + "monsterdata_extra";
689 jsongen = "";
690 TEST_EQ(LoadFile((extra_base + ".json").c_str(), false, &jsongen), true);
691 TEST_EQ(parser.Parse(jsongen.c_str()), true);
692 const auto test_file = parser.builder_.GetBufferPointer();
693 const auto test_size = parser.builder_.GetSize();
694 Verifier verifier(test_file, test_size);
695 TEST_ASSERT(VerifyMonsterExtraBuffer(verifier));
696 const auto extra = GetMonsterExtra(test_file);
697 TEST_NOTNULL(extra);
698 TEST_EQ(is_quiet_nan(extra->f0()), true);
699 TEST_EQ(is_quiet_nan(extra->f1()), true);
700 TEST_EQ(extra->f2(), +infinity_f);
701 TEST_EQ(extra->f3(), -infinity_f);
702 TEST_EQ(is_quiet_nan(extra->d0()), true);
703 TEST_EQ(extra->d1(), +infinity_d);
704 TEST_EQ(extra->d2(), -infinity_d);
705 TEST_EQ(is_quiet_nan(extra->d3()), true);
706 TEST_NOTNULL(extra->fvec());
707 TEST_EQ(extra->fvec()->size(), 4);
708 TEST_EQ(extra->fvec()->Get(0), 1.0f);
709 TEST_EQ(extra->fvec()->Get(1), -infinity_f);
710 TEST_EQ(extra->fvec()->Get(2), +infinity_f);
711 TEST_EQ(is_quiet_nan(extra->fvec()->Get(3)), true);
712 TEST_NOTNULL(extra->dvec());
713 TEST_EQ(extra->dvec()->size(), 4);
714 TEST_EQ(extra->dvec()->Get(0), 2.0);
715 TEST_EQ(extra->dvec()->Get(1), +infinity_d);
716 TEST_EQ(extra->dvec()->Get(2), -infinity_d);
717 TEST_EQ(is_quiet_nan(extra->dvec()->Get(3)), true);
718 #endif
719 }
720
EnumNamesTest()721 void EnumNamesTest() {
722 TEST_EQ_STR("Red", EnumNameColor(Color_Red));
723 TEST_EQ_STR("Green", EnumNameColor(Color_Green));
724 TEST_EQ_STR("Blue", EnumNameColor(Color_Blue));
725 // Check that Color to string don't crash while decode a mixture of Colors.
726 // 1) Example::Color enum is enum with unfixed underlying type.
727 // 2) Valid enum range: [0; 2^(ceil(log2(Color_ANY))) - 1].
728 // Consequence: A value is out of this range will lead to UB (since C++17).
729 // For details see C++17 standard or explanation on the SO:
730 // stackoverflow.com/questions/18195312/what-happens-if-you-static-cast-invalid-value-to-enum-class
731 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(0)));
732 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY - 1)));
733 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY + 1)));
734 }
735
TypeAliasesTest()736 void TypeAliasesTest() {
737 flatbuffers::FlatBufferBuilder builder;
738
739 builder.Finish(CreateTypeAliases(
740 builder, flatbuffers::numeric_limits<int8_t>::min(),
741 flatbuffers::numeric_limits<uint8_t>::max(),
742 flatbuffers::numeric_limits<int16_t>::min(),
743 flatbuffers::numeric_limits<uint16_t>::max(),
744 flatbuffers::numeric_limits<int32_t>::min(),
745 flatbuffers::numeric_limits<uint32_t>::max(),
746 flatbuffers::numeric_limits<int64_t>::min(),
747 flatbuffers::numeric_limits<uint64_t>::max(), 2.3f, 2.3));
748
749 auto p = builder.GetBufferPointer();
750 auto ta = flatbuffers::GetRoot<TypeAliases>(p);
751
752 TEST_EQ(ta->i8(), flatbuffers::numeric_limits<int8_t>::min());
753 TEST_EQ(ta->u8(), flatbuffers::numeric_limits<uint8_t>::max());
754 TEST_EQ(ta->i16(), flatbuffers::numeric_limits<int16_t>::min());
755 TEST_EQ(ta->u16(), flatbuffers::numeric_limits<uint16_t>::max());
756 TEST_EQ(ta->i32(), flatbuffers::numeric_limits<int32_t>::min());
757 TEST_EQ(ta->u32(), flatbuffers::numeric_limits<uint32_t>::max());
758 TEST_EQ(ta->i64(), flatbuffers::numeric_limits<int64_t>::min());
759 TEST_EQ(ta->u64(), flatbuffers::numeric_limits<uint64_t>::max());
760 TEST_EQ(ta->f32(), 2.3f);
761 TEST_EQ(ta->f64(), 2.3);
762 using namespace flatbuffers; // is_same
763 static_assert(is_same<decltype(ta->i8()), int8_t>::value, "invalid type");
764 static_assert(is_same<decltype(ta->i16()), int16_t>::value, "invalid type");
765 static_assert(is_same<decltype(ta->i32()), int32_t>::value, "invalid type");
766 static_assert(is_same<decltype(ta->i64()), int64_t>::value, "invalid type");
767 static_assert(is_same<decltype(ta->u8()), uint8_t>::value, "invalid type");
768 static_assert(is_same<decltype(ta->u16()), uint16_t>::value, "invalid type");
769 static_assert(is_same<decltype(ta->u32()), uint32_t>::value, "invalid type");
770 static_assert(is_same<decltype(ta->u64()), uint64_t>::value, "invalid type");
771 static_assert(is_same<decltype(ta->f32()), float>::value, "invalid type");
772 static_assert(is_same<decltype(ta->f64()), double>::value, "invalid type");
773 }
774
775 // example of parsing text straight into a buffer, and generating
776 // text back from it:
ParseAndGenerateTextTest(const std::string & tests_data_path,bool binary)777 void ParseAndGenerateTextTest(const std::string &tests_data_path, bool binary) {
778 // load FlatBuffer schema (.fbs) and JSON from disk
779 std::string schemafile;
780 std::string jsonfile;
781 TEST_EQ(flatbuffers::LoadFile(
782 (tests_data_path + "monster_test." + (binary ? "bfbs" : "fbs"))
783 .c_str(),
784 binary, &schemafile),
785 true);
786 TEST_EQ(flatbuffers::LoadFile(
787 (tests_data_path + "monsterdata_test.golden").c_str(), false,
788 &jsonfile),
789 true);
790
791 auto include_test_path =
792 flatbuffers::ConCatPathFileName(tests_data_path, "include_test");
793 const char *include_directories[] = { tests_data_path.c_str(),
794 include_test_path.c_str(), nullptr };
795
796 // parse schema first, so we can use it to parse the data after
797 flatbuffers::Parser parser;
798 if (binary) {
799 flatbuffers::Verifier verifier(
800 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
801 schemafile.size());
802 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
803 // auto schema = reflection::GetSchema(schemafile.c_str());
804 TEST_EQ(parser.Deserialize(
805 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
806 schemafile.size()),
807 true);
808 } else {
809 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
810 }
811 TEST_EQ(parser.ParseJson(jsonfile.c_str()), true);
812
813 // here, parser.builder_ contains a binary buffer that is the parsed data.
814
815 // First, verify it, just in case:
816 flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
817 parser.builder_.GetSize());
818 TEST_EQ(VerifyMonsterBuffer(verifier), true);
819
820 AccessFlatBufferTest(parser.builder_.GetBufferPointer(),
821 parser.builder_.GetSize(), false);
822
823 // to ensure it is correct, we now generate text back from the binary,
824 // and compare the two:
825 std::string jsongen;
826 auto result = GenText(parser, parser.builder_.GetBufferPointer(), &jsongen);
827 TEST_NULL(result);
828 TEST_EQ_STR(jsongen.c_str(), jsonfile.c_str());
829
830 // We can also do the above using the convenient Registry that knows about
831 // a set of file_identifiers mapped to schemas.
832 flatbuffers::Registry registry;
833 // Make sure schemas can find their includes.
834 registry.AddIncludeDirectory(tests_data_path.c_str());
835 registry.AddIncludeDirectory(include_test_path.c_str());
836 // Call this with many schemas if possible.
837 registry.Register(MonsterIdentifier(),
838 (tests_data_path + "monster_test.fbs").c_str());
839 // Now we got this set up, we can parse by just specifying the identifier,
840 // the correct schema will be loaded on the fly:
841 auto buf = registry.TextToFlatBuffer(jsonfile.c_str(), MonsterIdentifier());
842 // If this fails, check registry.lasterror_.
843 TEST_NOTNULL(buf.data());
844 // Test the buffer, to be sure:
845 AccessFlatBufferTest(buf.data(), buf.size(), false);
846 // We can use the registry to turn this back into text, in this case it
847 // will get the file_identifier from the binary:
848 std::string text;
849 auto ok = registry.FlatBufferToText(buf.data(), buf.size(), &text);
850 // If this fails, check registry.lasterror_.
851 TEST_EQ(ok, true);
852 TEST_EQ_STR(text.c_str(), jsonfile.c_str());
853
854 // Generate text for UTF-8 strings without escapes.
855 std::string jsonfile_utf8;
856 TEST_EQ(flatbuffers::LoadFile((tests_data_path + "unicode_test.json").c_str(),
857 false, &jsonfile_utf8),
858 true);
859 TEST_EQ(parser.Parse(jsonfile_utf8.c_str(), include_directories), true);
860 // To ensure it is correct, generate utf-8 text back from the binary.
861 std::string jsongen_utf8;
862 // request natural printing for utf-8 strings
863 parser.opts.natural_utf8 = true;
864 parser.opts.strict_json = true;
865 TEST_NULL(GenText(parser, parser.builder_.GetBufferPointer(), &jsongen_utf8));
866 TEST_EQ_STR(jsongen_utf8.c_str(), jsonfile_utf8.c_str());
867 }
868
UnPackTo(const uint8_t * flatbuf)869 void UnPackTo(const uint8_t *flatbuf) {
870 // Get a monster that has a name and no enemy
871 auto orig_monster = GetMonster(flatbuf);
872 TEST_EQ_STR(orig_monster->name()->c_str(), "MyMonster");
873 TEST_ASSERT(orig_monster->enemy() == nullptr);
874
875 // Create an enemy
876 MonsterT *enemy = new MonsterT();
877 enemy->name = "Enemy";
878
879 // And create another monster owning the enemy,
880 MonsterT mon;
881 mon.name = "I'm monster 1";
882 mon.enemy.reset(enemy);
883 TEST_ASSERT(mon.enemy != nullptr);
884
885 // Assert that all the Monster objects are correct.
886 TEST_EQ_STR(mon.name.c_str(), "I'm monster 1");
887 TEST_EQ_STR(enemy->name.c_str(), "Enemy");
888 TEST_EQ_STR(mon.enemy->name.c_str(), "Enemy");
889
890 // Now unpack monster ("MyMonster") into monster
891 orig_monster->UnPackTo(&mon);
892
893 // Monster name should be from monster
894 TEST_EQ_STR(mon.name.c_str(), "MyMonster");
895
896 // The monster shouldn't have any enemies, because monster didn't.
897 TEST_ASSERT(mon.enemy == nullptr);
898 }
899
900 } // namespace tests
901 } // namespace flatbuffers
902