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