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
17namespace flatbuffers {
18namespace tests {
19
20// Shortcuts for the infinity.
21static const auto infinity_f = std::numeric_limits<float>::infinity();
22static const auto infinity_d = std::numeric_limits<double>::infinity();
23
24using namespace MyGame::Example;
25
26// example of how to build up a serialized buffer algorithmically:
27flatbuffers::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:
194void 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 // Test accessing a vector of sorted structs
317 auto vecofstructs = monster->testarrayofsortedstruct();
318 if (vecofstructs) { // not filled in monster_test.bfbs
319 for (flatbuffers::uoffset_t i = 0; i < vecofstructs->size() - 1; i++) {
320 auto left = vecofstructs->Get(i);
321 auto right = vecofstructs->Get(i + 1);
322 TEST_EQ(true, (left->KeyCompareLessThan(right)));
323 }
324 TEST_NOTNULL(vecofstructs->LookupByKey(0)); // test default value
325 TEST_NOTNULL(vecofstructs->LookupByKey(3));
326 TEST_EQ(static_cast<const Ability *>(nullptr),
327 vecofstructs->LookupByKey(5));
328 }
329
330 if (auto vec_of_stat = monster->scalar_key_sorted_tables()) {
331 auto stat_0 = vec_of_stat->LookupByKey(static_cast<uint16_t>(0u));
332 TEST_NOTNULL(stat_0);
333 TEST_NOTNULL(stat_0->id());
334 TEST_EQ(0, stat_0->count());
335 TEST_EQ_STR("miss", stat_0->id()->c_str());
336 }
337
338 // Test nested FlatBuffers if available:
339 auto nested_buffer = monster->testnestedflatbuffer();
340 if (nested_buffer) {
341 // nested_buffer is a vector of bytes you can memcpy. However, if you
342 // actually want to access the nested data, this is a convenient
343 // accessor that directly gives you the root table:
344 auto nested_monster = monster->testnestedflatbuffer_nested_root();
345 TEST_EQ_STR(nested_monster->name()->c_str(), "NestedMonster");
346 }
347
348 // Test flexbuffer if available:
349 auto flex = monster->flex();
350 // flex is a vector of bytes you can memcpy etc.
351 TEST_EQ(flex->size(), 4); // Encoded FlexBuffer bytes.
352 // However, if you actually want to access the nested data, this is a
353 // convenient accessor that directly gives you the root value:
354 TEST_EQ(monster->flex_flexbuffer_root().AsInt16(), 1234);
355
356 // Test vector of enums:
357 auto colors = monster->vector_of_enums();
358 if (colors) {
359 TEST_EQ(colors->size(), 2);
360 TEST_EQ(colors->Get(0), Color_Blue);
361 TEST_EQ(colors->Get(1), Color_Green);
362 }
363
364 // Since Flatbuffers uses explicit mechanisms to override the default
365 // compiler alignment, double check that the compiler indeed obeys them:
366 // (Test consists of a short and byte):
367 TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
368 TEST_EQ(sizeof(Test), 4UL);
369
370 const flatbuffers::Vector<const Test *> *tests_array[] = {
371 monster->test4(),
372 monster->test5(),
373 };
374 for (size_t i = 0; i < sizeof(tests_array) / sizeof(tests_array[0]); ++i) {
375 auto tests = tests_array[i];
376 TEST_NOTNULL(tests);
377 auto test_0 = tests->Get(0);
378 auto test_1 = tests->Get(1);
379 TEST_EQ(test_0->a(), 10);
380 TEST_EQ(test_0->b(), 20);
381 TEST_EQ(test_1->a(), 30);
382 TEST_EQ(test_1->b(), 40);
383 for (auto it = tests->begin(); it != tests->end(); ++it) {
384 TEST_EQ(it->a() == 10 || it->a() == 30, true); // Just testing iterators.
385 }
386 }
387
388 // Checking for presence of fields:
389 TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_HP), true);
390 TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_MANA), false);
391
392 // Obtaining a buffer from a root:
393 TEST_EQ(GetBufferStartFromRootPointer(monster), flatbuf);
394}
395
396// Change a FlatBuffer in-place, after it has been constructed.
397void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
398 // Get non-const pointer to root.
399 auto monster = GetMutableMonster(flatbuf);
400
401 // Each of these tests mutates, then tests, then set back to the original,
402 // so we can test that the buffer in the end still passes our original test.
403 auto hp_ok = monster->mutate_hp(10);
404 TEST_EQ(hp_ok, true); // Field was present.
405 TEST_EQ(monster->hp(), 10);
406 // Mutate to default value
407 auto hp_ok_default = monster->mutate_hp(100);
408 TEST_EQ(hp_ok_default, true); // Field was present.
409 TEST_EQ(monster->hp(), 100);
410 // Test that mutate to default above keeps field valid for further mutations
411 auto hp_ok_2 = monster->mutate_hp(20);
412 TEST_EQ(hp_ok_2, true);
413 TEST_EQ(monster->hp(), 20);
414 monster->mutate_hp(80);
415
416 // Monster originally at 150 mana (default value)
417 auto mana_default_ok = monster->mutate_mana(150); // Mutate to default value.
418 TEST_EQ(mana_default_ok,
419 true); // Mutation should succeed, because default value.
420 TEST_EQ(monster->mana(), 150);
421 auto mana_ok = monster->mutate_mana(10);
422 TEST_EQ(mana_ok, false); // Field was NOT present, because default value.
423 TEST_EQ(monster->mana(), 150);
424
425 // Mutate structs.
426 auto pos = monster->mutable_pos();
427 auto &test3 = pos->mutable_test3(); // Struct inside a struct.
428 test3.mutate_a(50); // Struct fields never fail.
429 TEST_EQ(test3.a(), 50);
430 test3.mutate_a(10);
431
432 // Mutate vectors.
433 auto inventory = monster->mutable_inventory();
434 inventory->Mutate(9, 100);
435 TEST_EQ(inventory->Get(9), 100);
436 inventory->Mutate(9, 9);
437
438 auto tables = monster->mutable_testarrayoftables();
439 auto first = tables->GetMutableObject(0);
440 TEST_EQ(first->hp(), 1000);
441 first->mutate_hp(0);
442 TEST_EQ(first->hp(), 0);
443 first->mutate_hp(1000);
444
445 // Test for each loop over mutable entries
446 for (auto item : *tables) {
447 TEST_EQ(item->hp(), 1000);
448 item->mutate_hp(0);
449 TEST_EQ(item->hp(), 0);
450 item->mutate_hp(1000);
451 break; // one iteration is enough, just testing compilation
452 }
453
454 // Mutate via LookupByKey
455 TEST_NOTNULL(tables->MutableLookupByKey("Barney"));
456 TEST_EQ(static_cast<Monster *>(nullptr),
457 tables->MutableLookupByKey("DoesntExist"));
458 TEST_EQ(tables->MutableLookupByKey("Barney")->hp(), 1000);
459 TEST_EQ(tables->MutableLookupByKey("Barney")->mutate_hp(0), true);
460 TEST_EQ(tables->LookupByKey("Barney")->hp(), 0);
461 TEST_EQ(tables->MutableLookupByKey("Barney")->mutate_hp(1000), true);
462
463 // Run the verifier and the regular test to make sure we didn't trample on
464 // anything.
465 AccessFlatBufferTest(flatbuf, length);
466}
467
468// Unpack a FlatBuffer into objects.
469void ObjectFlatBuffersTest(uint8_t *flatbuf) {
470 // Optional: we can specify resolver and rehasher functions to turn hashed
471 // strings into object pointers and back, to implement remote references
472 // and such.
473 auto resolver = flatbuffers::resolver_function_t(
474 [](void **pointer_adr, flatbuffers::hash_value_t hash) {
475 (void)pointer_adr;
476 (void)hash;
477 // Don't actually do anything, leave variable null.
478 });
479 auto rehasher = flatbuffers::rehasher_function_t(
480 [](void *pointer) -> flatbuffers::hash_value_t {
481 (void)pointer;
482 return 0;
483 });
484
485 // Turn a buffer into C++ objects.
486 auto monster1 = UnPackMonster(flatbuf, &resolver);
487
488 // Re-serialize the data.
489 flatbuffers::FlatBufferBuilder fbb1;
490 fbb1.Finish(CreateMonster(fbb1, monster1.get(), &rehasher),
491 MonsterIdentifier());
492
493 // Unpack again, and re-serialize again.
494 auto monster2 = UnPackMonster(fbb1.GetBufferPointer(), &resolver);
495 flatbuffers::FlatBufferBuilder fbb2;
496 fbb2.Finish(CreateMonster(fbb2, monster2.get(), &rehasher),
497 MonsterIdentifier());
498
499 // Now we've gone full round-trip, the two buffers should match.
500 const auto len1 = fbb1.GetSize();
501 const auto len2 = fbb2.GetSize();
502 TEST_EQ(len1, len2);
503 TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(), len1), 0);
504
505 // Test it with the original buffer test to make sure all data survived.
506 AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false);
507
508 // Test accessing fields, similar to AccessFlatBufferTest above.
509 CheckMonsterObject(monster2.get());
510
511 // Test object copy.
512 MonsterT monster3 = *monster2;
513 flatbuffers::FlatBufferBuilder fbb3;
514 fbb3.Finish(CreateMonster(fbb3, &monster3, &rehasher), MonsterIdentifier());
515 const auto len3 = fbb3.GetSize();
516 TEST_EQ(len2, len3);
517 TEST_EQ(memcmp(fbb2.GetBufferPointer(), fbb3.GetBufferPointer(), len2), 0);
518 // Delete monster1 and monster2, then test accessing fields in monster3.
519 monster1.reset();
520 monster2.reset();
521 CheckMonsterObject(&monster3);
522}
523
524// Utility function to check a Monster object.
525void CheckMonsterObject(MonsterT *monster2) {
526 TEST_EQ(monster2->hp, 80);
527 TEST_EQ(monster2->mana, 150); // default
528 TEST_EQ_STR(monster2->name.c_str(), "MyMonster");
529
530 auto &pos = monster2->pos;
531 TEST_NOTNULL(pos);
532 TEST_EQ(pos->z(), 3);
533 TEST_EQ(pos->test3().a(), 10);
534 TEST_EQ(pos->test3().b(), 20);
535
536 auto &inventory = monster2->inventory;
537 TEST_EQ(inventory.size(), 10UL);
538 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
539 for (auto it = inventory.begin(); it != inventory.end(); ++it)
540 TEST_EQ(*it, inv_data[it - inventory.begin()]);
541
542 TEST_EQ(monster2->color, Color_Blue);
543
544 auto monster3 = monster2->test.AsMonster();
545 TEST_NOTNULL(monster3);
546 TEST_EQ_STR(monster3->name.c_str(), "Fred");
547
548 auto &vecofstrings = monster2->testarrayofstring;
549 TEST_EQ(vecofstrings.size(), 4U);
550 TEST_EQ_STR(vecofstrings[0].c_str(), "bob");
551 TEST_EQ_STR(vecofstrings[1].c_str(), "fred");
552
553 auto &vecofstrings2 = monster2->testarrayofstring2;
554 TEST_EQ(vecofstrings2.size(), 2U);
555 TEST_EQ_STR(vecofstrings2[0].c_str(), "jane");
556 TEST_EQ_STR(vecofstrings2[1].c_str(), "mary");
557
558 auto &vecoftables = monster2->testarrayoftables;
559 TEST_EQ(vecoftables.size(), 3U);
560 TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney");
561 TEST_EQ(vecoftables[0]->hp, 1000);
562 TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred");
563 TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma");
564
565 auto &tests = monster2->test4;
566 TEST_EQ(tests[0].a(), 10);
567 TEST_EQ(tests[0].b(), 20);
568 TEST_EQ(tests[1].a(), 30);
569 TEST_EQ(tests[1].b(), 40);
570}
571
572// Prefix a FlatBuffer with a size field.
573void SizePrefixedTest() {
574 // Create size prefixed buffer.
575 flatbuffers::FlatBufferBuilder fbb;
576 FinishSizePrefixedMonsterBuffer(
577 fbb, CreateMonster(fbb, nullptr, 200, 300, fbb.CreateString("bob")));
578
579 // Verify it.
580 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
581 TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier), true);
582
583 // The prefixed size doesn't include itself, so substract the size of the
584 // prefix
585 TEST_EQ(GetPrefixedSize(fbb.GetBufferPointer()),
586 fbb.GetSize() - sizeof(uoffset_t));
587
588 // Getting the buffer length does include the prefix size, so it should be the
589 // full lenght.
590 TEST_EQ(GetSizePrefixedBufferLength(fbb.GetBufferPointer()), fbb.GetSize());
591
592 // Access it.
593 auto m = GetSizePrefixedMonster(fbb.GetBufferPointer());
594 TEST_EQ(m->mana(), 200);
595 TEST_EQ(m->hp(), 300);
596 TEST_EQ_STR(m->name()->c_str(), "bob");
597
598 {
599 // Verify that passing a larger size is OK, but not a smaller
600 flatbuffers::Verifier verifier_larger(fbb.GetBufferPointer(),
601 fbb.GetSize() + 10);
602 TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_larger), true);
603
604 flatbuffers::Verifier verifier_smaller(fbb.GetBufferPointer(),
605 fbb.GetSize() - 10);
606 TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_smaller), false);
607 }
608}
609
610void TestMonsterExtraFloats(const std::string &tests_data_path) {
611#if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
612 TEST_EQ(is_quiet_nan(1.0), false);
613 TEST_EQ(is_quiet_nan(infinity_d), false);
614 TEST_EQ(is_quiet_nan(-infinity_f), false);
615 TEST_EQ(is_quiet_nan(std::numeric_limits<float>::quiet_NaN()), true);
616 TEST_EQ(is_quiet_nan(std::numeric_limits<double>::quiet_NaN()), true);
617
618 using namespace flatbuffers;
619 using namespace MyGame;
620 // Load FlatBuffer schema (.fbs) from disk.
621 std::string schemafile;
622 TEST_EQ(LoadFile((tests_data_path + "monster_extra.fbs").c_str(), false,
623 &schemafile),
624 true);
625 // Parse schema first, so we can use it to parse the data after.
626 Parser parser;
627 auto include_test_path = ConCatPathFileName(tests_data_path, "include_test");
628 const char *include_directories[] = { tests_data_path.c_str(),
629 include_test_path.c_str(), nullptr };
630 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
631 // Create empty extra and store to json.
632 parser.opts.output_default_scalars_in_json = true;
633 parser.opts.output_enum_identifiers = true;
634 FlatBufferBuilder builder;
635 const auto def_root = MonsterExtraBuilder(builder).Finish();
636 FinishMonsterExtraBuffer(builder, def_root);
637 const auto def_obj = builder.GetBufferPointer();
638 const auto def_extra = GetMonsterExtra(def_obj);
639 TEST_NOTNULL(def_extra);
640 TEST_EQ(is_quiet_nan(def_extra->f0()), true);
641 TEST_EQ(is_quiet_nan(def_extra->f1()), true);
642 TEST_EQ(def_extra->f2(), +infinity_f);
643 TEST_EQ(def_extra->f3(), -infinity_f);
644 TEST_EQ(is_quiet_nan(def_extra->d0()), true);
645 TEST_EQ(is_quiet_nan(def_extra->d1()), true);
646 TEST_EQ(def_extra->d2(), +infinity_d);
647 TEST_EQ(def_extra->d3(), -infinity_d);
648 std::string jsongen;
649 auto result = GenText(parser, def_obj, &jsongen);
650 TEST_NULL(result);
651 // Check expected default values.
652 TEST_EQ(std::string::npos != jsongen.find("f0: nan"), true);
653 TEST_EQ(std::string::npos != jsongen.find("f1: nan"), true);
654 TEST_EQ(std::string::npos != jsongen.find("f2: inf"), true);
655 TEST_EQ(std::string::npos != jsongen.find("f3: -inf"), true);
656 TEST_EQ(std::string::npos != jsongen.find("d0: nan"), true);
657 TEST_EQ(std::string::npos != jsongen.find("d1: nan"), true);
658 TEST_EQ(std::string::npos != jsongen.find("d2: inf"), true);
659 TEST_EQ(std::string::npos != jsongen.find("d3: -inf"), true);
660 // Parse 'mosterdata_extra.json'.
661 const auto extra_base = tests_data_path + "monsterdata_extra";
662 jsongen = "";
663 TEST_EQ(LoadFile((extra_base + ".json").c_str(), false, &jsongen), true);
664 TEST_EQ(parser.Parse(jsongen.c_str()), true);
665 const auto test_file = parser.builder_.GetBufferPointer();
666 const auto test_size = parser.builder_.GetSize();
667 Verifier verifier(test_file, test_size);
668 TEST_ASSERT(VerifyMonsterExtraBuffer(verifier));
669 const auto extra = GetMonsterExtra(test_file);
670 TEST_NOTNULL(extra);
671 TEST_EQ(is_quiet_nan(extra->f0()), true);
672 TEST_EQ(is_quiet_nan(extra->f1()), true);
673 TEST_EQ(extra->f2(), +infinity_f);
674 TEST_EQ(extra->f3(), -infinity_f);
675 TEST_EQ(is_quiet_nan(extra->d0()), true);
676 TEST_EQ(extra->d1(), +infinity_d);
677 TEST_EQ(extra->d2(), -infinity_d);
678 TEST_EQ(is_quiet_nan(extra->d3()), true);
679 TEST_NOTNULL(extra->fvec());
680 TEST_EQ(extra->fvec()->size(), 4);
681 TEST_EQ(extra->fvec()->Get(0), 1.0f);
682 TEST_EQ(extra->fvec()->Get(1), -infinity_f);
683 TEST_EQ(extra->fvec()->Get(2), +infinity_f);
684 TEST_EQ(is_quiet_nan(extra->fvec()->Get(3)), true);
685 TEST_NOTNULL(extra->dvec());
686 TEST_EQ(extra->dvec()->size(), 4);
687 TEST_EQ(extra->dvec()->Get(0), 2.0);
688 TEST_EQ(extra->dvec()->Get(1), +infinity_d);
689 TEST_EQ(extra->dvec()->Get(2), -infinity_d);
690 TEST_EQ(is_quiet_nan(extra->dvec()->Get(3)), true);
691#endif
692}
693
694void EnumNamesTest() {
695 TEST_EQ_STR("Red", EnumNameColor(Color_Red));
696 TEST_EQ_STR("Green", EnumNameColor(Color_Green));
697 TEST_EQ_STR("Blue", EnumNameColor(Color_Blue));
698 // Check that Color to string don't crash while decode a mixture of Colors.
699 // 1) Example::Color enum is enum with unfixed underlying type.
700 // 2) Valid enum range: [0; 2^(ceil(log2(Color_ANY))) - 1].
701 // Consequence: A value is out of this range will lead to UB (since C++17).
702 // For details see C++17 standard or explanation on the SO:
703 // stackoverflow.com/questions/18195312/what-happens-if-you-static-cast-invalid-value-to-enum-class
704 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(0)));
705 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY - 1)));
706 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY + 1)));
707}
708
709void TypeAliasesTest() {
710 flatbuffers::FlatBufferBuilder builder;
711
712 builder.Finish(CreateTypeAliases(
713 builder, flatbuffers::numeric_limits<int8_t>::min(),
714 flatbuffers::numeric_limits<uint8_t>::max(),
715 flatbuffers::numeric_limits<int16_t>::min(),
716 flatbuffers::numeric_limits<uint16_t>::max(),
717 flatbuffers::numeric_limits<int32_t>::min(),
718 flatbuffers::numeric_limits<uint32_t>::max(),
719 flatbuffers::numeric_limits<int64_t>::min(),
720 flatbuffers::numeric_limits<uint64_t>::max(), 2.3f, 2.3));
721
722 auto p = builder.GetBufferPointer();
723 auto ta = flatbuffers::GetRoot<TypeAliases>(p);
724
725 TEST_EQ(ta->i8(), flatbuffers::numeric_limits<int8_t>::min());
726 TEST_EQ(ta->u8(), flatbuffers::numeric_limits<uint8_t>::max());
727 TEST_EQ(ta->i16(), flatbuffers::numeric_limits<int16_t>::min());
728 TEST_EQ(ta->u16(), flatbuffers::numeric_limits<uint16_t>::max());
729 TEST_EQ(ta->i32(), flatbuffers::numeric_limits<int32_t>::min());
730 TEST_EQ(ta->u32(), flatbuffers::numeric_limits<uint32_t>::max());
731 TEST_EQ(ta->i64(), flatbuffers::numeric_limits<int64_t>::min());
732 TEST_EQ(ta->u64(), flatbuffers::numeric_limits<uint64_t>::max());
733 TEST_EQ(ta->f32(), 2.3f);
734 TEST_EQ(ta->f64(), 2.3);
735 using namespace flatbuffers; // is_same
736 static_assert(is_same<decltype(ta->i8()), int8_t>::value, "invalid type");
737 static_assert(is_same<decltype(ta->i16()), int16_t>::value, "invalid type");
738 static_assert(is_same<decltype(ta->i32()), int32_t>::value, "invalid type");
739 static_assert(is_same<decltype(ta->i64()), int64_t>::value, "invalid type");
740 static_assert(is_same<decltype(ta->u8()), uint8_t>::value, "invalid type");
741 static_assert(is_same<decltype(ta->u16()), uint16_t>::value, "invalid type");
742 static_assert(is_same<decltype(ta->u32()), uint32_t>::value, "invalid type");
743 static_assert(is_same<decltype(ta->u64()), uint64_t>::value, "invalid type");
744 static_assert(is_same<decltype(ta->f32()), float>::value, "invalid type");
745 static_assert(is_same<decltype(ta->f64()), double>::value, "invalid type");
746}
747
748// example of parsing text straight into a buffer, and generating
749// text back from it:
750void ParseAndGenerateTextTest(const std::string &tests_data_path, bool binary) {
751 // load FlatBuffer schema (.fbs) and JSON from disk
752 std::string schemafile;
753 std::string jsonfile;
754 TEST_EQ(flatbuffers::LoadFile(
755 (tests_data_path + "monster_test." + (binary ? "bfbs" : "fbs"))
756 .c_str(),
757 binary, &schemafile),
758 true);
759 TEST_EQ(flatbuffers::LoadFile(
760 (tests_data_path + "monsterdata_test.golden").c_str(), false,
761 &jsonfile),
762 true);
763
764 auto include_test_path =
765 flatbuffers::ConCatPathFileName(tests_data_path, "include_test");
766 const char *include_directories[] = { tests_data_path.c_str(),
767 include_test_path.c_str(), nullptr };
768
769 // parse schema first, so we can use it to parse the data after
770 flatbuffers::Parser parser;
771 if (binary) {
772 flatbuffers::Verifier verifier(
773 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
774 schemafile.size());
775 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
776 // auto schema = reflection::GetSchema(schemafile.c_str());
777 TEST_EQ(parser.Deserialize(
778 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
779 schemafile.size()),
780 true);
781 } else {
782 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
783 }
784 TEST_EQ(parser.ParseJson(jsonfile.c_str()), true);
785
786 // here, parser.builder_ contains a binary buffer that is the parsed data.
787
788 // First, verify it, just in case:
789 flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
790 parser.builder_.GetSize());
791 TEST_EQ(VerifyMonsterBuffer(verifier), true);
792
793 AccessFlatBufferTest(parser.builder_.GetBufferPointer(),
794 parser.builder_.GetSize(), false);
795
796 // to ensure it is correct, we now generate text back from the binary,
797 // and compare the two:
798 std::string jsongen;
799 auto result = GenText(parser, parser.builder_.GetBufferPointer(), &jsongen);
800 TEST_NULL(result);
801 TEST_EQ_STR(jsongen.c_str(), jsonfile.c_str());
802
803 // We can also do the above using the convenient Registry that knows about
804 // a set of file_identifiers mapped to schemas.
805 flatbuffers::Registry registry;
806 // Make sure schemas can find their includes.
807 registry.AddIncludeDirectory(tests_data_path.c_str());
808 registry.AddIncludeDirectory(include_test_path.c_str());
809 // Call this with many schemas if possible.
810 registry.Register(MonsterIdentifier(),
811 (tests_data_path + "monster_test.fbs").c_str());
812 // Now we got this set up, we can parse by just specifying the identifier,
813 // the correct schema will be loaded on the fly:
814 auto buf = registry.TextToFlatBuffer(jsonfile.c_str(), MonsterIdentifier());
815 // If this fails, check registry.lasterror_.
816 TEST_NOTNULL(buf.data());
817 // Test the buffer, to be sure:
818 AccessFlatBufferTest(buf.data(), buf.size(), false);
819 // We can use the registry to turn this back into text, in this case it
820 // will get the file_identifier from the binary:
821 std::string text;
822 auto ok = registry.FlatBufferToText(buf.data(), buf.size(), &text);
823 // If this fails, check registry.lasterror_.
824 TEST_EQ(ok, true);
825 TEST_EQ_STR(text.c_str(), jsonfile.c_str());
826
827 // Generate text for UTF-8 strings without escapes.
828 std::string jsonfile_utf8;
829 TEST_EQ(flatbuffers::LoadFile((tests_data_path + "unicode_test.json").c_str(),
830 false, &jsonfile_utf8),
831 true);
832 TEST_EQ(parser.Parse(jsonfile_utf8.c_str(), include_directories), true);
833 // To ensure it is correct, generate utf-8 text back from the binary.
834 std::string jsongen_utf8;
835 // request natural printing for utf-8 strings
836 parser.opts.natural_utf8 = true;
837 parser.opts.strict_json = true;
838 TEST_NULL(GenText(parser, parser.builder_.GetBufferPointer(), &jsongen_utf8));
839 TEST_EQ_STR(jsongen_utf8.c_str(), jsonfile_utf8.c_str());
840}
841
842void UnPackTo(const uint8_t *flatbuf) {
843 // Get a monster that has a name and no enemy
844 auto orig_monster = GetMonster(flatbuf);
845 TEST_EQ_STR(orig_monster->name()->c_str(), "MyMonster");
846 TEST_ASSERT(orig_monster->enemy() == nullptr);
847
848 // Create an enemy
849 MonsterT *enemy = new MonsterT();
850 enemy->name = "Enemy";
851
852 // And create another monster owning the enemy,
853 MonsterT mon;
854 mon.name = "I'm monster 1";
855 mon.enemy.reset(enemy);
856 TEST_ASSERT(mon.enemy != nullptr);
857
858 // Assert that all the Monster objects are correct.
859 TEST_EQ_STR(mon.name.c_str(), "I'm monster 1");
860 TEST_EQ_STR(enemy->name.c_str(), "Enemy");
861 TEST_EQ_STR(mon.enemy->name.c_str(), "Enemy");
862
863 // Now unpack monster ("MyMonster") into monster
864 orig_monster->UnPackTo(&mon);
865
866 // Monster name should be from monster
867 TEST_EQ_STR(mon.name.c_str(), "MyMonster");
868
869 // The monster shouldn't have any enemies, because monster didn't.
870 TEST_ASSERT(mon.enemy == nullptr);
871}
872
873} // namespace tests
874} // namespace flatbuffers
View as plain text