2 * Copyright 2014 Google Inc. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "flatbuffers/flatbuffers.h"
19 #include "flatbuffers/idl.h"
20 #include "flatbuffers/minireflect.h"
21 #include "flatbuffers/registry.h"
22 #include "flatbuffers/util.h"
25 #ifdef FLATBUFFERS_CPP98_STL
27 using flatbuffers::unique_ptr;
32 #include "monster_test_generated.h"
33 #include "namespace_test/namespace_test1_generated.h"
34 #include "namespace_test/namespace_test2_generated.h"
35 #include "union_vector/union_vector_generated.h"
36 #include "monster_extra_generated.h"
37 #include "optional_scalars_generated.h"
38 #if !defined(_MSC_VER) || _MSC_VER >= 1700
39 # include "arrays_test_generated.h"
40 # include "evolution_test/evolution_v1_generated.h"
41 # include "evolution_test/evolution_v2_generated.h"
44 #include "native_type_test_generated.h"
45 #include "test_assert.h"
47 #include "flatbuffers/flexbuffers.h"
48 #include "monster_test_bfbs_generated.h" // Generated using --bfbs-comments --bfbs-builtins --cpp --bfbs-gen-embed
51 // Check that char* and uint8_t* are interoperable types.
52 // The reinterpret_cast<> between the pointers are used to simplify data loading.
53 static_assert(flatbuffers::is_same<uint8_t, char>::value ||
54 flatbuffers::is_same<uint8_t, unsigned char>::value,
55 "unexpected uint8_t type");
57 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
58 // Ensure IEEE-754 support if tests of floats with NaN/Inf will run.
59 static_assert(std::numeric_limits<float>::is_iec559 &&
60 std::numeric_limits<double>::is_iec559,
61 "IEC-559 (IEEE-754) standard required");
65 // Shortcuts for the infinity.
66 static const auto infinityf = std::numeric_limits<float>::infinity();
67 static const auto infinityd = std::numeric_limits<double>::infinity();
69 using namespace MyGame::Example;
71 void FlatBufferBuilderTest();
73 // Include simple random number generator to ensure results will be the
74 // same cross platform.
75 // http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator
76 uint32_t lcg_seed = 48271;
79 (static_cast<uint64_t>(lcg_seed) * 279470273UL) % 4294967291UL;
81 void lcg_reset() { lcg_seed = 48271; }
83 std::string test_data_path =
84 #ifdef BAZEL_TEST_DATA_PATH
85 "../com_github_google_flatbuffers/tests/";
90 // example of how to build up a serialized buffer algorithmically:
91 flatbuffers::DetachedBuffer CreateFlatBufferTest(std::string &buffer) {
92 flatbuffers::FlatBufferBuilder builder;
94 auto vec = Vec3(1, 2, 3, 0, Color_Red, Test(10, 20));
96 auto name = builder.CreateString("MyMonster");
98 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
99 auto inventory = builder.CreateVector(inv_data, 10);
101 // Alternatively, create the vector first, and fill in data later:
102 // unsigned char *inv_buf = nullptr;
103 // auto inventory = builder.CreateUninitializedVector<unsigned char>(
105 // memcpy(inv_buf, inv_data, 10);
107 Test tests[] = { Test(10, 20), Test(30, 40) };
108 auto testv = builder.CreateVectorOfStructs(tests, 2);
111 #ifndef FLATBUFFERS_CPP98_STL
112 // Create a vector of structures from a lambda.
113 auto testv2 = builder.CreateVectorOfStructs<Test>(
114 2, [&](size_t i, Test* s) -> void {
118 // Create a vector of structures using a plain old C++ function.
119 auto testv2 = builder.CreateVectorOfStructs<Test>(
120 2, [](size_t i, Test* s, void *state) -> void {
121 *s = (reinterpret_cast<Test*>(state))[i];
123 #endif // FLATBUFFERS_CPP98_STL
126 // create monster with very few fields set:
127 // (same functionality as CreateMonster below, but sets fields manually)
128 flatbuffers::Offset<Monster> mlocs[3];
129 auto fred = builder.CreateString("Fred");
130 auto barney = builder.CreateString("Barney");
131 auto wilma = builder.CreateString("Wilma");
132 MonsterBuilder mb1(builder);
134 mlocs[0] = mb1.Finish();
135 MonsterBuilder mb2(builder);
136 mb2.add_name(barney);
138 mlocs[1] = mb2.Finish();
139 MonsterBuilder mb3(builder);
141 mlocs[2] = mb3.Finish();
143 // Create an array of strings. Also test string pooling, and lambdas.
145 builder.CreateVector<flatbuffers::Offset<flatbuffers::String>>(
147 [](size_t i, flatbuffers::FlatBufferBuilder *b)
148 -> flatbuffers::Offset<flatbuffers::String> {
149 static const char *names[] = { "bob", "fred", "bob", "fred" };
150 return b->CreateSharedString(names[i]);
154 // Creating vectors of strings in one convenient call.
155 std::vector<std::string> names2;
156 names2.push_back("jane");
157 names2.push_back("mary");
158 auto vecofstrings2 = builder.CreateVectorOfStrings(names2);
160 // Create an array of sorted tables, can be used with binary search when read:
161 auto vecoftables = builder.CreateVectorOfSortedTables(mlocs, 3);
163 // Create an array of sorted structs,
164 // can be used with binary search when read:
165 std::vector<Ability> abilities;
166 abilities.push_back(Ability(4, 40));
167 abilities.push_back(Ability(3, 30));
168 abilities.push_back(Ability(2, 20));
169 abilities.push_back(Ability(1, 10));
170 auto vecofstructs = builder.CreateVectorOfSortedStructs(&abilities);
172 // Create a nested FlatBuffer.
173 // Nested FlatBuffers are stored in a ubyte vector, which can be convenient
174 // since they can be memcpy'd around much easier than other FlatBuffer
175 // values. They have little overhead compared to storing the table directly.
176 // As a test, create a mostly empty Monster buffer:
177 flatbuffers::FlatBufferBuilder nested_builder;
178 auto nmloc = CreateMonster(nested_builder, nullptr, 0, 0,
179 nested_builder.CreateString("NestedMonster"));
180 FinishMonsterBuffer(nested_builder, nmloc);
181 // Now we can store the buffer in the parent. Note that by default, vectors
182 // are only aligned to their elements or size field, so in this case if the
183 // buffer contains 64-bit elements, they may not be correctly aligned. We fix
185 builder.ForceVectorAlignment(nested_builder.GetSize(), sizeof(uint8_t),
186 nested_builder.GetBufferMinAlignment());
187 // If for whatever reason you don't have the nested_builder available, you
188 // can substitute flatbuffers::largest_scalar_t (64-bit) for the alignment, or
189 // the largest force_align value in your schema if you're using it.
190 auto nested_flatbuffer_vector = builder.CreateVector(
191 nested_builder.GetBufferPointer(), nested_builder.GetSize());
193 // Test a nested FlexBuffer:
194 flexbuffers::Builder flexbuild;
197 auto flex = builder.CreateVector(flexbuild.GetBuffer());
199 // Test vector of enums.
200 Color colors[] = { Color_Blue, Color_Green };
201 // We use this special creation function because we have an array of
202 // pre-C++11 (enum class) enums whose size likely is int, yet its declared
203 // type in the schema is byte.
204 auto vecofcolors = builder.CreateVectorScalarCast<uint8_t, Color>(colors, 2);
206 // shortcut for creating monster with all fields set:
207 auto mloc = CreateMonster(
208 builder, &vec, 150, 80, name, inventory, Color_Blue, Any_Monster,
209 mlocs[1].Union(), // Store a union.
210 testv, vecofstrings, vecoftables, 0, nested_flatbuffer_vector, 0, false,
211 0, 0, 0, 0, 0, 0, 0, 0, 0, 3.14159f, 3.0f, 0.0f, vecofstrings2,
212 vecofstructs, flex, testv2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
213 AnyUniqueAliases_NONE, 0, AnyAmbiguousAliases_NONE, 0, vecofcolors);
215 FinishMonsterBuffer(builder, mloc);
218 #ifdef FLATBUFFERS_TEST_VERBOSE
219 // print byte data for debugging:
220 auto p = builder.GetBufferPointer();
221 for (flatbuffers::uoffset_t i = 0; i < builder.GetSize(); i++)
226 // return the buffer for the caller to use.
228 reinterpret_cast<const char *>(builder.GetBufferPointer());
229 buffer.assign(bufferpointer, bufferpointer + builder.GetSize());
231 return builder.Release();
234 // example of accessing a buffer loaded in memory:
235 void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length,
236 bool pooled = true) {
237 // First, verify the buffers integrity (optional)
238 flatbuffers::Verifier verifier(flatbuf, length);
239 TEST_EQ(VerifyMonsterBuffer(verifier), true);
242 #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
243 std::vector<uint8_t> test_buff;
244 test_buff.resize(length * 2);
245 std::memcpy(&test_buff[0], flatbuf, length);
246 std::memcpy(&test_buff[length], flatbuf, length);
248 flatbuffers::Verifier verifier1(&test_buff[0], length);
249 TEST_EQ(VerifyMonsterBuffer(verifier1), true);
250 TEST_EQ(verifier1.GetComputedSize(), length);
252 flatbuffers::Verifier verifier2(&test_buff[length], length);
253 TEST_EQ(VerifyMonsterBuffer(verifier2), true);
254 TEST_EQ(verifier2.GetComputedSize(), length);
258 TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0);
259 TEST_EQ(MonsterBufferHasIdentifier(flatbuf), true);
260 TEST_EQ(strcmp(MonsterExtension(), "mon"), 0);
262 // Access the buffer from the root.
263 auto monster = GetMonster(flatbuf);
265 TEST_EQ(monster->hp(), 80);
266 TEST_EQ(monster->mana(), 150); // default
267 TEST_EQ_STR(monster->name()->c_str(), "MyMonster");
268 // Can't access the following field, it is deprecated in the schema,
269 // which means accessors are not generated:
270 // monster.friendly()
272 auto pos = monster->pos();
274 TEST_EQ(pos->z(), 3);
275 TEST_EQ(pos->test3().a(), 10);
276 TEST_EQ(pos->test3().b(), 20);
278 auto inventory = monster->inventory();
279 TEST_EQ(VectorLength(inventory), 10UL); // Works even if inventory is null.
280 TEST_NOTNULL(inventory);
281 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
282 // Check compatibilty of iterators with STL.
283 std::vector<unsigned char> inv_vec(inventory->begin(), inventory->end());
285 for (auto it = inventory->begin(); it != inventory->end(); ++it, ++n) {
286 auto indx = it - inventory->begin();
287 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
288 TEST_EQ(*it, inv_data[indx]);
290 TEST_EQ(n, inv_vec.size());
293 for (auto it = inventory->cbegin(); it != inventory->cend(); ++it, ++n) {
294 auto indx = it - inventory->cbegin();
295 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
296 TEST_EQ(*it, inv_data[indx]);
298 TEST_EQ(n, inv_vec.size());
301 for (auto it = inventory->rbegin(); it != inventory->rend(); ++it, ++n) {
302 auto indx = inventory->rend() - it - 1;
303 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
304 TEST_EQ(*it, inv_data[indx]);
306 TEST_EQ(n, inv_vec.size());
309 for (auto it = inventory->crbegin(); it != inventory->crend(); ++it, ++n) {
310 auto indx = inventory->crend() - it - 1;
311 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
312 TEST_EQ(*it, inv_data[indx]);
314 TEST_EQ(n, inv_vec.size());
316 TEST_EQ(monster->color(), Color_Blue);
318 // Example of accessing a union:
319 TEST_EQ(monster->test_type(), Any_Monster); // First make sure which it is.
320 auto monster2 = reinterpret_cast<const Monster *>(monster->test());
321 TEST_NOTNULL(monster2);
322 TEST_EQ_STR(monster2->name()->c_str(), "Fred");
324 // Example of accessing a vector of strings:
325 auto vecofstrings = monster->testarrayofstring();
326 TEST_EQ(vecofstrings->size(), 4U);
327 TEST_EQ_STR(vecofstrings->Get(0)->c_str(), "bob");
328 TEST_EQ_STR(vecofstrings->Get(1)->c_str(), "fred");
330 // These should have pointer equality because of string pooling.
331 TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str());
332 TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str());
335 auto vecofstrings2 = monster->testarrayofstring2();
337 TEST_EQ(vecofstrings2->size(), 2U);
338 TEST_EQ_STR(vecofstrings2->Get(0)->c_str(), "jane");
339 TEST_EQ_STR(vecofstrings2->Get(1)->c_str(), "mary");
342 // Example of accessing a vector of tables:
343 auto vecoftables = monster->testarrayoftables();
344 TEST_EQ(vecoftables->size(), 3U);
345 for (auto it = vecoftables->begin(); it != vecoftables->end(); ++it) {
346 TEST_EQ(strlen(it->name()->c_str()) >= 4, true);
348 TEST_EQ_STR(vecoftables->Get(0)->name()->c_str(), "Barney");
349 TEST_EQ(vecoftables->Get(0)->hp(), 1000);
350 TEST_EQ_STR(vecoftables->Get(1)->name()->c_str(), "Fred");
351 TEST_EQ_STR(vecoftables->Get(2)->name()->c_str(), "Wilma");
352 TEST_NOTNULL(vecoftables->LookupByKey("Barney"));
353 TEST_NOTNULL(vecoftables->LookupByKey("Fred"));
354 TEST_NOTNULL(vecoftables->LookupByKey("Wilma"));
356 // Test accessing a vector of sorted structs
357 auto vecofstructs = monster->testarrayofsortedstruct();
358 if (vecofstructs) { // not filled in monster_test.bfbs
359 for (flatbuffers::uoffset_t i = 0; i < vecofstructs->size() - 1; i++) {
360 auto left = vecofstructs->Get(i);
361 auto right = vecofstructs->Get(i + 1);
362 TEST_EQ(true, (left->KeyCompareLessThan(right)));
364 TEST_NOTNULL(vecofstructs->LookupByKey(3));
365 TEST_EQ(static_cast<const Ability *>(nullptr),
366 vecofstructs->LookupByKey(5));
369 // Test nested FlatBuffers if available:
370 auto nested_buffer = monster->testnestedflatbuffer();
372 // nested_buffer is a vector of bytes you can memcpy. However, if you
373 // actually want to access the nested data, this is a convenient
374 // accessor that directly gives you the root table:
375 auto nested_monster = monster->testnestedflatbuffer_nested_root();
376 TEST_EQ_STR(nested_monster->name()->c_str(), "NestedMonster");
379 // Test flexbuffer if available:
380 auto flex = monster->flex();
381 // flex is a vector of bytes you can memcpy etc.
382 TEST_EQ(flex->size(), 4); // Encoded FlexBuffer bytes.
383 // However, if you actually want to access the nested data, this is a
384 // convenient accessor that directly gives you the root value:
385 TEST_EQ(monster->flex_flexbuffer_root().AsInt16(), 1234);
387 // Test vector of enums:
388 auto colors = monster->vector_of_enums();
390 TEST_EQ(colors->size(), 2);
391 TEST_EQ(colors->Get(0), Color_Blue);
392 TEST_EQ(colors->Get(1), Color_Green);
395 // Since Flatbuffers uses explicit mechanisms to override the default
396 // compiler alignment, double check that the compiler indeed obeys them:
397 // (Test consists of a short and byte):
398 TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
399 TEST_EQ(sizeof(Test), 4UL);
401 const flatbuffers::Vector<const Test *> *tests_array[] = {
405 for (size_t i = 0; i < sizeof(tests_array) / sizeof(tests_array[0]); ++i) {
406 auto tests = tests_array[i];
408 auto test_0 = tests->Get(0);
409 auto test_1 = tests->Get(1);
410 TEST_EQ(test_0->a(), 10);
411 TEST_EQ(test_0->b(), 20);
412 TEST_EQ(test_1->a(), 30);
413 TEST_EQ(test_1->b(), 40);
414 for (auto it = tests->begin(); it != tests->end(); ++it) {
415 TEST_EQ(it->a() == 10 || it->a() == 30, true); // Just testing iterators.
419 // Checking for presence of fields:
420 TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_HP), true);
421 TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_MANA), false);
423 // Obtaining a buffer from a root:
424 TEST_EQ(GetBufferStartFromRootPointer(monster), flatbuf);
427 // Change a FlatBuffer in-place, after it has been constructed.
428 void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
429 // Get non-const pointer to root.
430 auto monster = GetMutableMonster(flatbuf);
432 // Each of these tests mutates, then tests, then set back to the original,
433 // so we can test that the buffer in the end still passes our original test.
434 auto hp_ok = monster->mutate_hp(10);
435 TEST_EQ(hp_ok, true); // Field was present.
436 TEST_EQ(monster->hp(), 10);
437 // Mutate to default value
438 auto hp_ok_default = monster->mutate_hp(100);
439 TEST_EQ(hp_ok_default, true); // Field was present.
440 TEST_EQ(monster->hp(), 100);
441 // Test that mutate to default above keeps field valid for further mutations
442 auto hp_ok_2 = monster->mutate_hp(20);
443 TEST_EQ(hp_ok_2, true);
444 TEST_EQ(monster->hp(), 20);
445 monster->mutate_hp(80);
447 // Monster originally at 150 mana (default value)
448 auto mana_default_ok = monster->mutate_mana(150); // Mutate to default value.
449 TEST_EQ(mana_default_ok,
450 true); // Mutation should succeed, because default value.
451 TEST_EQ(monster->mana(), 150);
452 auto mana_ok = monster->mutate_mana(10);
453 TEST_EQ(mana_ok, false); // Field was NOT present, because default value.
454 TEST_EQ(monster->mana(), 150);
457 auto pos = monster->mutable_pos();
458 auto test3 = pos->mutable_test3(); // Struct inside a struct.
459 test3.mutate_a(50); // Struct fields never fail.
460 TEST_EQ(test3.a(), 50);
464 auto inventory = monster->mutable_inventory();
465 inventory->Mutate(9, 100);
466 TEST_EQ(inventory->Get(9), 100);
467 inventory->Mutate(9, 9);
469 auto tables = monster->mutable_testarrayoftables();
470 auto first = tables->GetMutableObject(0);
471 TEST_EQ(first->hp(), 1000);
473 TEST_EQ(first->hp(), 0);
474 first->mutate_hp(1000);
476 // Run the verifier and the regular test to make sure we didn't trample on
478 AccessFlatBufferTest(flatbuf, length);
481 // Unpack a FlatBuffer into objects.
482 void ObjectFlatBuffersTest(uint8_t *flatbuf) {
483 // Optional: we can specify resolver and rehasher functions to turn hashed
484 // strings into object pointers and back, to implement remote references
486 auto resolver = flatbuffers::resolver_function_t(
487 [](void **pointer_adr, flatbuffers::hash_value_t hash) {
490 // Don't actually do anything, leave variable null.
492 auto rehasher = flatbuffers::rehasher_function_t(
493 [](void *pointer) -> flatbuffers::hash_value_t {
498 // Turn a buffer into C++ objects.
499 auto monster1 = UnPackMonster(flatbuf, &resolver);
501 // Re-serialize the data.
502 flatbuffers::FlatBufferBuilder fbb1;
503 fbb1.Finish(CreateMonster(fbb1, monster1.get(), &rehasher),
504 MonsterIdentifier());
506 // Unpack again, and re-serialize again.
507 auto monster2 = UnPackMonster(fbb1.GetBufferPointer(), &resolver);
508 flatbuffers::FlatBufferBuilder fbb2;
509 fbb2.Finish(CreateMonster(fbb2, monster2.get(), &rehasher),
510 MonsterIdentifier());
512 // Now we've gone full round-trip, the two buffers should match.
513 auto len1 = fbb1.GetSize();
514 auto len2 = fbb2.GetSize();
516 TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(), len1), 0);
518 // Test it with the original buffer test to make sure all data survived.
519 AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false);
521 // Test accessing fields, similar to AccessFlatBufferTest above.
522 TEST_EQ(monster2->hp, 80);
523 TEST_EQ(monster2->mana, 150); // default
524 TEST_EQ_STR(monster2->name.c_str(), "MyMonster");
526 auto &pos = monster2->pos;
528 TEST_EQ(pos->z(), 3);
529 TEST_EQ(pos->test3().a(), 10);
530 TEST_EQ(pos->test3().b(), 20);
532 auto &inventory = monster2->inventory;
533 TEST_EQ(inventory.size(), 10UL);
534 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
535 for (auto it = inventory.begin(); it != inventory.end(); ++it)
536 TEST_EQ(*it, inv_data[it - inventory.begin()]);
538 TEST_EQ(monster2->color, Color_Blue);
540 auto monster3 = monster2->test.AsMonster();
541 TEST_NOTNULL(monster3);
542 TEST_EQ_STR(monster3->name.c_str(), "Fred");
544 auto &vecofstrings = monster2->testarrayofstring;
545 TEST_EQ(vecofstrings.size(), 4U);
546 TEST_EQ_STR(vecofstrings[0].c_str(), "bob");
547 TEST_EQ_STR(vecofstrings[1].c_str(), "fred");
549 auto &vecofstrings2 = monster2->testarrayofstring2;
550 TEST_EQ(vecofstrings2.size(), 2U);
551 TEST_EQ_STR(vecofstrings2[0].c_str(), "jane");
552 TEST_EQ_STR(vecofstrings2[1].c_str(), "mary");
554 auto &vecoftables = monster2->testarrayoftables;
555 TEST_EQ(vecoftables.size(), 3U);
556 TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney");
557 TEST_EQ(vecoftables[0]->hp, 1000);
558 TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred");
559 TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma");
561 auto &tests = monster2->test4;
562 TEST_EQ(tests[0].a(), 10);
563 TEST_EQ(tests[0].b(), 20);
564 TEST_EQ(tests[1].a(), 30);
565 TEST_EQ(tests[1].b(), 40);
568 // Prefix a FlatBuffer with a size field.
569 void SizePrefixedTest() {
570 // Create size prefixed buffer.
571 flatbuffers::FlatBufferBuilder fbb;
572 FinishSizePrefixedMonsterBuffer(
573 fbb, CreateMonster(fbb, 0, 200, 300, fbb.CreateString("bob")));
576 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
577 TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier), true);
580 auto m = GetSizePrefixedMonster(fbb.GetBufferPointer());
581 TEST_EQ(m->mana(), 200);
582 TEST_EQ(m->hp(), 300);
583 TEST_EQ_STR(m->name()->c_str(), "bob");
586 void TriviallyCopyableTest() {
588 #if __GNUG__ && __GNUC__ < 5
589 TEST_EQ(__has_trivial_copy(Vec3), true);
591 #if __cplusplus >= 201103L
592 TEST_EQ(std::is_trivially_copyable<Vec3>::value, true);
598 // Check stringify of an default enum value to json
599 void JsonDefaultTest() {
600 // load FlatBuffer schema (.fbs) from disk
601 std::string schemafile;
602 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
605 // parse schema first, so we can use it to parse the data after
606 flatbuffers::Parser parser;
607 auto include_test_path =
608 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
609 const char *include_directories[] = { test_data_path.c_str(),
610 include_test_path.c_str(), nullptr };
612 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
613 // create incomplete monster and store to json
614 parser.opts.output_default_scalars_in_json = true;
615 parser.opts.output_enum_identifiers = true;
616 flatbuffers::FlatBufferBuilder builder;
617 auto name = builder.CreateString("default_enum");
618 MonsterBuilder color_monster(builder);
619 color_monster.add_name(name);
620 FinishMonsterBuffer(builder, color_monster.Finish());
622 auto result = GenerateText(parser, builder.GetBufferPointer(), &jsongen);
623 TEST_EQ(result, true);
624 // default value of the "color" field is Blue
625 TEST_EQ(std::string::npos != jsongen.find("color: \"Blue\""), true);
626 // default value of the "testf" field is 3.14159
627 TEST_EQ(std::string::npos != jsongen.find("testf: 3.14159"), true);
630 void JsonEnumsTest() {
631 // load FlatBuffer schema (.fbs) from disk
632 std::string schemafile;
633 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
636 // parse schema first, so we can use it to parse the data after
637 flatbuffers::Parser parser;
638 auto include_test_path =
639 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
640 const char *include_directories[] = { test_data_path.c_str(),
641 include_test_path.c_str(), nullptr };
642 parser.opts.output_enum_identifiers = true;
643 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
644 flatbuffers::FlatBufferBuilder builder;
645 auto name = builder.CreateString("bitflag_enum");
646 MonsterBuilder color_monster(builder);
647 color_monster.add_name(name);
648 color_monster.add_color(Color(Color_Blue | Color_Red));
649 FinishMonsterBuffer(builder, color_monster.Finish());
651 auto result = GenerateText(parser, builder.GetBufferPointer(), &jsongen);
652 TEST_EQ(result, true);
653 TEST_EQ(std::string::npos != jsongen.find("color: \"Red Blue\""), true);
654 // Test forward compatibility with 'output_enum_identifiers = true'.
655 // Current Color doesn't have '(1u << 2)' field, let's add it.
657 std::string future_json;
658 auto future_name = builder.CreateString("future bitflag_enum");
659 MonsterBuilder future_color(builder);
660 future_color.add_name(future_name);
661 future_color.add_color(
662 static_cast<Color>((1u << 2) | Color_Blue | Color_Red));
663 FinishMonsterBuffer(builder, future_color.Finish());
664 result = GenerateText(parser, builder.GetBufferPointer(), &future_json);
665 TEST_EQ(result, true);
666 TEST_EQ(std::string::npos != future_json.find("color: 13"), true);
669 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
670 // The IEEE-754 quiet_NaN is not simple binary constant.
671 // All binary NaN bit strings have all the bits of the biased exponent field E
672 // set to 1. A quiet NaN bit string should be encoded with the first bit d[1]
673 // of the trailing significand field T being 1 (d[0] is implicit bit).
674 // It is assumed that endianness of floating-point is same as integer.
675 template<typename T, typename U, U qnan_base> bool is_quiet_nan_impl(T v) {
676 static_assert(sizeof(T) == sizeof(U), "unexpected");
678 std::memcpy(&b, &v, sizeof(T));
679 return ((b & qnan_base) == qnan_base);
681 #if defined(__mips__) || defined(__hppa__)
682 static bool is_quiet_nan(float v) {
683 return is_quiet_nan_impl<float, uint32_t, 0x7FC00000u>(v) ||
684 is_quiet_nan_impl<float, uint32_t, 0x7FBFFFFFu>(v);
686 static bool is_quiet_nan(double v) {
687 return is_quiet_nan_impl<double, uint64_t, 0x7FF8000000000000ul>(v) ||
688 is_quiet_nan_impl<double, uint64_t, 0x7FF7FFFFFFFFFFFFu>(v);
691 static bool is_quiet_nan(float v) {
692 return is_quiet_nan_impl<float, uint32_t, 0x7FC00000u>(v);
694 static bool is_quiet_nan(double v) {
695 return is_quiet_nan_impl<double, uint64_t, 0x7FF8000000000000ul>(v);
699 void TestMonsterExtraFloats() {
700 TEST_EQ(is_quiet_nan(1.0), false);
701 TEST_EQ(is_quiet_nan(infinityd), false);
702 TEST_EQ(is_quiet_nan(-infinityf), false);
703 TEST_EQ(is_quiet_nan(std::numeric_limits<float>::quiet_NaN()), true);
704 TEST_EQ(is_quiet_nan(std::numeric_limits<double>::quiet_NaN()), true);
706 using namespace flatbuffers;
707 using namespace MyGame;
708 // Load FlatBuffer schema (.fbs) from disk.
709 std::string schemafile;
710 TEST_EQ(LoadFile((test_data_path + "monster_extra.fbs").c_str(), false,
713 // Parse schema first, so we can use it to parse the data after.
715 auto include_test_path = ConCatPathFileName(test_data_path, "include_test");
716 const char *include_directories[] = { test_data_path.c_str(),
717 include_test_path.c_str(), nullptr };
718 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
719 // Create empty extra and store to json.
720 parser.opts.output_default_scalars_in_json = true;
721 parser.opts.output_enum_identifiers = true;
722 FlatBufferBuilder builder;
723 const auto def_root = MonsterExtraBuilder(builder).Finish();
724 FinishMonsterExtraBuffer(builder, def_root);
725 const auto def_obj = builder.GetBufferPointer();
726 const auto def_extra = GetMonsterExtra(def_obj);
727 TEST_NOTNULL(def_extra);
728 TEST_EQ(is_quiet_nan(def_extra->f0()), true);
729 TEST_EQ(is_quiet_nan(def_extra->f1()), true);
730 TEST_EQ(def_extra->f2(), +infinityf);
731 TEST_EQ(def_extra->f3(), -infinityf);
732 TEST_EQ(is_quiet_nan(def_extra->d0()), true);
733 TEST_EQ(is_quiet_nan(def_extra->d1()), true);
734 TEST_EQ(def_extra->d2(), +infinityd);
735 TEST_EQ(def_extra->d3(), -infinityd);
737 auto result = GenerateText(parser, def_obj, &jsongen);
738 TEST_EQ(result, true);
739 // Check expected default values.
740 TEST_EQ(std::string::npos != jsongen.find("f0: nan"), true);
741 TEST_EQ(std::string::npos != jsongen.find("f1: nan"), true);
742 TEST_EQ(std::string::npos != jsongen.find("f2: inf"), true);
743 TEST_EQ(std::string::npos != jsongen.find("f3: -inf"), true);
744 TEST_EQ(std::string::npos != jsongen.find("d0: nan"), true);
745 TEST_EQ(std::string::npos != jsongen.find("d1: nan"), true);
746 TEST_EQ(std::string::npos != jsongen.find("d2: inf"), true);
747 TEST_EQ(std::string::npos != jsongen.find("d3: -inf"), true);
748 // Parse 'mosterdata_extra.json'.
749 const auto extra_base = test_data_path + "monsterdata_extra";
751 TEST_EQ(LoadFile((extra_base + ".json").c_str(), false, &jsongen), true);
752 TEST_EQ(parser.Parse(jsongen.c_str()), true);
753 const auto test_file = parser.builder_.GetBufferPointer();
754 const auto test_size = parser.builder_.GetSize();
755 Verifier verifier(test_file, test_size);
756 TEST_ASSERT(VerifyMonsterExtraBuffer(verifier));
757 const auto extra = GetMonsterExtra(test_file);
759 TEST_EQ(is_quiet_nan(extra->f0()), true);
760 TEST_EQ(is_quiet_nan(extra->f1()), true);
761 TEST_EQ(extra->f2(), +infinityf);
762 TEST_EQ(extra->f3(), -infinityf);
763 TEST_EQ(is_quiet_nan(extra->d0()), true);
764 TEST_EQ(extra->d1(), +infinityd);
765 TEST_EQ(extra->d2(), -infinityd);
766 TEST_EQ(is_quiet_nan(extra->d3()), true);
767 TEST_NOTNULL(extra->fvec());
768 TEST_EQ(extra->fvec()->size(), 4);
769 TEST_EQ(extra->fvec()->Get(0), 1.0f);
770 TEST_EQ(extra->fvec()->Get(1), -infinityf);
771 TEST_EQ(extra->fvec()->Get(2), +infinityf);
772 TEST_EQ(is_quiet_nan(extra->fvec()->Get(3)), true);
773 TEST_NOTNULL(extra->dvec());
774 TEST_EQ(extra->dvec()->size(), 4);
775 TEST_EQ(extra->dvec()->Get(0), 2.0);
776 TEST_EQ(extra->dvec()->Get(1), +infinityd);
777 TEST_EQ(extra->dvec()->Get(2), -infinityd);
778 TEST_EQ(is_quiet_nan(extra->dvec()->Get(3)), true);
781 void TestMonsterExtraFloats() {}
784 // example of parsing text straight into a buffer, and generating
785 // text back from it:
786 void ParseAndGenerateTextTest(bool binary) {
787 // load FlatBuffer schema (.fbs) and JSON from disk
788 std::string schemafile;
789 std::string jsonfile;
790 TEST_EQ(flatbuffers::LoadFile(
791 (test_data_path + "monster_test." + (binary ? "bfbs" : "fbs"))
793 binary, &schemafile),
795 TEST_EQ(flatbuffers::LoadFile(
796 (test_data_path + "monsterdata_test.golden").c_str(), false,
800 auto include_test_path =
801 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
802 const char *include_directories[] = { test_data_path.c_str(),
803 include_test_path.c_str(), nullptr };
805 // parse schema first, so we can use it to parse the data after
806 flatbuffers::Parser parser;
808 flatbuffers::Verifier verifier(
809 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
811 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
812 // auto schema = reflection::GetSchema(schemafile.c_str());
813 TEST_EQ(parser.Deserialize((const uint8_t *)schemafile.c_str(),
817 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
819 TEST_EQ(parser.Parse(jsonfile.c_str(), include_directories), true);
821 // here, parser.builder_ contains a binary buffer that is the parsed data.
823 // First, verify it, just in case:
824 flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
825 parser.builder_.GetSize());
826 TEST_EQ(VerifyMonsterBuffer(verifier), true);
828 AccessFlatBufferTest(parser.builder_.GetBufferPointer(),
829 parser.builder_.GetSize(), false);
831 // to ensure it is correct, we now generate text back from the binary,
832 // and compare the two:
835 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
836 TEST_EQ(result, true);
837 TEST_EQ_STR(jsongen.c_str(), jsonfile.c_str());
839 // We can also do the above using the convenient Registry that knows about
840 // a set of file_identifiers mapped to schemas.
841 flatbuffers::Registry registry;
842 // Make sure schemas can find their includes.
843 registry.AddIncludeDirectory(test_data_path.c_str());
844 registry.AddIncludeDirectory(include_test_path.c_str());
845 // Call this with many schemas if possible.
846 registry.Register(MonsterIdentifier(),
847 (test_data_path + "monster_test.fbs").c_str());
848 // Now we got this set up, we can parse by just specifying the identifier,
849 // the correct schema will be loaded on the fly:
850 auto buf = registry.TextToFlatBuffer(jsonfile.c_str(), MonsterIdentifier());
851 // If this fails, check registry.lasterror_.
852 TEST_NOTNULL(buf.data());
853 // Test the buffer, to be sure:
854 AccessFlatBufferTest(buf.data(), buf.size(), false);
855 // We can use the registry to turn this back into text, in this case it
856 // will get the file_identifier from the binary:
858 auto ok = registry.FlatBufferToText(buf.data(), buf.size(), &text);
859 // If this fails, check registry.lasterror_.
861 TEST_EQ_STR(text.c_str(), jsonfile.c_str());
863 // Generate text for UTF-8 strings without escapes.
864 std::string jsonfile_utf8;
865 TEST_EQ(flatbuffers::LoadFile((test_data_path + "unicode_test.json").c_str(),
866 false, &jsonfile_utf8),
868 TEST_EQ(parser.Parse(jsonfile_utf8.c_str(), include_directories), true);
869 // To ensure it is correct, generate utf-8 text back from the binary.
870 std::string jsongen_utf8;
871 // request natural printing for utf-8 strings
872 parser.opts.natural_utf8 = true;
873 parser.opts.strict_json = true;
875 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen_utf8),
877 TEST_EQ_STR(jsongen_utf8.c_str(), jsonfile_utf8.c_str());
880 void ReflectionTest(uint8_t *flatbuf, size_t length) {
881 // Load a binary schema.
882 std::string bfbsfile;
883 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.bfbs").c_str(),
887 // Verify it, just in case:
888 flatbuffers::Verifier verifier(
889 reinterpret_cast<const uint8_t *>(bfbsfile.c_str()), bfbsfile.length());
890 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
892 // Make sure the schema is what we expect it to be.
893 auto &schema = *reflection::GetSchema(bfbsfile.c_str());
894 auto root_table = schema.root_table();
895 TEST_EQ_STR(root_table->name()->c_str(), "MyGame.Example.Monster");
896 auto fields = root_table->fields();
897 auto hp_field_ptr = fields->LookupByKey("hp");
898 TEST_NOTNULL(hp_field_ptr);
899 auto &hp_field = *hp_field_ptr;
900 TEST_EQ_STR(hp_field.name()->c_str(), "hp");
901 TEST_EQ(hp_field.id(), 2);
902 TEST_EQ(hp_field.type()->base_type(), reflection::Short);
904 auto friendly_field_ptr = fields->LookupByKey("friendly");
905 TEST_NOTNULL(friendly_field_ptr);
906 TEST_NOTNULL(friendly_field_ptr->attributes());
907 TEST_NOTNULL(friendly_field_ptr->attributes()->LookupByKey("priority"));
909 // Make sure the table index is what we expect it to be.
910 auto pos_field_ptr = fields->LookupByKey("pos");
911 TEST_NOTNULL(pos_field_ptr);
912 TEST_EQ(pos_field_ptr->type()->base_type(), reflection::Obj);
913 auto pos_table_ptr = schema.objects()->Get(pos_field_ptr->type()->index());
914 TEST_NOTNULL(pos_table_ptr);
915 TEST_EQ_STR(pos_table_ptr->name()->c_str(), "MyGame.Example.Vec3");
917 // Test nullability of fields: hp is a 0-default scalar, pos is a struct =>
918 // optional, and name is a required string => not optional.
919 TEST_EQ(hp_field.optional(), false);
920 TEST_EQ(pos_field_ptr->optional(), true);
921 TEST_EQ(fields->LookupByKey("name")->optional(), false);
923 // Now use it to dynamically access a buffer.
924 auto &root = *flatbuffers::GetAnyRoot(flatbuf);
926 // Verify the buffer first using reflection based verification
927 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), flatbuf, length),
930 auto hp = flatbuffers::GetFieldI<uint16_t>(root, hp_field);
933 // Rather than needing to know the type, we can also get the value of
934 // any field as an int64_t/double/string, regardless of what it actually is.
935 auto hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
936 TEST_EQ(hp_int64, 80);
937 auto hp_double = flatbuffers::GetAnyFieldF(root, hp_field);
938 TEST_EQ(hp_double, 80.0);
939 auto hp_string = flatbuffers::GetAnyFieldS(root, hp_field, &schema);
940 TEST_EQ_STR(hp_string.c_str(), "80");
942 // Get struct field through reflection
943 auto pos_struct = flatbuffers::GetFieldStruct(root, *pos_field_ptr);
944 TEST_NOTNULL(pos_struct);
945 TEST_EQ(flatbuffers::GetAnyFieldF(*pos_struct,
946 *pos_table_ptr->fields()->LookupByKey("z")),
949 auto test3_field = pos_table_ptr->fields()->LookupByKey("test3");
950 auto test3_struct = flatbuffers::GetFieldStruct(*pos_struct, *test3_field);
951 TEST_NOTNULL(test3_struct);
952 auto test3_object = schema.objects()->Get(test3_field->type()->index());
954 TEST_EQ(flatbuffers::GetAnyFieldF(*test3_struct,
955 *test3_object->fields()->LookupByKey("a")),
958 // We can also modify it.
959 flatbuffers::SetField<uint16_t>(&root, hp_field, 200);
960 hp = flatbuffers::GetFieldI<uint16_t>(root, hp_field);
963 // We can also set fields generically:
964 flatbuffers::SetAnyFieldI(&root, hp_field, 300);
965 hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
966 TEST_EQ(hp_int64, 300);
967 flatbuffers::SetAnyFieldF(&root, hp_field, 300.5);
968 hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
969 TEST_EQ(hp_int64, 300);
970 flatbuffers::SetAnyFieldS(&root, hp_field, "300");
971 hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
972 TEST_EQ(hp_int64, 300);
974 // Test buffer is valid after the modifications
975 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), flatbuf, length),
978 // Reset it, for further tests.
979 flatbuffers::SetField<uint16_t>(&root, hp_field, 80);
981 // More advanced functionality: changing the size of items in-line!
982 // First we put the FlatBuffer inside an std::vector.
983 std::vector<uint8_t> resizingbuf(flatbuf, flatbuf + length);
984 // Find the field we want to modify.
985 auto &name_field = *fields->LookupByKey("name");
987 // This time we wrap the result from GetAnyRoot in a smartpointer that
988 // will keep rroot valid as resizingbuf resizes.
989 auto rroot = flatbuffers::piv(
990 flatbuffers::GetAnyRoot(flatbuffers::vector_data(resizingbuf)),
992 SetString(schema, "totally new string", GetFieldS(**rroot, name_field),
994 // Here resizingbuf has changed, but rroot is still valid.
995 TEST_EQ_STR(GetFieldS(**rroot, name_field)->c_str(), "totally new string");
996 // Now lets extend a vector by 100 elements (10 -> 110).
997 auto &inventory_field = *fields->LookupByKey("inventory");
998 auto rinventory = flatbuffers::piv(
999 flatbuffers::GetFieldV<uint8_t>(**rroot, inventory_field), resizingbuf);
1000 flatbuffers::ResizeVector<uint8_t>(schema, 110, 50, *rinventory,
1002 // rinventory still valid, so lets read from it.
1003 TEST_EQ(rinventory->Get(10), 50);
1005 // For reflection uses not covered already, there is a more powerful way:
1006 // we can simply generate whatever object we want to add/modify in a
1007 // FlatBuffer of its own, then add that to an existing FlatBuffer:
1008 // As an example, let's add a string to an array of strings.
1009 // First, find our field:
1010 auto &testarrayofstring_field = *fields->LookupByKey("testarrayofstring");
1011 // Find the vector value:
1012 auto rtestarrayofstring = flatbuffers::piv(
1013 flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>(
1014 **rroot, testarrayofstring_field),
1016 // It's a vector of 2 strings, to which we add one more, initialized to
1018 flatbuffers::ResizeVector<flatbuffers::Offset<flatbuffers::String>>(
1019 schema, 3, 0, *rtestarrayofstring, &resizingbuf);
1020 // Here we just create a buffer that contans a single string, but this
1021 // could also be any complex set of tables and other values.
1022 flatbuffers::FlatBufferBuilder stringfbb;
1023 stringfbb.Finish(stringfbb.CreateString("hank"));
1024 // Add the contents of it to our existing FlatBuffer.
1025 // We do this last, so the pointer doesn't get invalidated (since it is
1026 // at the end of the buffer):
1027 auto string_ptr = flatbuffers::AddFlatBuffer(
1028 resizingbuf, stringfbb.GetBufferPointer(), stringfbb.GetSize());
1029 // Finally, set the new value in the vector.
1030 rtestarrayofstring->MutateOffset(2, string_ptr);
1031 TEST_EQ_STR(rtestarrayofstring->Get(0)->c_str(), "bob");
1032 TEST_EQ_STR(rtestarrayofstring->Get(2)->c_str(), "hank");
1033 // Test integrity of all resize operations above.
1034 flatbuffers::Verifier resize_verifier(
1035 reinterpret_cast<const uint8_t *>(flatbuffers::vector_data(resizingbuf)),
1036 resizingbuf.size());
1037 TEST_EQ(VerifyMonsterBuffer(resize_verifier), true);
1039 // Test buffer is valid using reflection as well
1040 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(),
1041 flatbuffers::vector_data(resizingbuf),
1042 resizingbuf.size()),
1045 // As an additional test, also set it on the name field.
1046 // Note: unlike the name change above, this just overwrites the offset,
1047 // rather than changing the string in-place.
1048 SetFieldT(*rroot, name_field, string_ptr);
1049 TEST_EQ_STR(GetFieldS(**rroot, name_field)->c_str(), "hank");
1051 // Using reflection, rather than mutating binary FlatBuffers, we can also copy
1052 // tables and other things out of other FlatBuffers into a FlatBufferBuilder,
1053 // either part or whole.
1054 flatbuffers::FlatBufferBuilder fbb;
1055 auto root_offset = flatbuffers::CopyTable(
1056 fbb, schema, *root_table, *flatbuffers::GetAnyRoot(flatbuf), true);
1057 fbb.Finish(root_offset, MonsterIdentifier());
1058 // Test that it was copied correctly:
1059 AccessFlatBufferTest(fbb.GetBufferPointer(), fbb.GetSize());
1061 // Test buffer is valid using reflection as well
1062 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(),
1063 fbb.GetBufferPointer(), fbb.GetSize()),
1067 void MiniReflectFlatBuffersTest(uint8_t *flatbuf) {
1069 flatbuffers::FlatBufferToString(flatbuf, Monster::MiniReflectTypeTable());
1073 "pos: { x: 1.0, y: 2.0, z: 3.0, test1: 0.0, test2: Red, test3: "
1074 "{ a: 10, b: 20 } }, "
1076 "name: \"MyMonster\", "
1077 "inventory: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "
1078 "test_type: Monster, "
1079 "test: { name: \"Fred\" }, "
1080 "test4: [ { a: 10, b: 20 }, { a: 30, b: 40 } ], "
1081 "testarrayofstring: [ \"bob\", \"fred\", \"bob\", \"fred\" ], "
1082 "testarrayoftables: [ { hp: 1000, name: \"Barney\" }, { name: \"Fred\" "
1084 "{ name: \"Wilma\" } ], "
1085 // TODO(wvo): should really print this nested buffer correctly.
1086 "testnestedflatbuffer: [ 20, 0, 0, 0, 77, 79, 78, 83, 12, 0, 12, 0, 0, "
1088 "4, 0, 6, 0, 8, 0, 12, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 13, 0, 0, 0, 78, "
1089 "101, 115, 116, 101, 100, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0 ], "
1090 "testarrayofstring2: [ \"jane\", \"mary\" ], "
1091 "testarrayofsortedstruct: [ { id: 1, distance: 10 }, "
1092 "{ id: 2, distance: 20 }, { id: 3, distance: 30 }, "
1093 "{ id: 4, distance: 40 } ], "
1094 "flex: [ 210, 4, 5, 2 ], "
1095 "test5: [ { a: 10, b: 20 }, { a: 30, b: 40 } ], "
1096 "vector_of_enums: [ Blue, Green ] "
1100 Vec3 vec(1, 2, 3, 1.5, Color_Red, test);
1101 flatbuffers::FlatBufferBuilder vec_builder;
1102 vec_builder.Finish(vec_builder.CreateStruct(vec));
1103 auto vec_buffer = vec_builder.Release();
1104 auto vec_str = flatbuffers::FlatBufferToString(vec_buffer.data(),
1105 Vec3::MiniReflectTypeTable());
1106 TEST_EQ_STR(vec_str.c_str(),
1107 "{ x: 1.0, y: 2.0, z: 3.0, test1: 1.5, test2: Red, test3: { a: "
1111 void MiniReflectFixedLengthArrayTest() {
1112 // VS10 does not support typed enums, exclude from tests
1113 #if !defined(_MSC_VER) || _MSC_VER >= 1700
1114 flatbuffers::FlatBufferBuilder fbb;
1115 MyGame::Example::ArrayStruct aStruct(2, 12, 1);
1116 auto aTable = MyGame::Example::CreateArrayTable(fbb, &aStruct);
1119 auto flatbuf = fbb.Release();
1120 auto s = flatbuffers::FlatBufferToString(
1121 flatbuf.data(), MyGame::Example::ArrayTableTypeTable());
1125 "b: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], "
1127 "d: [ { a: [ 0, 0 ], b: A, c: [ A, A ], d: [ 0, 0 ] }, "
1128 "{ a: [ 0, 0 ], b: A, c: [ A, A ], d: [ 0, 0 ] } ], "
1129 "e: 1, f: [ 0, 0 ] } "
1135 // Parse a .proto schema, output as .fbs
1136 void ParseProtoTest() {
1137 // load the .proto and the golden file from disk
1138 std::string protofile;
1139 std::string goldenfile;
1140 std::string goldenunionfile;
1142 flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(),
1146 flatbuffers::LoadFile((test_data_path + "prototest/test.golden").c_str(),
1147 false, &goldenfile),
1149 TEST_EQ(flatbuffers::LoadFile(
1150 (test_data_path + "prototest/test_union.golden").c_str(), false,
1154 flatbuffers::IDLOptions opts;
1155 opts.include_dependence_headers = false;
1156 opts.proto_mode = true;
1159 flatbuffers::Parser parser(opts);
1160 auto protopath = test_data_path + "prototest/";
1161 const char *include_directories[] = { protopath.c_str(), nullptr };
1162 TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true);
1165 auto fbs = flatbuffers::GenerateFBS(parser, "test");
1167 // Ensure generated file is parsable.
1168 flatbuffers::Parser parser2;
1169 TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
1170 TEST_EQ_STR(fbs.c_str(), goldenfile.c_str());
1172 // Parse proto with --oneof-union option.
1173 opts.proto_oneof_union = true;
1174 flatbuffers::Parser parser3(opts);
1175 TEST_EQ(parser3.Parse(protofile.c_str(), include_directories), true);
1178 auto fbs_union = flatbuffers::GenerateFBS(parser3, "test");
1180 // Ensure generated file is parsable.
1181 flatbuffers::Parser parser4;
1182 TEST_EQ(parser4.Parse(fbs_union.c_str(), nullptr), true);
1183 TEST_EQ_STR(fbs_union.c_str(), goldenunionfile.c_str());
1186 // Parse a .proto schema, output as .fbs
1187 void ParseProtoTestWithSuffix() {
1188 // load the .proto and the golden file from disk
1189 std::string protofile;
1190 std::string goldenfile;
1191 std::string goldenunionfile;
1193 flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(),
1196 TEST_EQ(flatbuffers::LoadFile(
1197 (test_data_path + "prototest/test_suffix.golden").c_str(), false,
1200 TEST_EQ(flatbuffers::LoadFile(
1201 (test_data_path + "prototest/test_union_suffix.golden").c_str(),
1202 false, &goldenunionfile),
1205 flatbuffers::IDLOptions opts;
1206 opts.include_dependence_headers = false;
1207 opts.proto_mode = true;
1208 opts.proto_namespace_suffix = "test_namespace_suffix";
1211 flatbuffers::Parser parser(opts);
1212 auto protopath = test_data_path + "prototest/";
1213 const char *include_directories[] = { protopath.c_str(), nullptr };
1214 TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true);
1217 auto fbs = flatbuffers::GenerateFBS(parser, "test");
1219 // Ensure generated file is parsable.
1220 flatbuffers::Parser parser2;
1221 TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
1222 TEST_EQ_STR(fbs.c_str(), goldenfile.c_str());
1224 // Parse proto with --oneof-union option.
1225 opts.proto_oneof_union = true;
1226 flatbuffers::Parser parser3(opts);
1227 TEST_EQ(parser3.Parse(protofile.c_str(), include_directories), true);
1230 auto fbs_union = flatbuffers::GenerateFBS(parser3, "test");
1232 // Ensure generated file is parsable.
1233 flatbuffers::Parser parser4;
1234 TEST_EQ(parser4.Parse(fbs_union.c_str(), nullptr), true);
1235 TEST_EQ_STR(fbs_union.c_str(), goldenunionfile.c_str());
1238 // Parse a .proto schema, output as .fbs
1239 void ParseProtoTestWithIncludes() {
1240 // load the .proto and the golden file from disk
1241 std::string protofile;
1242 std::string goldenfile;
1243 std::string goldenunionfile;
1244 std::string importprotofile;
1246 flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(),
1249 TEST_EQ(flatbuffers::LoadFile(
1250 (test_data_path + "prototest/imported.proto").c_str(), false,
1253 TEST_EQ(flatbuffers::LoadFile(
1254 (test_data_path + "prototest/test_include.golden").c_str(), false,
1257 TEST_EQ(flatbuffers::LoadFile(
1258 (test_data_path + "prototest/test_union_include.golden").c_str(),
1259 false, &goldenunionfile),
1262 flatbuffers::IDLOptions opts;
1263 opts.include_dependence_headers = true;
1264 opts.proto_mode = true;
1267 flatbuffers::Parser parser(opts);
1268 auto protopath = test_data_path + "prototest/";
1269 const char *include_directories[] = { protopath.c_str(), nullptr };
1270 TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true);
1273 auto fbs = flatbuffers::GenerateFBS(parser, "test");
1275 // Generate fbs from import.proto
1276 flatbuffers::Parser import_parser(opts);
1277 TEST_EQ(import_parser.Parse(importprotofile.c_str(), include_directories),
1279 auto import_fbs = flatbuffers::GenerateFBS(import_parser, "test");
1281 // Ensure generated file is parsable.
1282 flatbuffers::Parser parser2;
1284 parser2.Parse(import_fbs.c_str(), include_directories, "imported.fbs"),
1286 TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
1287 TEST_EQ_STR(fbs.c_str(), goldenfile.c_str());
1289 // Parse proto with --oneof-union option.
1290 opts.proto_oneof_union = true;
1291 flatbuffers::Parser parser3(opts);
1292 TEST_EQ(parser3.Parse(protofile.c_str(), include_directories), true);
1295 auto fbs_union = flatbuffers::GenerateFBS(parser3, "test");
1297 // Ensure generated file is parsable.
1298 flatbuffers::Parser parser4;
1299 TEST_EQ(parser4.Parse(import_fbs.c_str(), nullptr, "imported.fbs"), true);
1300 TEST_EQ(parser4.Parse(fbs_union.c_str(), nullptr), true);
1301 TEST_EQ_STR(fbs_union.c_str(), goldenunionfile.c_str());
1304 template<typename T>
1305 void CompareTableFieldValue(flatbuffers::Table *table,
1306 flatbuffers::voffset_t voffset, T val) {
1307 T read = table->GetField(voffset, static_cast<T>(0));
1311 // Low level stress/fuzz test: serialize/deserialize a variety of
1312 // different kinds of data in different combinations
1314 // Values we're testing against: chosen to ensure no bits get chopped
1315 // off anywhere, and also be different from eachother.
1316 const uint8_t bool_val = true;
1317 const int8_t char_val = -127; // 0x81
1318 const uint8_t uchar_val = 0xFF;
1319 const int16_t short_val = -32222; // 0x8222;
1320 const uint16_t ushort_val = 0xFEEE;
1321 const int32_t int_val = 0x83333333;
1322 const uint32_t uint_val = 0xFDDDDDDD;
1323 const int64_t long_val = 0x8444444444444444LL;
1324 const uint64_t ulong_val = 0xFCCCCCCCCCCCCCCCULL;
1325 const float float_val = 3.14159f;
1326 const double double_val = 3.14159265359;
1328 const int test_values_max = 11;
1329 const flatbuffers::voffset_t fields_per_object = 4;
1330 const int num_fuzz_objects = 10000; // The higher, the more thorough :)
1332 flatbuffers::FlatBufferBuilder builder;
1334 lcg_reset(); // Keep it deterministic.
1336 flatbuffers::uoffset_t objects[num_fuzz_objects];
1338 // Generate num_fuzz_objects random objects each consisting of
1339 // fields_per_object fields, each of a random type.
1340 for (int i = 0; i < num_fuzz_objects; i++) {
1341 auto start = builder.StartTable();
1342 for (flatbuffers::voffset_t f = 0; f < fields_per_object; f++) {
1343 int choice = lcg_rand() % test_values_max;
1344 auto off = flatbuffers::FieldIndexToOffset(f);
1346 case 0: builder.AddElement<uint8_t>(off, bool_val, 0); break;
1347 case 1: builder.AddElement<int8_t>(off, char_val, 0); break;
1348 case 2: builder.AddElement<uint8_t>(off, uchar_val, 0); break;
1349 case 3: builder.AddElement<int16_t>(off, short_val, 0); break;
1350 case 4: builder.AddElement<uint16_t>(off, ushort_val, 0); break;
1351 case 5: builder.AddElement<int32_t>(off, int_val, 0); break;
1352 case 6: builder.AddElement<uint32_t>(off, uint_val, 0); break;
1353 case 7: builder.AddElement<int64_t>(off, long_val, 0); break;
1354 case 8: builder.AddElement<uint64_t>(off, ulong_val, 0); break;
1355 case 9: builder.AddElement<float>(off, float_val, 0); break;
1356 case 10: builder.AddElement<double>(off, double_val, 0); break;
1359 objects[i] = builder.EndTable(start);
1361 builder.PreAlign<flatbuffers::largest_scalar_t>(0); // Align whole buffer.
1363 lcg_reset(); // Reset.
1365 uint8_t *eob = builder.GetCurrentBufferPointer() + builder.GetSize();
1367 // Test that all objects we generated are readable and return the
1368 // expected values. We generate random objects in the same order
1369 // so this is deterministic.
1370 for (int i = 0; i < num_fuzz_objects; i++) {
1371 auto table = reinterpret_cast<flatbuffers::Table *>(eob - objects[i]);
1372 for (flatbuffers::voffset_t f = 0; f < fields_per_object; f++) {
1373 int choice = lcg_rand() % test_values_max;
1374 flatbuffers::voffset_t off = flatbuffers::FieldIndexToOffset(f);
1376 case 0: CompareTableFieldValue(table, off, bool_val); break;
1377 case 1: CompareTableFieldValue(table, off, char_val); break;
1378 case 2: CompareTableFieldValue(table, off, uchar_val); break;
1379 case 3: CompareTableFieldValue(table, off, short_val); break;
1380 case 4: CompareTableFieldValue(table, off, ushort_val); break;
1381 case 5: CompareTableFieldValue(table, off, int_val); break;
1382 case 6: CompareTableFieldValue(table, off, uint_val); break;
1383 case 7: CompareTableFieldValue(table, off, long_val); break;
1384 case 8: CompareTableFieldValue(table, off, ulong_val); break;
1385 case 9: CompareTableFieldValue(table, off, float_val); break;
1386 case 10: CompareTableFieldValue(table, off, double_val); break;
1392 // High level stress/fuzz test: generate a big schema and
1393 // matching json data in random combinations, then parse both,
1394 // generate json back from the binary, and compare with the original.
1396 lcg_reset(); // Keep it deterministic.
1398 const int num_definitions = 30;
1399 const int num_struct_definitions = 5; // Subset of num_definitions.
1400 const int fields_per_definition = 15;
1401 const int instances_per_definition = 5;
1402 const int deprecation_rate = 10; // 1 in deprecation_rate fields will
1405 std::string schema = "namespace test;\n\n";
1408 std::string instances[instances_per_definition];
1410 // Since we're generating schema and corresponding data in tandem,
1411 // this convenience function adds strings to both at once.
1412 static void Add(RndDef (&definitions_l)[num_definitions],
1413 std::string &schema_l, const int instances_per_definition_l,
1414 const char *schema_add, const char *instance_add,
1416 schema_l += schema_add;
1417 for (int i = 0; i < instances_per_definition_l; i++)
1418 definitions_l[definition].instances[i] += instance_add;
1423 #define AddToSchemaAndInstances(schema_add, instance_add) \
1424 RndDef::Add(definitions, schema, instances_per_definition, \
1425 schema_add, instance_add, definition)
1428 RndDef::Add(definitions, schema, instances_per_definition, \
1429 "byte", "1", definition)
1432 RndDef definitions[num_definitions];
1434 // We are going to generate num_definitions, the first
1435 // num_struct_definitions will be structs, the rest tables. For each
1436 // generate random fields, some of which may be struct/table types
1437 // referring to previously generated structs/tables.
1438 // Simultanenously, we generate instances_per_definition JSON data
1439 // definitions, which will have identical structure to the schema
1440 // being generated. We generate multiple instances such that when creating
1441 // hierarchy, we get some variety by picking one randomly.
1442 for (int definition = 0; definition < num_definitions; definition++) {
1443 std::string definition_name = "D" + flatbuffers::NumToString(definition);
1445 bool is_struct = definition < num_struct_definitions;
1447 AddToSchemaAndInstances(
1448 ((is_struct ? "struct " : "table ") + definition_name + " {\n").c_str(),
1451 for (int field = 0; field < fields_per_definition; field++) {
1452 const bool is_last_field = field == fields_per_definition - 1;
1454 // Deprecate 1 in deprecation_rate fields. Only table fields can be
1456 // Don't deprecate the last field to avoid dangling commas in JSON.
1457 const bool deprecated =
1458 !is_struct && !is_last_field && (lcg_rand() % deprecation_rate == 0);
1460 std::string field_name = "f" + flatbuffers::NumToString(field);
1461 AddToSchemaAndInstances((" " + field_name + ":").c_str(),
1462 deprecated ? "" : (field_name + ": ").c_str());
1463 // Pick random type:
1464 auto base_type = static_cast<flatbuffers::BaseType>(
1465 lcg_rand() % (flatbuffers::BASE_TYPE_UNION + 1));
1466 switch (base_type) {
1467 case flatbuffers::BASE_TYPE_STRING:
1469 Dummy(); // No strings in structs.
1471 AddToSchemaAndInstances("string", deprecated ? "" : "\"hi\"");
1474 case flatbuffers::BASE_TYPE_VECTOR:
1476 Dummy(); // No vectors in structs.
1478 AddToSchemaAndInstances("[ubyte]",
1479 deprecated ? "" : "[\n0,\n1,\n255\n]");
1482 case flatbuffers::BASE_TYPE_NONE:
1483 case flatbuffers::BASE_TYPE_UTYPE:
1484 case flatbuffers::BASE_TYPE_STRUCT:
1485 case flatbuffers::BASE_TYPE_UNION:
1487 // Pick a random previous definition and random data instance of
1489 int defref = lcg_rand() % definition;
1490 int instance = lcg_rand() % instances_per_definition;
1491 AddToSchemaAndInstances(
1492 ("D" + flatbuffers::NumToString(defref)).c_str(),
1494 : definitions[defref].instances[instance].c_str());
1496 // If this is the first definition, we have no definition we can
1501 case flatbuffers::BASE_TYPE_BOOL:
1502 AddToSchemaAndInstances(
1503 "bool", deprecated ? "" : (lcg_rand() % 2 ? "true" : "false"));
1505 case flatbuffers::BASE_TYPE_ARRAY:
1507 AddToSchemaAndInstances(
1509 deprecated ? "" : "255"); // No fixed-length arrays in tables.
1511 AddToSchemaAndInstances("[int:3]", deprecated ? "" : "[\n,\n,\n]");
1515 // All the scalar types.
1516 schema += flatbuffers::kTypeNames[base_type];
1519 // We want each instance to use its own random value.
1520 for (int inst = 0; inst < instances_per_definition; inst++)
1521 definitions[definition].instances[inst] +=
1522 flatbuffers::IsFloat(base_type)
1523 ? flatbuffers::NumToString<double>(lcg_rand() % 128)
1525 : flatbuffers::NumToString<int>(lcg_rand() % 128).c_str();
1528 AddToSchemaAndInstances(deprecated ? "(deprecated);\n" : ";\n",
1529 deprecated ? "" : is_last_field ? "\n" : ",\n");
1531 AddToSchemaAndInstances("}\n\n", "}");
1534 schema += "root_type D" + flatbuffers::NumToString(num_definitions - 1);
1537 flatbuffers::Parser parser;
1539 // Will not compare against the original if we don't write defaults
1540 parser.builder_.ForceDefaults(true);
1542 // Parse the schema, parse the generated data, then generate text back
1543 // from the binary and compare against the original.
1544 TEST_EQ(parser.Parse(schema.c_str()), true);
1546 const std::string &json =
1547 definitions[num_definitions - 1].instances[0] + "\n";
1549 TEST_EQ(parser.Parse(json.c_str()), true);
1551 std::string jsongen;
1552 parser.opts.indent_step = 0;
1554 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
1555 TEST_EQ(result, true);
1557 if (jsongen != json) {
1558 // These strings are larger than a megabyte, so we show the bytes around
1559 // the first bytes that are different rather than the whole string.
1560 size_t len = std::min(json.length(), jsongen.length());
1561 for (size_t i = 0; i < len; i++) {
1562 if (json[i] != jsongen[i]) {
1563 i -= std::min(static_cast<size_t>(10), i); // show some context;
1564 size_t end = std::min(len, i + 20);
1565 for (; i < end; i++)
1566 TEST_OUTPUT_LINE("at %d: found \"%c\", expected \"%c\"\n",
1567 static_cast<int>(i), jsongen[i], json[i]);
1571 TEST_NOTNULL(nullptr); //-V501 (this comment supresses CWE-570 warning)
1575 #ifdef FLATBUFFERS_TEST_VERBOSE
1576 TEST_OUTPUT_LINE("%dk schema tested with %dk of json\n",
1577 static_cast<int>(schema.length() / 1024),
1578 static_cast<int>(json.length() / 1024));
1583 // Test that parser errors are actually generated.
1584 void TestError_(const char *src, const char *error_substr, bool strict_json,
1585 const char *file, int line, const char *func) {
1586 flatbuffers::IDLOptions opts;
1587 opts.strict_json = strict_json;
1588 flatbuffers::Parser parser(opts);
1589 if (parser.Parse(src)) {
1590 TestFail("true", "false",
1591 ("parser.Parse(\"" + std::string(src) + "\")").c_str(), file, line,
1593 } else if (!strstr(parser.error_.c_str(), error_substr)) {
1594 TestFail(error_substr, parser.error_.c_str(),
1595 ("parser.Parse(\"" + std::string(src) + "\")").c_str(), file, line,
1600 void TestError_(const char *src, const char *error_substr, const char *file,
1601 int line, const char *func) {
1602 TestError_(src, error_substr, false, file, line, func);
1606 # define TestError(src, ...) \
1607 TestError_(src, __VA_ARGS__, __FILE__, __LINE__, __FUNCTION__)
1609 # define TestError(src, ...) \
1610 TestError_(src, __VA_ARGS__, __FILE__, __LINE__, __PRETTY_FUNCTION__)
1613 // Test that parsing errors occur as we'd expect.
1614 // Also useful for coverage, making sure these paths are run.
1616 // In order they appear in idl_parser.cpp
1617 TestError("table X { Y:byte; } root_type X; { Y: 999 }", "does not fit");
1618 TestError("\"\0", "illegal");
1619 TestError("\"\\q", "escape code");
1620 TestError("table ///", "documentation");
1621 TestError("@", "illegal");
1622 TestError("table 1", "expecting");
1623 TestError("table X { Y:[[int]]; }", "nested vector");
1624 TestError("table X { Y:1; }", "illegal type");
1625 TestError("table X { Y:int; Y:int; }", "field already");
1626 TestError("table Y {} table X { Y:int; }", "same as table");
1627 TestError("struct X { Y:string; }", "only scalar");
1628 TestError("table X { Y:string = \"\"; }", "default values");
1629 TestError("struct X { a:uint = 42; }", "default values");
1630 TestError("enum Y:byte { Z = 1 } table X { y:Y; }", "not part of enum");
1631 TestError("struct X { Y:int (deprecated); }", "deprecate");
1632 TestError("union Z { X } table X { Y:Z; } root_type X; { Y: {}, A:1 }",
1633 "missing type field");
1634 TestError("union Z { X } table X { Y:Z; } root_type X; { Y_type: 99, Y: {",
1636 TestError("table X { Y:int; } root_type X; { Z:", "unknown field");
1637 TestError("table X { Y:int; } root_type X; { Y:", "string constant", true);
1638 TestError("table X { Y:int; } root_type X; { \"Y\":1, }", "string constant",
1641 "struct X { Y:int; Z:int; } table W { V:X; } root_type W; "
1644 TestError("enum E:byte { A } table X { Y:E; } root_type X; { Y:U }",
1645 "unknown enum value");
1646 TestError("table X { Y:byte; } root_type X; { Y:; }", "starting");
1647 TestError("enum X:byte { Y } enum X {", "enum already");
1648 TestError("enum X:float {}", "underlying");
1649 TestError("enum X:byte { Y, Y }", "value already");
1650 TestError("enum X:byte { Y=2, Z=2 }", "unique");
1651 TestError("table X { Y:int; } table X {", "datatype already");
1652 TestError("struct X (force_align: 7) { Y:int; }", "force_align");
1653 TestError("struct X {}", "size 0");
1654 TestError("{}", "no root");
1655 TestError("table X { Y:byte; } root_type X; { Y:1 } { Y:1 }", "end of file");
1656 TestError("table X { Y:byte; } root_type X; { Y:1 } table Y{ Z:int }",
1658 TestError("root_type X;", "unknown root");
1659 TestError("struct X { Y:int; } root_type X;", "a table");
1660 TestError("union X { Y }", "referenced");
1661 TestError("union Z { X } struct X { Y:int; }", "only tables");
1662 TestError("table X { Y:[int]; YLength:int; }", "clash");
1663 TestError("table X { Y:byte; } root_type X; { Y:1, Y:2 }", "more than once");
1664 // float to integer conversion is forbidden
1665 TestError("table X { Y:int; } root_type X; { Y:1.0 }", "float");
1666 TestError("table X { Y:bool; } root_type X; { Y:1.0 }", "float");
1667 TestError("enum X:bool { Y = true }", "must be integral");
1668 // Array of non-scalar
1669 TestError("table X { x:int; } struct Y { y:[X:2]; }",
1670 "may contain only scalar or struct fields");
1671 // Non-snake case field names
1672 TestError("table X { Y: int; } root_type Y: {Y:1.0}", "snake_case");
1675 template<typename T>
1676 T TestValue(const char *json, const char *type_name,
1677 const char *decls = nullptr) {
1678 flatbuffers::Parser parser;
1679 parser.builder_.ForceDefaults(true); // return defaults
1680 auto check_default = json ? false : true;
1681 if (check_default) { parser.opts.output_default_scalars_in_json = true; }
1683 std::string schema = std::string(decls ? decls : "") + "\n" +
1684 "table X { y:" + std::string(type_name) +
1686 auto schema_done = parser.Parse(schema.c_str());
1687 TEST_EQ_STR(parser.error_.c_str(), "");
1688 TEST_EQ(schema_done, true);
1690 auto done = parser.Parse(check_default ? "{}" : json);
1691 TEST_EQ_STR(parser.error_.c_str(), "");
1692 TEST_EQ(done, true);
1694 // Check with print.
1695 std::string print_back;
1696 parser.opts.indent_step = -1;
1697 TEST_EQ(GenerateText(parser, parser.builder_.GetBufferPointer(), &print_back),
1699 // restore value from its default
1700 if (check_default) { TEST_EQ(parser.Parse(print_back.c_str()), true); }
1702 auto root = flatbuffers::GetRoot<flatbuffers::Table>(
1703 parser.builder_.GetBufferPointer());
1704 return root->GetField<T>(flatbuffers::FieldIndexToOffset(0), 0);
1707 bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; }
1709 // Additional parser testing not covered elsewhere.
1711 // Test scientific notation numbers.
1713 FloatCompare(TestValue<float>("{ y:0.0314159e+2 }", "float"), 3.14159f),
1716 TEST_EQ(FloatCompare(TestValue<float>("{ y:\"0.0314159e+2\" }", "float"),
1720 // Test conversion functions.
1721 TEST_EQ(FloatCompare(TestValue<float>("{ y:cos(rad(180)) }", "float"), -1),
1724 // int embedded to string
1725 TEST_EQ(TestValue<int>("{ y:\"-876\" }", "int=-123"), -876);
1726 TEST_EQ(TestValue<int>("{ y:\"876\" }", "int=-123"), 876);
1728 // Test negative hex constant.
1729 TEST_EQ(TestValue<int>("{ y:-0x8ea0 }", "int=-0x8ea0"), -36512);
1730 TEST_EQ(TestValue<int>(nullptr, "int=-0x8ea0"), -36512);
1732 // positive hex constant
1733 TEST_EQ(TestValue<int>("{ y:0x1abcdef }", "int=0x1"), 0x1abcdef);
1734 // with optional '+' sign
1735 TEST_EQ(TestValue<int>("{ y:+0x1abcdef }", "int=+0x1"), 0x1abcdef);
1737 TEST_EQ(TestValue<int>("{ y:\"0x1abcdef\" }", "int=+0x1"), 0x1abcdef);
1739 // Make sure we do unsigned 64bit correctly.
1740 TEST_EQ(TestValue<uint64_t>("{ y:12335089644688340133 }", "ulong"),
1741 12335089644688340133ULL);
1744 TEST_EQ(TestValue<bool>("{ y:\"false\" }", "bool=true"), false);
1745 TEST_EQ(TestValue<bool>("{ y:\"true\" }", "bool=\"true\""), true);
1746 TEST_EQ(TestValue<bool>("{ y:'false' }", "bool=true"), false);
1747 TEST_EQ(TestValue<bool>("{ y:'true' }", "bool=\"true\""), true);
1749 // check comments before and after json object
1750 TEST_EQ(TestValue<int>("/*before*/ { y:1 } /*after*/", "int"), 1);
1751 TEST_EQ(TestValue<int>("//before \n { y:1 } //after", "int"), 1);
1754 void NestedListTest() {
1755 flatbuffers::Parser parser1;
1756 TEST_EQ(parser1.Parse("struct Test { a:short; b:byte; } table T { F:[Test]; }"
1758 "{ F:[ [10,20], [30,40]] }"),
1762 void EnumStringsTest() {
1763 flatbuffers::Parser parser1;
1764 TEST_EQ(parser1.Parse("enum E:byte { A, B, C } table T { F:[E]; }"
1766 "{ F:[ A, B, \"C\", \"A B C\" ] }"),
1768 flatbuffers::Parser parser2;
1769 TEST_EQ(parser2.Parse("enum E:byte { A, B, C } table T { F:[int]; }"
1771 "{ F:[ \"E.C\", \"E.A E.B E.C\" ] }"),
1773 // unsigned bit_flags
1774 flatbuffers::Parser parser3;
1776 parser3.Parse("enum E:uint16 (bit_flags) { F0, F07=7, F08, F14=14, F15 }"
1777 " table T { F: E = \"F15 F08\"; }"
1782 void EnumNamesTest() {
1783 TEST_EQ_STR("Red", EnumNameColor(Color_Red));
1784 TEST_EQ_STR("Green", EnumNameColor(Color_Green));
1785 TEST_EQ_STR("Blue", EnumNameColor(Color_Blue));
1786 // Check that Color to string don't crash while decode a mixture of Colors.
1787 // 1) Example::Color enum is enum with unfixed underlying type.
1788 // 2) Valid enum range: [0; 2^(ceil(log2(Color_ANY))) - 1].
1789 // Consequence: A value is out of this range will lead to UB (since C++17).
1790 // For details see C++17 standard or explanation on the SO:
1791 // stackoverflow.com/questions/18195312/what-happens-if-you-static-cast-invalid-value-to-enum-class
1792 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(0)));
1793 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY - 1)));
1794 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY + 1)));
1797 void EnumOutOfRangeTest() {
1798 TestError("enum X:byte { Y = 128 }", "enum value does not fit");
1799 TestError("enum X:byte { Y = -129 }", "enum value does not fit");
1800 TestError("enum X:byte { Y = 126, Z0, Z1 }", "enum value does not fit");
1801 TestError("enum X:ubyte { Y = -1 }", "enum value does not fit");
1802 TestError("enum X:ubyte { Y = 256 }", "enum value does not fit");
1803 TestError("enum X:ubyte { Y = 255, Z }", "enum value does not fit");
1804 TestError("table Y{} union X { Y = -1 }", "enum value does not fit");
1805 TestError("table Y{} union X { Y = 256 }", "enum value does not fit");
1806 TestError("table Y{} union X { Y = 255, Z:Y }", "enum value does not fit");
1807 TestError("enum X:int { Y = -2147483649 }", "enum value does not fit");
1808 TestError("enum X:int { Y = 2147483648 }", "enum value does not fit");
1809 TestError("enum X:uint { Y = -1 }", "enum value does not fit");
1810 TestError("enum X:uint { Y = 4294967297 }", "enum value does not fit");
1811 TestError("enum X:long { Y = 9223372036854775808 }", "does not fit");
1812 TestError("enum X:long { Y = 9223372036854775807, Z }",
1813 "enum value does not fit");
1814 TestError("enum X:ulong { Y = -1 }", "does not fit");
1815 TestError("enum X:ubyte (bit_flags) { Y=8 }", "bit flag out");
1816 TestError("enum X:byte (bit_flags) { Y=7 }", "must be unsigned"); // -128
1817 // bit_flgs out of range
1818 TestError("enum X:ubyte (bit_flags) { Y0,Y1,Y2,Y3,Y4,Y5,Y6,Y7,Y8 }",
1822 void EnumValueTest() {
1823 // json: "{ Y:0 }", schema: table X { y: "E"}
1824 // 0 in enum (V=0) E then Y=0 is valid.
1825 TEST_EQ(TestValue<int>("{ y:0 }", "E", "enum E:int { V }"), 0);
1826 TEST_EQ(TestValue<int>("{ y:V }", "E", "enum E:int { V }"), 0);
1827 // A default value of Y is 0.
1828 TEST_EQ(TestValue<int>("{ }", "E", "enum E:int { V }"), 0);
1829 TEST_EQ(TestValue<int>("{ y:5 }", "E=V", "enum E:int { V=5 }"), 5);
1830 // Generate json with defaults and check.
1831 TEST_EQ(TestValue<int>(nullptr, "E=V", "enum E:int { V=5 }"), 5);
1833 TEST_EQ(TestValue<int>("{ y:5 }", "E", "enum E:int { Z, V=5 }"), 5);
1834 TEST_EQ(TestValue<int>("{ y:5 }", "E=V", "enum E:int { Z, V=5 }"), 5);
1835 // Generate json with defaults and check.
1836 TEST_EQ(TestValue<int>(nullptr, "E", "enum E:int { Z, V=5 }"), 0);
1837 TEST_EQ(TestValue<int>(nullptr, "E=V", "enum E:int { Z, V=5 }"), 5);
1839 TEST_EQ(TestValue<uint64_t>(nullptr, "E=V",
1840 "enum E:ulong { V = 13835058055282163712 }"),
1841 13835058055282163712ULL);
1842 TEST_EQ(TestValue<uint64_t>(nullptr, "E=V",
1843 "enum E:ulong { V = 18446744073709551615 }"),
1844 18446744073709551615ULL);
1845 // Assign non-enum value to enum field. Is it right?
1846 TEST_EQ(TestValue<int>("{ y:7 }", "E", "enum E:int { V = 0 }"), 7);
1847 // Check that non-ascending values are valid.
1848 TEST_EQ(TestValue<int>("{ y:5 }", "E=V", "enum E:int { Z=10, V=5 }"), 5);
1851 void IntegerOutOfRangeTest() {
1852 TestError("table T { F:byte; } root_type T; { F:128 }",
1853 "constant does not fit");
1854 TestError("table T { F:byte; } root_type T; { F:-129 }",
1855 "constant does not fit");
1856 TestError("table T { F:ubyte; } root_type T; { F:256 }",
1857 "constant does not fit");
1858 TestError("table T { F:ubyte; } root_type T; { F:-1 }",
1859 "constant does not fit");
1860 TestError("table T { F:short; } root_type T; { F:32768 }",
1861 "constant does not fit");
1862 TestError("table T { F:short; } root_type T; { F:-32769 }",
1863 "constant does not fit");
1864 TestError("table T { F:ushort; } root_type T; { F:65536 }",
1865 "constant does not fit");
1866 TestError("table T { F:ushort; } root_type T; { F:-1 }",
1867 "constant does not fit");
1868 TestError("table T { F:int; } root_type T; { F:2147483648 }",
1869 "constant does not fit");
1870 TestError("table T { F:int; } root_type T; { F:-2147483649 }",
1871 "constant does not fit");
1872 TestError("table T { F:uint; } root_type T; { F:4294967296 }",
1873 "constant does not fit");
1874 TestError("table T { F:uint; } root_type T; { F:-1 }",
1875 "constant does not fit");
1876 // Check fixed width aliases
1877 TestError("table X { Y:uint8; } root_type X; { Y: -1 }", "does not fit");
1878 TestError("table X { Y:uint8; } root_type X; { Y: 256 }", "does not fit");
1879 TestError("table X { Y:uint16; } root_type X; { Y: -1 }", "does not fit");
1880 TestError("table X { Y:uint16; } root_type X; { Y: 65536 }", "does not fit");
1881 TestError("table X { Y:uint32; } root_type X; { Y: -1 }", "");
1882 TestError("table X { Y:uint32; } root_type X; { Y: 4294967296 }",
1884 TestError("table X { Y:uint64; } root_type X; { Y: -1 }", "");
1885 TestError("table X { Y:uint64; } root_type X; { Y: -9223372036854775809 }",
1887 TestError("table X { Y:uint64; } root_type X; { Y: 18446744073709551616 }",
1890 TestError("table X { Y:int8; } root_type X; { Y: -129 }", "does not fit");
1891 TestError("table X { Y:int8; } root_type X; { Y: 128 }", "does not fit");
1892 TestError("table X { Y:int16; } root_type X; { Y: -32769 }", "does not fit");
1893 TestError("table X { Y:int16; } root_type X; { Y: 32768 }", "does not fit");
1894 TestError("table X { Y:int32; } root_type X; { Y: -2147483649 }", "");
1895 TestError("table X { Y:int32; } root_type X; { Y: 2147483648 }",
1897 TestError("table X { Y:int64; } root_type X; { Y: -9223372036854775809 }",
1899 TestError("table X { Y:int64; } root_type X; { Y: 9223372036854775808 }",
1901 // check out-of-int64 as int8
1902 TestError("table X { Y:int8; } root_type X; { Y: -9223372036854775809 }",
1904 TestError("table X { Y:int8; } root_type X; { Y: 9223372036854775808 }",
1907 // Check default values
1908 TestError("table X { Y:int64=-9223372036854775809; } root_type X; {}",
1910 TestError("table X { Y:int64= 9223372036854775808; } root_type X; {}",
1912 TestError("table X { Y:uint64; } root_type X; { Y: -1 }", "");
1913 TestError("table X { Y:uint64=-9223372036854775809; } root_type X; {}",
1915 TestError("table X { Y:uint64= 18446744073709551616; } root_type X; {}",
1919 void IntegerBoundaryTest() {
1920 // Check numerical compatibility with non-C++ languages.
1921 // By the C++ standard, std::numerical_limits<int64_t>::min() ==
1922 // -9223372036854775807 (-2^63+1) or less* The Flatbuffers grammar and most of
1923 // the languages (C#, Java, Rust) expect that minimum values are: -128,
1924 // -32768,.., -9223372036854775808. Since C++20,
1925 // static_cast<int64>(0x8000000000000000ULL) is well-defined two's complement
1926 // cast. Therefore -9223372036854775808 should be valid negative value.
1927 TEST_EQ(flatbuffers::numeric_limits<int8_t>::min(), -128);
1928 TEST_EQ(flatbuffers::numeric_limits<int8_t>::max(), 127);
1929 TEST_EQ(flatbuffers::numeric_limits<int16_t>::min(), -32768);
1930 TEST_EQ(flatbuffers::numeric_limits<int16_t>::max(), 32767);
1931 TEST_EQ(flatbuffers::numeric_limits<int32_t>::min() + 1, -2147483647);
1932 TEST_EQ(flatbuffers::numeric_limits<int32_t>::max(), 2147483647ULL);
1933 TEST_EQ(flatbuffers::numeric_limits<int64_t>::min() + 1LL,
1934 -9223372036854775807LL);
1935 TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(), 9223372036854775807ULL);
1936 TEST_EQ(flatbuffers::numeric_limits<uint8_t>::max(), 255);
1937 TEST_EQ(flatbuffers::numeric_limits<uint16_t>::max(), 65535);
1938 TEST_EQ(flatbuffers::numeric_limits<uint32_t>::max(), 4294967295ULL);
1939 TEST_EQ(flatbuffers::numeric_limits<uint64_t>::max(),
1940 18446744073709551615ULL);
1942 TEST_EQ(TestValue<int8_t>("{ y:127 }", "byte"), 127);
1943 TEST_EQ(TestValue<int8_t>("{ y:-128 }", "byte"), -128);
1944 TEST_EQ(TestValue<uint8_t>("{ y:255 }", "ubyte"), 255);
1945 TEST_EQ(TestValue<uint8_t>("{ y:0 }", "ubyte"), 0);
1946 TEST_EQ(TestValue<int16_t>("{ y:32767 }", "short"), 32767);
1947 TEST_EQ(TestValue<int16_t>("{ y:-32768 }", "short"), -32768);
1948 TEST_EQ(TestValue<uint16_t>("{ y:65535 }", "ushort"), 65535);
1949 TEST_EQ(TestValue<uint16_t>("{ y:0 }", "ushort"), 0);
1950 TEST_EQ(TestValue<int32_t>("{ y:2147483647 }", "int"), 2147483647);
1951 TEST_EQ(TestValue<int32_t>("{ y:-2147483648 }", "int") + 1, -2147483647);
1952 TEST_EQ(TestValue<uint32_t>("{ y:4294967295 }", "uint"), 4294967295);
1953 TEST_EQ(TestValue<uint32_t>("{ y:0 }", "uint"), 0);
1954 TEST_EQ(TestValue<int64_t>("{ y:9223372036854775807 }", "long"),
1955 9223372036854775807LL);
1956 TEST_EQ(TestValue<int64_t>("{ y:-9223372036854775808 }", "long") + 1LL,
1957 -9223372036854775807LL);
1958 TEST_EQ(TestValue<uint64_t>("{ y:18446744073709551615 }", "ulong"),
1959 18446744073709551615ULL);
1960 TEST_EQ(TestValue<uint64_t>("{ y:0 }", "ulong"), 0);
1961 TEST_EQ(TestValue<uint64_t>("{ y: 18446744073709551615 }", "uint64"),
1962 18446744073709551615ULL);
1963 // check that the default works
1964 TEST_EQ(TestValue<uint64_t>(nullptr, "uint64 = 18446744073709551615"),
1965 18446744073709551615ULL);
1968 void ValidFloatTest() {
1969 // check rounding to infinity
1970 TEST_EQ(TestValue<float>("{ y:+3.4029e+38 }", "float"), +infinityf);
1971 TEST_EQ(TestValue<float>("{ y:-3.4029e+38 }", "float"), -infinityf);
1972 TEST_EQ(TestValue<double>("{ y:+1.7977e+308 }", "double"), +infinityd);
1973 TEST_EQ(TestValue<double>("{ y:-1.7977e+308 }", "double"), -infinityd);
1976 FloatCompare(TestValue<float>("{ y:0.0314159e+2 }", "float"), 3.14159f),
1979 TEST_EQ(FloatCompare(TestValue<float>("{ y:\" 0.0314159e+2 \" }", "float"),
1983 TEST_EQ(TestValue<float>("{ y:1 }", "float"), 1.0f);
1984 TEST_EQ(TestValue<float>("{ y:1.0 }", "float"), 1.0f);
1985 TEST_EQ(TestValue<float>("{ y:1. }", "float"), 1.0f);
1986 TEST_EQ(TestValue<float>("{ y:+1. }", "float"), 1.0f);
1987 TEST_EQ(TestValue<float>("{ y:-1. }", "float"), -1.0f);
1988 TEST_EQ(TestValue<float>("{ y:1.e0 }", "float"), 1.0f);
1989 TEST_EQ(TestValue<float>("{ y:1.e+0 }", "float"), 1.0f);
1990 TEST_EQ(TestValue<float>("{ y:1.e-0 }", "float"), 1.0f);
1991 TEST_EQ(TestValue<float>("{ y:0.125 }", "float"), 0.125f);
1992 TEST_EQ(TestValue<float>("{ y:.125 }", "float"), 0.125f);
1993 TEST_EQ(TestValue<float>("{ y:-.125 }", "float"), -0.125f);
1994 TEST_EQ(TestValue<float>("{ y:+.125 }", "float"), +0.125f);
1995 TEST_EQ(TestValue<float>("{ y:5 }", "float"), 5.0f);
1996 TEST_EQ(TestValue<float>("{ y:\"5\" }", "float"), 5.0f);
1998 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
1999 // Old MSVC versions may have problem with this check.
2000 // https://www.exploringbinary.com/visual-c-plus-plus-strtod-still-broken/
2001 TEST_EQ(TestValue<double>("{ y:6.9294956446009195e15 }", "double"),
2002 6929495644600920.0);
2004 TEST_EQ(std::isnan(TestValue<double>("{ y:nan }", "double")), true);
2005 TEST_EQ(std::isnan(TestValue<float>("{ y:nan }", "float")), true);
2006 TEST_EQ(std::isnan(TestValue<float>("{ y:\"nan\" }", "float")), true);
2007 TEST_EQ(std::isnan(TestValue<float>("{ y:+nan }", "float")), true);
2008 TEST_EQ(std::isnan(TestValue<float>("{ y:-nan }", "float")), true);
2009 TEST_EQ(std::isnan(TestValue<float>(nullptr, "float=nan")), true);
2010 TEST_EQ(std::isnan(TestValue<float>(nullptr, "float=-nan")), true);
2012 TEST_EQ(TestValue<float>("{ y:inf }", "float"), infinityf);
2013 TEST_EQ(TestValue<float>("{ y:\"inf\" }", "float"), infinityf);
2014 TEST_EQ(TestValue<float>("{ y:+inf }", "float"), infinityf);
2015 TEST_EQ(TestValue<float>("{ y:-inf }", "float"), -infinityf);
2016 TEST_EQ(TestValue<float>(nullptr, "float=inf"), infinityf);
2017 TEST_EQ(TestValue<float>(nullptr, "float=-inf"), -infinityf);
2019 "{ y: [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, "
2023 "{ y: [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, "
2027 // Test binary format of float point.
2028 // https://en.cppreference.com/w/cpp/language/floating_literal
2029 // 0x11.12p-1 = (1*16^1 + 2*16^0 + 3*16^-1 + 4*16^-2) * 2^-1 =
2030 TEST_EQ(TestValue<double>("{ y:0x12.34p-1 }", "double"), 9.1015625);
2031 // hex fraction 1.2 (decimal 1.125) scaled by 2^3, that is 9.0
2032 TEST_EQ(TestValue<float>("{ y:-0x0.2p0 }", "float"), -0.125f);
2033 TEST_EQ(TestValue<float>("{ y:-0x.2p1 }", "float"), -0.25f);
2034 TEST_EQ(TestValue<float>("{ y:0x1.2p3 }", "float"), 9.0f);
2035 TEST_EQ(TestValue<float>("{ y:0x10.1p0 }", "float"), 16.0625f);
2036 TEST_EQ(TestValue<double>("{ y:0x1.2p3 }", "double"), 9.0);
2037 TEST_EQ(TestValue<double>("{ y:0x10.1p0 }", "double"), 16.0625);
2038 TEST_EQ(TestValue<double>("{ y:0xC.68p+2 }", "double"), 49.625);
2039 TestValue<double>("{ y: [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[double]");
2040 TestValue<float>("{ y: [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[float]");
2042 #else // FLATBUFFERS_HAS_NEW_STRTOD
2043 TEST_OUTPUT_LINE("FLATBUFFERS_HAS_NEW_STRTOD tests skipped");
2044 #endif // !FLATBUFFERS_HAS_NEW_STRTOD
2047 void InvalidFloatTest() {
2048 auto invalid_msg = "invalid number";
2049 auto comma_msg = "expecting: ,";
2050 TestError("table T { F:float; } root_type T; { F:1,0 }", "");
2051 TestError("table T { F:float; } root_type T; { F:. }", "");
2052 TestError("table T { F:float; } root_type T; { F:- }", invalid_msg);
2053 TestError("table T { F:float; } root_type T; { F:+ }", invalid_msg);
2054 TestError("table T { F:float; } root_type T; { F:-. }", invalid_msg);
2055 TestError("table T { F:float; } root_type T; { F:+. }", invalid_msg);
2056 TestError("table T { F:float; } root_type T; { F:.e }", "");
2057 TestError("table T { F:float; } root_type T; { F:-e }", invalid_msg);
2058 TestError("table T { F:float; } root_type T; { F:+e }", invalid_msg);
2059 TestError("table T { F:float; } root_type T; { F:-.e }", invalid_msg);
2060 TestError("table T { F:float; } root_type T; { F:+.e }", invalid_msg);
2061 TestError("table T { F:float; } root_type T; { F:-e1 }", invalid_msg);
2062 TestError("table T { F:float; } root_type T; { F:+e1 }", invalid_msg);
2063 TestError("table T { F:float; } root_type T; { F:1.0e+ }", invalid_msg);
2064 TestError("table T { F:float; } root_type T; { F:1.0e- }", invalid_msg);
2065 // exponent pP is mandatory for hex-float
2066 TestError("table T { F:float; } root_type T; { F:0x0 }", invalid_msg);
2067 TestError("table T { F:float; } root_type T; { F:-0x. }", invalid_msg);
2068 TestError("table T { F:float; } root_type T; { F:0x. }", invalid_msg);
2069 TestError("table T { F:float; } root_type T; { F:0Xe }", invalid_msg);
2070 TestError("table T { F:float; } root_type T; { F:\"0Xe\" }", invalid_msg);
2071 TestError("table T { F:float; } root_type T; { F:\"nan(1)\" }", invalid_msg);
2072 // eE not exponent in hex-float!
2073 TestError("table T { F:float; } root_type T; { F:0x0.0e+ }", invalid_msg);
2074 TestError("table T { F:float; } root_type T; { F:0x0.0e- }", invalid_msg);
2075 TestError("table T { F:float; } root_type T; { F:0x0.0p }", invalid_msg);
2076 TestError("table T { F:float; } root_type T; { F:0x0.0p+ }", invalid_msg);
2077 TestError("table T { F:float; } root_type T; { F:0x0.0p- }", invalid_msg);
2078 TestError("table T { F:float; } root_type T; { F:0x0.0pa1 }", invalid_msg);
2079 TestError("table T { F:float; } root_type T; { F:0x0.0e+ }", invalid_msg);
2080 TestError("table T { F:float; } root_type T; { F:0x0.0e- }", invalid_msg);
2081 TestError("table T { F:float; } root_type T; { F:0x0.0e+0 }", invalid_msg);
2082 TestError("table T { F:float; } root_type T; { F:0x0.0e-0 }", invalid_msg);
2083 TestError("table T { F:float; } root_type T; { F:0x0.0ep+ }", invalid_msg);
2084 TestError("table T { F:float; } root_type T; { F:0x0.0ep- }", invalid_msg);
2085 TestError("table T { F:float; } root_type T; { F:1.2.3 }", invalid_msg);
2086 TestError("table T { F:float; } root_type T; { F:1.2.e3 }", invalid_msg);
2087 TestError("table T { F:float; } root_type T; { F:1.2e.3 }", invalid_msg);
2088 TestError("table T { F:float; } root_type T; { F:1.2e0.3 }", invalid_msg);
2089 TestError("table T { F:float; } root_type T; { F:1.2e3. }", invalid_msg);
2090 TestError("table T { F:float; } root_type T; { F:1.2e3.0 }", invalid_msg);
2091 TestError("table T { F:float; } root_type T; { F:+-1.0 }", invalid_msg);
2092 TestError("table T { F:float; } root_type T; { F:1.0e+-1 }", invalid_msg);
2093 TestError("table T { F:float; } root_type T; { F:\"1.0e+-1\" }", invalid_msg);
2094 TestError("table T { F:float; } root_type T; { F:1.e0e }", comma_msg);
2095 TestError("table T { F:float; } root_type T; { F:0x1.p0e }", comma_msg);
2096 TestError("table T { F:float; } root_type T; { F:\" 0x10 \" }", invalid_msg);
2098 TestError("table T { F:float; } root_type T; { F:\"1,2.\" }", invalid_msg);
2099 TestError("table T { F:float; } root_type T; { F:\"1.2e3.\" }", invalid_msg);
2100 TestError("table T { F:float; } root_type T; { F:\"0x1.p0e\" }", invalid_msg);
2101 TestError("table T { F:float; } root_type T; { F:\"0x1.0\" }", invalid_msg);
2102 TestError("table T { F:float; } root_type T; { F:\" 0x1.0\" }", invalid_msg);
2103 TestError("table T { F:float; } root_type T; { F:\"+ 0\" }", invalid_msg);
2104 // disable escapes for "number-in-string"
2105 TestError("table T { F:float; } root_type T; { F:\"\\f1.2e3.\" }", "invalid");
2106 TestError("table T { F:float; } root_type T; { F:\"\\t1.2e3.\" }", "invalid");
2107 TestError("table T { F:float; } root_type T; { F:\"\\n1.2e3.\" }", "invalid");
2108 TestError("table T { F:float; } root_type T; { F:\"\\r1.2e3.\" }", "invalid");
2109 TestError("table T { F:float; } root_type T; { F:\"4\\x005\" }", "invalid");
2110 TestError("table T { F:float; } root_type T; { F:\"\'12\'\" }", invalid_msg);
2111 // null is not a number constant!
2112 TestError("table T { F:float; } root_type T; { F:\"null\" }", invalid_msg);
2113 TestError("table T { F:float; } root_type T; { F:null }", invalid_msg);
2116 void GenerateTableTextTest() {
2117 std::string schemafile;
2118 std::string jsonfile;
2120 flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
2121 false, &schemafile) &&
2122 flatbuffers::LoadFile((test_data_path + "monsterdata_test.json").c_str(),
2125 auto include_test_path =
2126 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
2127 const char *include_directories[] = { test_data_path.c_str(),
2128 include_test_path.c_str(), nullptr };
2129 flatbuffers::IDLOptions opt;
2130 opt.indent_step = -1;
2131 flatbuffers::Parser parser(opt);
2132 ok = parser.Parse(schemafile.c_str(), include_directories) &&
2133 parser.Parse(jsonfile.c_str(), include_directories);
2136 const Monster *monster = GetMonster(parser.builder_.GetBufferPointer());
2137 std::string jsongen;
2138 auto result = GenerateTextFromTable(parser, monster, "MyGame.Example.Monster",
2140 TEST_EQ(result, true);
2142 const Vec3 *pos = monster->pos();
2144 result = GenerateTextFromTable(parser, pos, "MyGame.Example.Vec3", &jsongen);
2145 TEST_EQ(result, true);
2148 "{x: 1.0,y: 2.0,z: 3.0,test1: 3.0,test2: \"Green\",test3: {a: 5,b: 6}}");
2149 const Test &test3 = pos->test3();
2152 GenerateTextFromTable(parser, &test3, "MyGame.Example.Test", &jsongen);
2153 TEST_EQ(result, true);
2154 TEST_EQ_STR(jsongen.c_str(), "{a: 5,b: 6}");
2155 const Test *test4 = monster->test4()->Get(0);
2158 GenerateTextFromTable(parser, test4, "MyGame.Example.Test", &jsongen);
2159 TEST_EQ(result, true);
2160 TEST_EQ_STR(jsongen.c_str(), "{a: 10,b: 20}");
2163 template<typename T>
2164 void NumericUtilsTestInteger(const char *lower, const char *upper) {
2166 TEST_EQ(flatbuffers::StringToNumber("1q", &x), false);
2168 TEST_EQ(flatbuffers::StringToNumber(upper, &x), false);
2169 TEST_EQ(x, flatbuffers::numeric_limits<T>::max());
2170 TEST_EQ(flatbuffers::StringToNumber(lower, &x), false);
2171 auto expval = flatbuffers::is_unsigned<T>::value
2172 ? flatbuffers::numeric_limits<T>::max()
2173 : flatbuffers::numeric_limits<T>::lowest();
2177 template<typename T>
2178 void NumericUtilsTestFloat(const char *lower, const char *upper) {
2180 TEST_EQ(flatbuffers::StringToNumber("", &f), false);
2181 TEST_EQ(flatbuffers::StringToNumber("1q", &f), false);
2183 TEST_EQ(flatbuffers::StringToNumber(upper, &f), true);
2184 TEST_EQ(f, +flatbuffers::numeric_limits<T>::infinity());
2185 TEST_EQ(flatbuffers::StringToNumber(lower, &f), true);
2186 TEST_EQ(f, -flatbuffers::numeric_limits<T>::infinity());
2189 void NumericUtilsTest() {
2190 NumericUtilsTestInteger<uint64_t>("-1", "18446744073709551616");
2191 NumericUtilsTestInteger<uint8_t>("-1", "256");
2192 NumericUtilsTestInteger<int64_t>("-9223372036854775809",
2193 "9223372036854775808");
2194 NumericUtilsTestInteger<int8_t>("-129", "128");
2195 NumericUtilsTestFloat<float>("-3.4029e+38", "+3.4029e+38");
2196 NumericUtilsTestFloat<float>("-1.7977e+308", "+1.7977e+308");
2199 void IsAsciiUtilsTest() {
2201 for (int cnt = 0; cnt < 256; cnt++) {
2202 auto alpha = (('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z'));
2203 auto dec = (('0' <= c) && (c <= '9'));
2204 auto hex = (('a' <= c) && (c <= 'f')) || (('A' <= c) && (c <= 'F'));
2205 TEST_EQ(flatbuffers::is_alpha(c), alpha);
2206 TEST_EQ(flatbuffers::is_alnum(c), alpha || dec);
2207 TEST_EQ(flatbuffers::is_digit(c), dec);
2208 TEST_EQ(flatbuffers::is_xdigit(c), dec || hex);
2213 void UnicodeTest() {
2214 flatbuffers::Parser parser;
2215 // Without setting allow_non_utf8 = true, we treat \x sequences as byte
2216 // sequences which are then validated as UTF-8.
2217 TEST_EQ(parser.Parse("table T { F:string; }"
2219 "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2220 "\\u5225\\u30B5\\u30A4\\u30C8\\xE2\\x82\\xAC\\u0080\\uD8"
2223 std::string jsongen;
2224 parser.opts.indent_step = -1;
2226 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2227 TEST_EQ(result, true);
2228 TEST_EQ_STR(jsongen.c_str(),
2229 "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2230 "\\u5225\\u30B5\\u30A4\\u30C8\\u20AC\\u0080\\uD83D\\uDE0E\"}");
2233 void UnicodeTestAllowNonUTF8() {
2234 flatbuffers::Parser parser;
2235 parser.opts.allow_non_utf8 = true;
2238 "table T { F:string; }"
2240 "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2241 "\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\\u0080\\uD83D\\uDE0E\" }"),
2243 std::string jsongen;
2244 parser.opts.indent_step = -1;
2246 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2247 TEST_EQ(result, true);
2250 "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2251 "\\u5225\\u30B5\\u30A4\\u30C8\\u0001\\x80\\u0080\\uD83D\\uDE0E\"}");
2254 void UnicodeTestGenerateTextFailsOnNonUTF8() {
2255 flatbuffers::Parser parser;
2256 // Allow non-UTF-8 initially to model what happens when we load a binary
2257 // flatbuffer from disk which contains non-UTF-8 strings.
2258 parser.opts.allow_non_utf8 = true;
2261 "table T { F:string; }"
2263 "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2264 "\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\\u0080\\uD83D\\uDE0E\" }"),
2266 std::string jsongen;
2267 parser.opts.indent_step = -1;
2268 // Now, disallow non-UTF-8 (the default behavior) so GenerateText indicates
2270 parser.opts.allow_non_utf8 = false;
2272 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2273 TEST_EQ(result, false);
2276 void UnicodeSurrogatesTest() {
2277 flatbuffers::Parser parser;
2279 TEST_EQ(parser.Parse("table T { F:string (id: 0); }"
2281 "{ F:\"\\uD83D\\uDCA9\"}"),
2283 auto root = flatbuffers::GetRoot<flatbuffers::Table>(
2284 parser.builder_.GetBufferPointer());
2285 auto string = root->GetPointer<flatbuffers::String *>(
2286 flatbuffers::FieldIndexToOffset(0));
2287 TEST_EQ_STR(string->c_str(), "\xF0\x9F\x92\xA9");
2290 void UnicodeInvalidSurrogatesTest() {
2292 "table T { F:string; }"
2295 "unpaired high surrogate");
2297 "table T { F:string; }"
2299 "{ F:\"\\uD800abcd\"}",
2300 "unpaired high surrogate");
2302 "table T { F:string; }"
2304 "{ F:\"\\uD800\\n\"}",
2305 "unpaired high surrogate");
2307 "table T { F:string; }"
2309 "{ F:\"\\uD800\\uD800\"}",
2310 "multiple high surrogates");
2312 "table T { F:string; }"
2315 "unpaired low surrogate");
2318 void InvalidUTF8Test() {
2319 // "1 byte" pattern, under min length of 2 bytes
2321 "table T { F:string; }"
2324 "illegal UTF-8 sequence");
2325 // 2 byte pattern, string too short
2327 "table T { F:string; }"
2330 "illegal UTF-8 sequence");
2331 // 3 byte pattern, string too short
2333 "table T { F:string; }"
2335 "{ F:\"\xEF\xBF\"}",
2336 "illegal UTF-8 sequence");
2337 // 4 byte pattern, string too short
2339 "table T { F:string; }"
2341 "{ F:\"\xF7\xBF\xBF\"}",
2342 "illegal UTF-8 sequence");
2343 // "5 byte" pattern, string too short
2345 "table T { F:string; }"
2347 "{ F:\"\xFB\xBF\xBF\xBF\"}",
2348 "illegal UTF-8 sequence");
2349 // "6 byte" pattern, string too short
2351 "table T { F:string; }"
2353 "{ F:\"\xFD\xBF\xBF\xBF\xBF\"}",
2354 "illegal UTF-8 sequence");
2355 // "7 byte" pattern, string too short
2357 "table T { F:string; }"
2359 "{ F:\"\xFE\xBF\xBF\xBF\xBF\xBF\"}",
2360 "illegal UTF-8 sequence");
2361 // "5 byte" pattern, over max length of 4 bytes
2363 "table T { F:string; }"
2365 "{ F:\"\xFB\xBF\xBF\xBF\xBF\"}",
2366 "illegal UTF-8 sequence");
2367 // "6 byte" pattern, over max length of 4 bytes
2369 "table T { F:string; }"
2371 "{ F:\"\xFD\xBF\xBF\xBF\xBF\xBF\"}",
2372 "illegal UTF-8 sequence");
2373 // "7 byte" pattern, over max length of 4 bytes
2375 "table T { F:string; }"
2377 "{ F:\"\xFE\xBF\xBF\xBF\xBF\xBF\xBF\"}",
2378 "illegal UTF-8 sequence");
2380 // Three invalid encodings for U+000A (\n, aka NEWLINE)
2382 "table T { F:string; }"
2384 "{ F:\"\xC0\x8A\"}",
2385 "illegal UTF-8 sequence");
2387 "table T { F:string; }"
2389 "{ F:\"\xE0\x80\x8A\"}",
2390 "illegal UTF-8 sequence");
2392 "table T { F:string; }"
2394 "{ F:\"\xF0\x80\x80\x8A\"}",
2395 "illegal UTF-8 sequence");
2397 // Two invalid encodings for U+00A9 (COPYRIGHT SYMBOL)
2399 "table T { F:string; }"
2401 "{ F:\"\xE0\x81\xA9\"}",
2402 "illegal UTF-8 sequence");
2404 "table T { F:string; }"
2406 "{ F:\"\xF0\x80\x81\xA9\"}",
2407 "illegal UTF-8 sequence");
2409 // Invalid encoding for U+20AC (EURO SYMBOL)
2411 "table T { F:string; }"
2413 "{ F:\"\xF0\x82\x82\xAC\"}",
2414 "illegal UTF-8 sequence");
2416 // UTF-16 surrogate values between U+D800 and U+DFFF cannot be encoded in
2419 "table T { F:string; }"
2421 // U+10400 "encoded" as U+D801 U+DC00
2422 "{ F:\"\xED\xA0\x81\xED\xB0\x80\"}",
2423 "illegal UTF-8 sequence");
2425 // Check independence of identifier from locale.
2426 std::string locale_ident;
2427 locale_ident += "table T { F";
2428 locale_ident += static_cast<char>(-32); // unsigned 0xE0
2429 locale_ident += " :string; }";
2430 locale_ident += "root_type T;";
2431 locale_ident += "{}";
2432 TestError(locale_ident.c_str(), "");
2435 void UnknownFieldsTest() {
2436 flatbuffers::IDLOptions opts;
2437 opts.skip_unexpected_fields_in_json = true;
2438 flatbuffers::Parser parser(opts);
2440 TEST_EQ(parser.Parse("table T { str:string; i:int;}"
2443 "unknown_string:\"test\","
2444 "\"unknown_string\":\"test\","
2446 "unknown_float:1.0,"
2447 "unknown_array: [ 1, 2, 3, 4],"
2448 "unknown_object: { i: 10 },"
2449 "\"unknown_object\": { \"i\": 10 },"
2453 std::string jsongen;
2454 parser.opts.indent_step = -1;
2456 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2457 TEST_EQ(result, true);
2458 TEST_EQ_STR(jsongen.c_str(), "{str: \"test\",i: 10}");
2461 void ParseUnionTest() {
2462 // Unions must be parseable with the type field following the object.
2463 flatbuffers::Parser parser;
2464 TEST_EQ(parser.Parse("table T { A:int; }"
2468 "{ X:{ A:1 }, X_type: T }"),
2470 // Unions must be parsable with prefixed namespace.
2471 flatbuffers::Parser parser2;
2472 TEST_EQ(parser2.Parse("namespace N; table A {} namespace; union U { N.A }"
2473 "table B { e:U; } root_type B;"
2474 "{ e_type: N_A, e: {} }"),
2478 void InvalidNestedFlatbufferTest() {
2479 // First, load and parse FlatBuffer schema (.fbs)
2480 std::string schemafile;
2481 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
2482 false, &schemafile),
2484 auto include_test_path =
2485 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
2486 const char *include_directories[] = { test_data_path.c_str(),
2487 include_test_path.c_str(), nullptr };
2488 flatbuffers::Parser parser1;
2489 TEST_EQ(parser1.Parse(schemafile.c_str(), include_directories), true);
2491 // "color" inside nested flatbuffer contains invalid enum value
2492 TEST_EQ(parser1.Parse("{ name: \"Bender\", testnestedflatbuffer: { name: "
2493 "\"Leela\", color: \"nonexistent\"}}"),
2497 void EvolutionTest() {
2498 // VS10 does not support typed enums, exclude from tests
2499 #if !defined(_MSC_VER) || _MSC_VER >= 1700
2500 const int NUM_VERSIONS = 2;
2501 std::string schemas[NUM_VERSIONS];
2502 std::string jsonfiles[NUM_VERSIONS];
2503 std::vector<uint8_t> binaries[NUM_VERSIONS];
2505 flatbuffers::IDLOptions idl_opts;
2506 idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
2507 flatbuffers::Parser parser(idl_opts);
2509 // Load all the schema versions and their associated data.
2510 for (int i = 0; i < NUM_VERSIONS; ++i) {
2511 std::string schema = test_data_path + "evolution_test/evolution_v" +
2512 flatbuffers::NumToString(i + 1) + ".fbs";
2513 TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i]));
2514 std::string json = test_data_path + "evolution_test/evolution_v" +
2515 flatbuffers::NumToString(i + 1) + ".json";
2516 TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i]));
2518 TEST_ASSERT(parser.Parse(schemas[i].c_str()));
2519 TEST_ASSERT(parser.Parse(jsonfiles[i].c_str()));
2521 auto bufLen = parser.builder_.GetSize();
2522 auto buf = parser.builder_.GetBufferPointer();
2523 binaries[i].reserve(bufLen);
2524 std::copy(buf, buf + bufLen, std::back_inserter(binaries[i]));
2527 // Assert that all the verifiers for the different schema versions properly
2528 // verify any version data.
2529 for (int i = 0; i < NUM_VERSIONS; ++i) {
2530 flatbuffers::Verifier verifier(&binaries[i].front(), binaries[i].size());
2531 TEST_ASSERT(Evolution::V1::VerifyRootBuffer(verifier));
2532 TEST_ASSERT(Evolution::V2::VerifyRootBuffer(verifier));
2535 // Test backwards compatibility by reading old data with an evolved schema.
2536 auto root_v1_viewed_from_v2 = Evolution::V2::GetRoot(&binaries[0].front());
2537 // field 'k' is new in version 2, so it should be null.
2538 TEST_ASSERT(nullptr == root_v1_viewed_from_v2->k());
2539 // field 'l' is new in version 2 with a default of 56.
2540 TEST_EQ(root_v1_viewed_from_v2->l(), 56);
2541 // field 'c' of 'TableA' is new in version 2, so it should be null.
2542 TEST_ASSERT(nullptr == root_v1_viewed_from_v2->e()->c());
2543 // 'TableC' was added to field 'c' union in version 2, so it should be null.
2544 TEST_ASSERT(nullptr == root_v1_viewed_from_v2->c_as_TableC());
2545 // The field 'c' union should be of type 'TableB' regardless of schema version
2546 TEST_ASSERT(root_v1_viewed_from_v2->c_type() == Evolution::V2::Union::TableB);
2547 // The field 'f' was renamed to 'ff' in version 2, it should still be
2549 TEST_EQ(root_v1_viewed_from_v2->ff()->a(), 16);
2551 // Test forwards compatibility by reading new data with an old schema.
2552 auto root_v2_viewed_from_v1 = Evolution::V1::GetRoot(&binaries[1].front());
2553 // The field 'c' union in version 2 is a new table (index = 3) and should
2554 // still be accessible, but not interpretable.
2555 TEST_EQ(static_cast<uint8_t>(root_v2_viewed_from_v1->c_type()), 3);
2556 TEST_NOTNULL(root_v2_viewed_from_v1->c());
2557 // The field 'd' enum in verison 2 has new members and should still be
2558 // accessible, but not interpretable.
2559 TEST_EQ(static_cast<int8_t>(root_v2_viewed_from_v1->d()), 3);
2560 // The field 'a' in version 2 is deprecated and should return the default
2561 // value (0) instead of the value stored in the in the buffer (42).
2562 TEST_EQ(root_v2_viewed_from_v1->a(), 0);
2563 // The field 'ff' was originally named 'f' in version 1, it should still be
2565 TEST_EQ(root_v2_viewed_from_v1->f()->a(), 35);
2569 void UnionDeprecationTest() {
2570 const int NUM_VERSIONS = 2;
2571 std::string schemas[NUM_VERSIONS];
2572 std::string jsonfiles[NUM_VERSIONS];
2573 std::vector<uint8_t> binaries[NUM_VERSIONS];
2575 flatbuffers::IDLOptions idl_opts;
2576 idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
2577 flatbuffers::Parser parser(idl_opts);
2579 // Load all the schema versions and their associated data.
2580 for (int i = 0; i < NUM_VERSIONS; ++i) {
2581 std::string schema = test_data_path + "evolution_test/evolution_v" +
2582 flatbuffers::NumToString(i + 1) + ".fbs";
2583 TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i]));
2584 std::string json = test_data_path + "evolution_test/evolution_v" +
2585 flatbuffers::NumToString(i + 1) + ".json";
2586 TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i]));
2588 TEST_ASSERT(parser.Parse(schemas[i].c_str()));
2589 TEST_ASSERT(parser.Parse(jsonfiles[i].c_str()));
2591 auto bufLen = parser.builder_.GetSize();
2592 auto buf = parser.builder_.GetBufferPointer();
2593 binaries[i].reserve(bufLen);
2594 std::copy(buf, buf + bufLen, std::back_inserter(binaries[i]));
2597 auto v2 = parser.LookupStruct("Evolution.V2.Root");
2599 auto j_type_field = v2->fields.Lookup("j_type");
2600 TEST_NOTNULL(j_type_field);
2601 TEST_ASSERT(j_type_field->deprecated);
2604 void UnionVectorTest() {
2605 // load FlatBuffer fbs schema and json.
2606 std::string schemafile, jsonfile;
2607 TEST_EQ(flatbuffers::LoadFile(
2608 (test_data_path + "union_vector/union_vector.fbs").c_str(), false,
2611 TEST_EQ(flatbuffers::LoadFile(
2612 (test_data_path + "union_vector/union_vector.json").c_str(),
2617 flatbuffers::IDLOptions idl_opts;
2618 idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
2619 flatbuffers::Parser parser(idl_opts);
2620 TEST_EQ(parser.Parse(schemafile.c_str()), true);
2622 flatbuffers::FlatBufferBuilder fbb;
2625 std::vector<uint8_t> types;
2626 types.push_back(static_cast<uint8_t>(Character_Belle));
2627 types.push_back(static_cast<uint8_t>(Character_MuLan));
2628 types.push_back(static_cast<uint8_t>(Character_BookFan));
2629 types.push_back(static_cast<uint8_t>(Character_Other));
2630 types.push_back(static_cast<uint8_t>(Character_Unused));
2633 std::vector<flatbuffers::Offset<void>> characters;
2634 characters.push_back(fbb.CreateStruct(BookReader(/*books_read=*/7)).Union());
2635 characters.push_back(CreateAttacker(fbb, /*sword_attack_damage=*/5).Union());
2636 characters.push_back(fbb.CreateStruct(BookReader(/*books_read=*/2)).Union());
2637 characters.push_back(fbb.CreateString("Other").Union());
2638 characters.push_back(fbb.CreateString("Unused").Union());
2641 const auto movie_offset =
2642 CreateMovie(fbb, Character_Rapunzel,
2643 fbb.CreateStruct(Rapunzel(/*hair_length=*/6)).Union(),
2644 fbb.CreateVector(types), fbb.CreateVector(characters));
2645 FinishMovieBuffer(fbb, movie_offset);
2647 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
2648 TEST_EQ(VerifyMovieBuffer(verifier), true);
2650 auto flat_movie = GetMovie(fbb.GetBufferPointer());
2652 auto TestMovie = [](const Movie *movie) {
2653 TEST_EQ(movie->main_character_type() == Character_Rapunzel, true);
2655 auto cts = movie->characters_type();
2656 TEST_EQ(movie->characters_type()->size(), 5);
2657 TEST_EQ(cts->GetEnum<Character>(0) == Character_Belle, true);
2658 TEST_EQ(cts->GetEnum<Character>(1) == Character_MuLan, true);
2659 TEST_EQ(cts->GetEnum<Character>(2) == Character_BookFan, true);
2660 TEST_EQ(cts->GetEnum<Character>(3) == Character_Other, true);
2661 TEST_EQ(cts->GetEnum<Character>(4) == Character_Unused, true);
2663 auto rapunzel = movie->main_character_as_Rapunzel();
2664 TEST_NOTNULL(rapunzel);
2665 TEST_EQ(rapunzel->hair_length(), 6);
2667 auto cs = movie->characters();
2668 TEST_EQ(cs->size(), 5);
2669 auto belle = cs->GetAs<BookReader>(0);
2670 TEST_EQ(belle->books_read(), 7);
2671 auto mu_lan = cs->GetAs<Attacker>(1);
2672 TEST_EQ(mu_lan->sword_attack_damage(), 5);
2673 auto book_fan = cs->GetAs<BookReader>(2);
2674 TEST_EQ(book_fan->books_read(), 2);
2675 auto other = cs->GetAsString(3);
2676 TEST_EQ_STR(other->c_str(), "Other");
2677 auto unused = cs->GetAsString(4);
2678 TEST_EQ_STR(unused->c_str(), "Unused");
2681 TestMovie(flat_movie);
2683 // Also test the JSON we loaded above.
2684 TEST_EQ(parser.Parse(jsonfile.c_str()), true);
2685 auto jbuf = parser.builder_.GetBufferPointer();
2686 flatbuffers::Verifier jverifier(jbuf, parser.builder_.GetSize());
2687 TEST_EQ(VerifyMovieBuffer(jverifier), true);
2688 TestMovie(GetMovie(jbuf));
2690 auto movie_object = flat_movie->UnPack();
2691 TEST_EQ(movie_object->main_character.AsRapunzel()->hair_length(), 6);
2692 TEST_EQ(movie_object->characters[0].AsBelle()->books_read(), 7);
2693 TEST_EQ(movie_object->characters[1].AsMuLan()->sword_attack_damage, 5);
2694 TEST_EQ(movie_object->characters[2].AsBookFan()->books_read(), 2);
2695 TEST_EQ_STR(movie_object->characters[3].AsOther()->c_str(), "Other");
2696 TEST_EQ_STR(movie_object->characters[4].AsUnused()->c_str(), "Unused");
2699 fbb.Finish(Movie::Pack(fbb, movie_object));
2701 delete movie_object;
2703 auto repacked_movie = GetMovie(fbb.GetBufferPointer());
2705 TestMovie(repacked_movie);
2707 // Generate text using mini-reflection.
2709 flatbuffers::FlatBufferToString(fbb.GetBufferPointer(), MovieTypeTable());
2712 "{ main_character_type: Rapunzel, main_character: { hair_length: 6 }, "
2713 "characters_type: [ Belle, MuLan, BookFan, Other, Unused ], "
2714 "characters: [ { books_read: 7 }, { sword_attack_damage: 5 }, "
2715 "{ books_read: 2 }, \"Other\", \"Unused\" ] }");
2717 flatbuffers::ToStringVisitor visitor("\n", true, " ");
2718 IterateFlatBuffer(fbb.GetBufferPointer(), MovieTypeTable(), &visitor);
2719 TEST_EQ_STR(visitor.s.c_str(),
2721 " \"main_character_type\": \"Rapunzel\",\n"
2722 " \"main_character\": {\n"
2723 " \"hair_length\": 6\n"
2725 " \"characters_type\": [\n"
2732 " \"characters\": [\n"
2734 " \"books_read\": 7\n"
2737 " \"sword_attack_damage\": 5\n"
2740 " \"books_read\": 2\n"
2747 // Generate text using parsed schema.
2748 std::string jsongen;
2749 auto result = GenerateText(parser, fbb.GetBufferPointer(), &jsongen);
2750 TEST_EQ(result, true);
2751 TEST_EQ_STR(jsongen.c_str(),
2753 " main_character_type: \"Rapunzel\",\n"
2754 " main_character: {\n"
2757 " characters_type: [\n"
2769 " sword_attack_damage: 5\n"
2779 // Simple test with reflection.
2781 auto schema = reflection::GetSchema(parser.builder_.GetBufferPointer());
2782 auto ok = flatbuffers::Verify(*schema, *schema->root_table(),
2783 fbb.GetBufferPointer(), fbb.GetSize());
2786 flatbuffers::Parser parser2(idl_opts);
2787 TEST_EQ(parser2.Parse("struct Bool { b:bool; }"
2788 "union Any { Bool }"
2789 "table Root { a:Any; }"
2792 TEST_EQ(parser2.Parse("{a_type:Bool,a:{b:true}}"), true);
2795 void ConformTest() {
2796 flatbuffers::Parser parser;
2797 TEST_EQ(parser.Parse("table T { A:int; } enum E:byte { A }"), true);
2799 auto test_conform = [](flatbuffers::Parser &parser1, const char *test,
2800 const char *expected_err) {
2801 flatbuffers::Parser parser2;
2802 TEST_EQ(parser2.Parse(test), true);
2803 auto err = parser2.ConformTo(parser1);
2804 TEST_NOTNULL(strstr(err.c_str(), expected_err));
2807 test_conform(parser, "table T { A:byte; }", "types differ for field");
2808 test_conform(parser, "table T { B:int; A:int; }", "offsets differ for field");
2809 test_conform(parser, "table T { A:int = 1; }", "defaults differ for field");
2810 test_conform(parser, "table T { B:float; }",
2811 "field renamed to different type");
2812 test_conform(parser, "enum E:byte { B, A }", "values differ for enum");
2815 void ParseProtoBufAsciiTest() {
2816 // We can put the parser in a mode where it will accept JSON that looks more
2817 // like Protobuf ASCII, for users that have data in that format.
2818 // This uses no "" for field names (which we already support by default,
2819 // omits `,`, `:` before `{` and a couple of other features.
2820 flatbuffers::Parser parser;
2821 parser.opts.protobuf_ascii_alike = true;
2823 parser.Parse("table S { B:int; } table T { A:[int]; C:S; } root_type T;"),
2825 TEST_EQ(parser.Parse("{ A [1 2] C { B:2 }}"), true);
2826 // Similarly, in text output, it should omit these.
2828 auto ok = flatbuffers::GenerateText(
2829 parser, parser.builder_.GetBufferPointer(), &text);
2831 TEST_EQ_STR(text.c_str(),
2832 "{\n A [\n 1\n 2\n ]\n C {\n B: 2\n }\n}\n");
2835 void FlexBuffersTest() {
2836 flexbuffers::Builder slb(512,
2837 flexbuffers::BUILDER_FLAG_SHARE_KEYS_AND_STRINGS);
2839 // Write the equivalent of:
2840 // { vec: [ -100, "Fred", 4.0, false ], bar: [ 1, 2, 3 ], bar3: [ 1, 2, 3 ],
2841 // foo: 100, bool: true, mymap: { foo: "Fred" } }
2843 #ifndef FLATBUFFERS_CPP98_STL
2844 // It's possible to do this without std::function support as well.
2846 slb.Vector("vec", [&]() {
2847 slb += -100; // Equivalent to slb.Add(-100) or slb.Int(-100);
2849 slb.IndirectFloat(4.0f);
2850 auto i_f = slb.LastValue();
2851 uint8_t blob[] = { 77 };
2854 slb.ReuseValue(i_f);
2856 int ints[] = { 1, 2, 3 };
2857 slb.Vector("bar", ints, 3);
2858 slb.FixedTypedVector("bar3", ints, 3);
2859 bool bools[] = {true, false, true, false};
2860 slb.Vector("bools", bools, 4);
2861 slb.Bool("bool", true);
2862 slb.Double("foo", 100);
2863 slb.Map("mymap", [&]() {
2864 slb.String("foo", "Fred"); // Testing key and string reuse.
2869 // It's possible to do this without std::function support as well.
2870 slb.Map([](flexbuffers::Builder& slb2) {
2871 slb2.Vector("vec", [](flexbuffers::Builder& slb3) {
2872 slb3 += -100; // Equivalent to slb.Add(-100) or slb.Int(-100);
2874 slb3.IndirectFloat(4.0f);
2875 auto i_f = slb3.LastValue();
2876 uint8_t blob[] = { 77 };
2879 slb3.ReuseValue(i_f);
2881 int ints[] = { 1, 2, 3 };
2882 slb2.Vector("bar", ints, 3);
2883 slb2.FixedTypedVector("bar3", ints, 3);
2884 slb2.Bool("bool", true);
2885 slb2.Double("foo", 100);
2886 slb2.Map("mymap", [](flexbuffers::Builder& slb3) {
2887 slb3.String("foo", "Fred"); // Testing key and string reuse.
2891 #endif // FLATBUFFERS_CPP98_STL
2893 #ifdef FLATBUFFERS_TEST_VERBOSE
2894 for (size_t i = 0; i < slb.GetBuffer().size(); i++)
2895 printf("%d ", flatbuffers::vector_data(slb.GetBuffer())[i]);
2900 auto map = flexbuffers::GetRoot(slb.GetBuffer()).AsMap();
2901 TEST_EQ(map.size(), 7);
2902 auto vec = map["vec"].AsVector();
2903 TEST_EQ(vec.size(), 6);
2904 TEST_EQ(vec[0].AsInt64(), -100);
2905 TEST_EQ_STR(vec[1].AsString().c_str(), "Fred");
2906 TEST_EQ(vec[1].AsInt64(), 0); // Number parsing failed.
2907 TEST_EQ(vec[2].AsDouble(), 4.0);
2908 TEST_EQ(vec[2].AsString().IsTheEmptyString(), true); // Wrong Type.
2909 TEST_EQ_STR(vec[2].AsString().c_str(), ""); // This still works though.
2910 TEST_EQ_STR(vec[2].ToString().c_str(), "4.0"); // Or have it converted.
2911 // Few tests for templated version of As.
2912 TEST_EQ(vec[0].As<int64_t>(), -100);
2913 TEST_EQ_STR(vec[1].As<std::string>().c_str(), "Fred");
2914 TEST_EQ(vec[1].As<int64_t>(), 0); // Number parsing failed.
2915 TEST_EQ(vec[2].As<double>(), 4.0);
2916 // Test that the blob can be accessed.
2917 TEST_EQ(vec[3].IsBlob(), true);
2918 auto blob = vec[3].AsBlob();
2919 TEST_EQ(blob.size(), 1);
2920 TEST_EQ(blob.data()[0], 77);
2921 TEST_EQ(vec[4].IsBool(), true); // Check if type is a bool
2922 TEST_EQ(vec[4].AsBool(), false); // Check if value is false
2923 TEST_EQ(vec[5].AsDouble(), 4.0); // This is shared with vec[2] !
2924 auto tvec = map["bar"].AsTypedVector();
2925 TEST_EQ(tvec.size(), 3);
2926 TEST_EQ(tvec[2].AsInt8(), 3);
2927 auto tvec3 = map["bar3"].AsFixedTypedVector();
2928 TEST_EQ(tvec3.size(), 3);
2929 TEST_EQ(tvec3[2].AsInt8(), 3);
2930 TEST_EQ(map["bool"].AsBool(), true);
2931 auto tvecb = map["bools"].AsTypedVector();
2932 TEST_EQ(tvecb.ElementType(), flexbuffers::FBT_BOOL);
2933 TEST_EQ(map["foo"].AsUInt8(), 100);
2934 TEST_EQ(map["unknown"].IsNull(), true);
2935 auto mymap = map["mymap"].AsMap();
2936 // These should be equal by pointer equality, since key and value are shared.
2937 TEST_EQ(mymap.Keys()[0].AsKey(), map.Keys()[4].AsKey());
2938 TEST_EQ(mymap.Values()[0].AsString().c_str(), vec[1].AsString().c_str());
2939 // We can mutate values in the buffer.
2940 TEST_EQ(vec[0].MutateInt(-99), true);
2941 TEST_EQ(vec[0].AsInt64(), -99);
2942 TEST_EQ(vec[1].MutateString("John"), true); // Size must match.
2943 TEST_EQ_STR(vec[1].AsString().c_str(), "John");
2944 TEST_EQ(vec[1].MutateString("Alfred"), false); // Too long.
2945 TEST_EQ(vec[2].MutateFloat(2.0f), true);
2946 TEST_EQ(vec[2].AsFloat(), 2.0f);
2947 TEST_EQ(vec[2].MutateFloat(3.14159), false); // Double does not fit in float.
2948 TEST_EQ(vec[4].AsBool(), false); // Is false before change
2949 TEST_EQ(vec[4].MutateBool(true), true); // Can change a bool
2950 TEST_EQ(vec[4].AsBool(), true); // Changed bool is now true
2953 flatbuffers::Parser parser;
2955 auto jsontest = "{ a: [ 123, 456.0 ], b: \"hello\", c: true, d: false }";
2956 TEST_EQ(parser.ParseFlexBuffer(jsontest, nullptr, &slb), true);
2957 auto jroot = flexbuffers::GetRoot(slb.GetBuffer());
2958 auto jmap = jroot.AsMap();
2959 auto jvec = jmap["a"].AsVector();
2960 TEST_EQ(jvec[0].AsInt64(), 123);
2961 TEST_EQ(jvec[1].AsDouble(), 456.0);
2962 TEST_EQ_STR(jmap["b"].AsString().c_str(), "hello");
2963 TEST_EQ(jmap["c"].IsBool(), true); // Parsed correctly to a bool
2964 TEST_EQ(jmap["c"].AsBool(), true); // Parsed correctly to true
2965 TEST_EQ(jmap["d"].IsBool(), true); // Parsed correctly to a bool
2966 TEST_EQ(jmap["d"].AsBool(), false); // Parsed correctly to false
2967 // And from FlexBuffer back to JSON:
2968 auto jsonback = jroot.ToString();
2969 TEST_EQ_STR(jsontest, jsonback.c_str());
2973 for (int i = 0; i < 130; ++i) slb.Add(static_cast<uint8_t>(255));
2975 for (int i = 0; i < 130; ++i) slb.Add(static_cast<uint8_t>(255));
2980 TEST_EQ(slb.GetSize(), 664);
2983 void FlexBuffersDeprecatedTest() {
2984 // FlexBuffers as originally designed had a flaw involving the
2985 // FBT_VECTOR_STRING datatype, and this test documents/tests the fix for it.
2986 // Discussion: https://github.com/google/flatbuffers/issues/5627
2987 flexbuffers::Builder slb;
2988 // FBT_VECTOR_* are "typed vectors" where all elements are of the same type.
2989 // Problem is, when storing FBT_STRING elements, it relies on that type to
2990 // get the bit-width for the size field of the string, which in this case
2991 // isn't present, and instead defaults to 8-bit. This means that any strings
2992 // stored inside such a vector, when accessed thru the old API that returns
2993 // a String reference, will appear to be truncated if the string stored is
2994 // actually >=256 bytes.
2995 std::string test_data(300, 'A');
2996 auto start = slb.StartVector();
2997 // This one will have a 16-bit size field.
2998 slb.String(test_data);
2999 // This one will have an 8-bit size field.
3000 slb.String("hello");
3001 // We're asking this to be serialized as a typed vector (true), but not
3002 // fixed size (false). The type will be FBT_VECTOR_STRING with a bit-width
3003 // of whatever the offsets in the vector need, the bit-widths of the strings
3004 // are not stored(!) <- the actual design flaw.
3005 // Note that even in the fixed code, we continue to serialize the elements of
3006 // FBT_VECTOR_STRING as FBT_STRING, since there may be old code out there
3007 // reading new data that we want to continue to function.
3008 // Thus, FBT_VECTOR_STRING, while deprecated, will always be represented the
3009 // same way, the fix lies on the reading side.
3010 slb.EndVector(start, true, false);
3012 // So now lets read this data back.
3013 // For existing data, since we have no way of knowing what the actual
3014 // bit-width of the size field of the string is, we are going to ignore this
3015 // field, and instead treat these strings as FBT_KEY (null-terminated), so we
3016 // can deal with strings of arbitrary length. This of course truncates strings
3017 // with embedded nulls, but we think that that is preferrable over truncating
3018 // strings >= 256 bytes.
3019 auto vec = flexbuffers::GetRoot(slb.GetBuffer()).AsTypedVector();
3020 // Even though this was serialized as FBT_VECTOR_STRING, it is read as
3022 TEST_EQ(vec.ElementType(), flexbuffers::FBT_KEY);
3023 // Access the long string. Previously, this would return a string of size 1,
3024 // since it would read the high-byte of the 16-bit length.
3025 // This should now correctly test the full 300 bytes, using AsKey():
3026 TEST_EQ_STR(vec[0].AsKey(), test_data.c_str());
3027 // Old code that called AsString will continue to work, as the String
3028 // accessor objects now use a cached size that can come from a key as well.
3029 TEST_EQ_STR(vec[0].AsString().c_str(), test_data.c_str());
3030 // Short strings work as before:
3031 TEST_EQ_STR(vec[1].AsKey(), "hello");
3032 TEST_EQ_STR(vec[1].AsString().c_str(), "hello");
3033 // So, while existing code and data mostly "just work" with the fixes applied
3034 // to AsTypedVector and AsString, what do you do going forward?
3035 // Code accessing existing data doesn't necessarily need to change, though
3036 // you could consider using AsKey instead of AsString for a) documenting
3037 // that you are accessing keys, or b) a speedup if you don't actually use
3039 // For new data, or data that doesn't need to be backwards compatible,
3040 // instead serialize as FBT_VECTOR (call EndVector with typed = false, then
3041 // read elements with AsString), or, for maximum compactness, use
3042 // FBT_VECTOR_KEY (call slb.Key above instead, read with AsKey or AsString).
3045 void TypeAliasesTest() {
3046 flatbuffers::FlatBufferBuilder builder;
3048 builder.Finish(CreateTypeAliases(
3049 builder, flatbuffers::numeric_limits<int8_t>::min(),
3050 flatbuffers::numeric_limits<uint8_t>::max(),
3051 flatbuffers::numeric_limits<int16_t>::min(),
3052 flatbuffers::numeric_limits<uint16_t>::max(),
3053 flatbuffers::numeric_limits<int32_t>::min(),
3054 flatbuffers::numeric_limits<uint32_t>::max(),
3055 flatbuffers::numeric_limits<int64_t>::min(),
3056 flatbuffers::numeric_limits<uint64_t>::max(), 2.3f, 2.3));
3058 auto p = builder.GetBufferPointer();
3059 auto ta = flatbuffers::GetRoot<TypeAliases>(p);
3061 TEST_EQ(ta->i8(), flatbuffers::numeric_limits<int8_t>::min());
3062 TEST_EQ(ta->u8(), flatbuffers::numeric_limits<uint8_t>::max());
3063 TEST_EQ(ta->i16(), flatbuffers::numeric_limits<int16_t>::min());
3064 TEST_EQ(ta->u16(), flatbuffers::numeric_limits<uint16_t>::max());
3065 TEST_EQ(ta->i32(), flatbuffers::numeric_limits<int32_t>::min());
3066 TEST_EQ(ta->u32(), flatbuffers::numeric_limits<uint32_t>::max());
3067 TEST_EQ(ta->i64(), flatbuffers::numeric_limits<int64_t>::min());
3068 TEST_EQ(ta->u64(), flatbuffers::numeric_limits<uint64_t>::max());
3069 TEST_EQ(ta->f32(), 2.3f);
3070 TEST_EQ(ta->f64(), 2.3);
3071 using namespace flatbuffers; // is_same
3072 static_assert(is_same<decltype(ta->i8()), int8_t>::value, "invalid type");
3073 static_assert(is_same<decltype(ta->i16()), int16_t>::value, "invalid type");
3074 static_assert(is_same<decltype(ta->i32()), int32_t>::value, "invalid type");
3075 static_assert(is_same<decltype(ta->i64()), int64_t>::value, "invalid type");
3076 static_assert(is_same<decltype(ta->u8()), uint8_t>::value, "invalid type");
3077 static_assert(is_same<decltype(ta->u16()), uint16_t>::value, "invalid type");
3078 static_assert(is_same<decltype(ta->u32()), uint32_t>::value, "invalid type");
3079 static_assert(is_same<decltype(ta->u64()), uint64_t>::value, "invalid type");
3080 static_assert(is_same<decltype(ta->f32()), float>::value, "invalid type");
3081 static_assert(is_same<decltype(ta->f64()), double>::value, "invalid type");
3084 void EndianSwapTest() {
3085 TEST_EQ(flatbuffers::EndianSwap(static_cast<int16_t>(0x1234)), 0x3412);
3086 TEST_EQ(flatbuffers::EndianSwap(static_cast<int32_t>(0x12345678)),
3088 TEST_EQ(flatbuffers::EndianSwap(static_cast<int64_t>(0x1234567890ABCDEF)),
3089 0xEFCDAB9078563412);
3090 TEST_EQ(flatbuffers::EndianSwap(flatbuffers::EndianSwap(3.14f)), 3.14f);
3093 void UninitializedVectorTest() {
3094 flatbuffers::FlatBufferBuilder builder;
3096 Test *buf = nullptr;
3097 auto vector_offset =
3098 builder.CreateUninitializedVectorOfStructs<Test>(2, &buf);
3100 buf[0] = Test(10, 20);
3101 buf[1] = Test(30, 40);
3103 auto required_name = builder.CreateString("myMonster");
3104 auto monster_builder = MonsterBuilder(builder);
3105 monster_builder.add_name(
3106 required_name); // required field mandated for monster.
3107 monster_builder.add_test4(vector_offset);
3108 builder.Finish(monster_builder.Finish());
3110 auto p = builder.GetBufferPointer();
3111 auto uvt = flatbuffers::GetRoot<Monster>(p);
3113 auto vec = uvt->test4();
3115 auto test_0 = vec->Get(0);
3116 auto test_1 = vec->Get(1);
3117 TEST_EQ(test_0->a(), 10);
3118 TEST_EQ(test_0->b(), 20);
3119 TEST_EQ(test_1->a(), 30);
3120 TEST_EQ(test_1->b(), 40);
3123 void EqualOperatorTest() {
3126 TEST_EQ(b == a, true);
3127 TEST_EQ(b != a, false);
3130 TEST_EQ(b == a, false);
3131 TEST_EQ(b != a, true);
3133 TEST_EQ(b == a, true);
3134 TEST_EQ(b != a, false);
3136 b.inventory.push_back(3);
3137 TEST_EQ(b == a, false);
3138 TEST_EQ(b != a, true);
3139 b.inventory.clear();
3140 TEST_EQ(b == a, true);
3141 TEST_EQ(b != a, false);
3143 b.test.type = Any_Monster;
3144 TEST_EQ(b == a, false);
3145 TEST_EQ(b != a, true);
3148 // For testing any binaries, e.g. from fuzzing.
3149 void LoadVerifyBinaryTest() {
3151 if (flatbuffers::LoadFile(
3152 (test_data_path + "fuzzer/your-filename-here").c_str(), true,
3154 flatbuffers::Verifier verifier(
3155 reinterpret_cast<const uint8_t *>(binary.data()), binary.size());
3156 TEST_EQ(VerifyMonsterBuffer(verifier), true);
3160 void CreateSharedStringTest() {
3161 flatbuffers::FlatBufferBuilder builder;
3162 const auto one1 = builder.CreateSharedString("one");
3163 const auto two = builder.CreateSharedString("two");
3164 const auto one2 = builder.CreateSharedString("one");
3165 TEST_EQ(one1.o, one2.o);
3166 const auto onetwo = builder.CreateSharedString("onetwo");
3167 TEST_EQ(onetwo.o != one1.o, true);
3168 TEST_EQ(onetwo.o != two.o, true);
3170 // Support for embedded nulls
3171 const char chars_b[] = { 'a', '\0', 'b' };
3172 const char chars_c[] = { 'a', '\0', 'c' };
3173 const auto null_b1 = builder.CreateSharedString(chars_b, sizeof(chars_b));
3174 const auto null_c = builder.CreateSharedString(chars_c, sizeof(chars_c));
3175 const auto null_b2 = builder.CreateSharedString(chars_b, sizeof(chars_b));
3176 TEST_EQ(null_b1.o != null_c.o, true); // Issue#5058 repro
3177 TEST_EQ(null_b1.o, null_b2.o);
3179 // Put the strings into an array for round trip verification.
3180 const flatbuffers::Offset<flatbuffers::String> array[7] = {
3181 one1, two, one2, onetwo, null_b1, null_c, null_b2
3183 const auto vector_offset =
3184 builder.CreateVector(array, flatbuffers::uoffset_t(7));
3185 MonsterBuilder monster_builder(builder);
3186 monster_builder.add_name(two);
3187 monster_builder.add_testarrayofstring(vector_offset);
3188 builder.Finish(monster_builder.Finish());
3190 // Read the Monster back.
3191 const auto *monster =
3192 flatbuffers::GetRoot<Monster>(builder.GetBufferPointer());
3193 TEST_EQ_STR(monster->name()->c_str(), "two");
3194 const auto *testarrayofstring = monster->testarrayofstring();
3195 TEST_EQ(testarrayofstring->size(), flatbuffers::uoffset_t(7));
3196 const auto &a = *testarrayofstring;
3197 TEST_EQ_STR(a[0]->c_str(), "one");
3198 TEST_EQ_STR(a[1]->c_str(), "two");
3199 TEST_EQ_STR(a[2]->c_str(), "one");
3200 TEST_EQ_STR(a[3]->c_str(), "onetwo");
3201 TEST_EQ(a[4]->str(), (std::string(chars_b, sizeof(chars_b))));
3202 TEST_EQ(a[5]->str(), (std::string(chars_c, sizeof(chars_c))));
3203 TEST_EQ(a[6]->str(), (std::string(chars_b, sizeof(chars_b))));
3205 // Make sure String::operator< works, too, since it is related to
3206 // StringOffsetCompare.
3207 TEST_EQ((*a[0]) < (*a[1]), true);
3208 TEST_EQ((*a[1]) < (*a[0]), false);
3209 TEST_EQ((*a[1]) < (*a[2]), false);
3210 TEST_EQ((*a[2]) < (*a[1]), true);
3211 TEST_EQ((*a[4]) < (*a[3]), true);
3212 TEST_EQ((*a[5]) < (*a[4]), false);
3213 TEST_EQ((*a[5]) < (*a[4]), false);
3214 TEST_EQ((*a[6]) < (*a[5]), true);
3217 #if !defined(FLATBUFFERS_SPAN_MINIMAL)
3218 void FlatbuffersSpanTest() {
3219 // Compile-time checking of non-const [] to const [] conversions.
3220 using flatbuffers::internal::is_span_convertable;
3221 (void)is_span_convertable<int, 1, int, 1>::type(123);
3222 (void)is_span_convertable<const int, 1, int, 1>::type(123);
3223 (void)is_span_convertable<const int64_t, 1, int64_t, 1>::type(123);
3224 (void)is_span_convertable<const uint64_t, 1, uint64_t, 1>::type(123);
3225 (void)is_span_convertable<const int, 1, const int, 1>::type(123);
3226 (void)is_span_convertable<const int64_t, 1, const int64_t, 1>::type(123);
3227 (void)is_span_convertable<const uint64_t, 1, const uint64_t, 1>::type(123);
3229 using flatbuffers::span;
3231 TEST_EQ(c1.size(), 0);
3232 span<char, flatbuffers::dynamic_extent> c2;
3233 TEST_EQ(c2.size(), 0);
3235 TEST_EQ(c3.size(), 0);
3236 TEST_ASSERT(c1.empty() && c2.empty() && c3.empty());
3238 int i_data7[7] = { 0, 1, 2, 3, 4, 5, 6 };
3239 span<int, 7> i1(&i_data7[0], 7);
3240 span<int> i2(i1); // make dynamic from static
3241 TEST_EQ(i1.size(), 7);
3242 TEST_EQ(i1.empty(), false);
3243 TEST_EQ(i1.size(), i2.size());
3244 TEST_EQ(i1.data(), i_data7);
3246 // Make const span from a non-const one.
3247 span<const int, 7> i3(i1);
3248 // Construct from a C-array.
3249 span<int, 7> i4(i_data7);
3250 span<const int, 7> i5(i_data7);
3251 span<int> i6(i_data7);
3252 span<const int> i7(i_data7);
3253 TEST_EQ(i7.size(), 7);
3254 // Check construction from a const array.
3255 const int i_cdata5[5] = { 4, 3, 2, 1, 0 };
3256 span<const int, 5> i8(i_cdata5);
3257 span<const int> i9(i_cdata5);
3258 TEST_EQ(i9.size(), 5);
3259 // Construction from a (ptr, size) pair.
3260 span<int, 7> i10(i_data7, 7);
3261 span<int> i11(i_data7, 7);
3262 TEST_EQ(i11.size(), 7);
3263 span<const int, 5> i12(i_cdata5, 5);
3264 span<const int> i13(i_cdata5, 5);
3265 TEST_EQ(i13.size(), 5);
3266 // Construction from std::array.
3267 std::array<int, 6> i_arr6 = { { 0, 1, 2, 3, 4, 5 } };
3268 span<int, 6> i14(i_arr6);
3269 span<const int, 6> i15(i_arr6);
3270 span<int> i16(i_arr6);
3271 span<const int> i17(i_arr6);
3272 TEST_EQ(i17.size(), 6);
3273 const std::array<int, 8> i_carr8 = { { 0, 1, 2, 3, 4, 5, 6, 7 } };
3274 span<const int, 8> i18(i_carr8);
3275 span<const int> i19(i_carr8);
3276 TEST_EQ(i18.size(), 8);
3277 TEST_EQ(i19.size(), 8);
3279 // Check compatibility with flatbuffers::Array.
3280 int fbs_int3_underlaying[3] = { 0 };
3281 int fbs_int3_data[3] = { 1, 2, 3 };
3282 auto &fbs_int3 = flatbuffers::CastToArray(fbs_int3_underlaying);
3283 fbs_int3.CopyFromSpan(fbs_int3_data);
3284 TEST_EQ(fbs_int3.Get(1), 2);
3285 const int fbs_cint3_data[3] = { 2, 3, 4 };
3286 fbs_int3.CopyFromSpan(fbs_cint3_data);
3287 TEST_EQ(fbs_int3.Get(1), 3);
3288 // Check with Array<Enum, N>
3289 enum class Dummy : uint16_t { Zero = 0, One, Two };
3290 Dummy fbs_dummy3_underlaying[3] = {};
3291 Dummy fbs_dummy3_data[3] = { Dummy::One, Dummy::Two, Dummy::Two };
3292 auto &fbs_dummy3 = flatbuffers::CastToArray(fbs_dummy3_underlaying);
3293 fbs_dummy3.CopyFromSpan(fbs_dummy3_data);
3294 TEST_EQ(fbs_dummy3.Get(1), Dummy::Two);
3297 void FlatbuffersSpanTest() {}
3300 void FixedLengthArrayTest() {
3301 // VS10 does not support typed enums, exclude from tests
3302 #if !defined(_MSC_VER) || _MSC_VER >= 1700
3303 // Generate an ArrayTable containing one ArrayStruct.
3304 flatbuffers::FlatBufferBuilder fbb;
3305 MyGame::Example::NestedStruct nStruct0(MyGame::Example::TestEnum::B);
3306 TEST_NOTNULL(nStruct0.mutable_a());
3307 nStruct0.mutable_a()->Mutate(0, 1);
3308 nStruct0.mutable_a()->Mutate(1, 2);
3309 TEST_NOTNULL(nStruct0.mutable_c());
3310 nStruct0.mutable_c()->Mutate(0, MyGame::Example::TestEnum::C);
3311 nStruct0.mutable_c()->Mutate(1, MyGame::Example::TestEnum::A);
3312 TEST_NOTNULL(nStruct0.mutable_d());
3313 nStruct0.mutable_d()->Mutate(0, flatbuffers::numeric_limits<int64_t>::max());
3314 nStruct0.mutable_d()->Mutate(1, flatbuffers::numeric_limits<int64_t>::min());
3315 MyGame::Example::NestedStruct nStruct1(MyGame::Example::TestEnum::C);
3316 TEST_NOTNULL(nStruct1.mutable_a());
3317 nStruct1.mutable_a()->Mutate(0, 3);
3318 nStruct1.mutable_a()->Mutate(1, 4);
3319 TEST_NOTNULL(nStruct1.mutable_c());
3320 nStruct1.mutable_c()->Mutate(0, MyGame::Example::TestEnum::C);
3321 nStruct1.mutable_c()->Mutate(1, MyGame::Example::TestEnum::A);
3322 TEST_NOTNULL(nStruct1.mutable_d());
3323 nStruct1.mutable_d()->Mutate(0, flatbuffers::numeric_limits<int64_t>::min());
3324 nStruct1.mutable_d()->Mutate(1, flatbuffers::numeric_limits<int64_t>::max());
3325 MyGame::Example::ArrayStruct aStruct(2, 12, 1);
3326 TEST_NOTNULL(aStruct.b());
3327 TEST_NOTNULL(aStruct.mutable_b());
3328 TEST_NOTNULL(aStruct.mutable_d());
3329 TEST_NOTNULL(aStruct.mutable_f());
3330 for (int i = 0; i < aStruct.b()->size(); i++)
3331 aStruct.mutable_b()->Mutate(i, i + 1);
3332 aStruct.mutable_d()->Mutate(0, nStruct0);
3333 aStruct.mutable_d()->Mutate(1, nStruct1);
3334 auto aTable = MyGame::Example::CreateArrayTable(fbb, &aStruct);
3335 MyGame::Example::FinishArrayTableBuffer(fbb, aTable);
3337 // Verify correctness of the ArrayTable.
3338 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
3339 MyGame::Example::VerifyArrayTableBuffer(verifier);
3340 auto p = MyGame::Example::GetMutableArrayTable(fbb.GetBufferPointer());
3341 auto mArStruct = p->mutable_a();
3342 TEST_NOTNULL(mArStruct);
3343 TEST_NOTNULL(mArStruct->b());
3344 TEST_NOTNULL(mArStruct->d());
3345 TEST_NOTNULL(mArStruct->f());
3346 TEST_NOTNULL(mArStruct->mutable_b());
3347 TEST_NOTNULL(mArStruct->mutable_d());
3348 TEST_NOTNULL(mArStruct->mutable_f());
3349 mArStruct->mutable_b()->Mutate(14, -14);
3350 TEST_EQ(mArStruct->a(), 2);
3351 TEST_EQ(mArStruct->b()->size(), 15);
3352 TEST_EQ(mArStruct->b()->Get(aStruct.b()->size() - 1), -14);
3353 TEST_EQ(mArStruct->c(), 12);
3354 TEST_NOTNULL(mArStruct->d()->Get(0));
3355 TEST_NOTNULL(mArStruct->d()->Get(0)->a());
3356 TEST_EQ(mArStruct->d()->Get(0)->a()->Get(0), 1);
3357 TEST_EQ(mArStruct->d()->Get(0)->a()->Get(1), 2);
3358 TEST_NOTNULL(mArStruct->d()->Get(1));
3359 TEST_NOTNULL(mArStruct->d()->Get(1)->a());
3360 TEST_EQ(mArStruct->d()->Get(1)->a()->Get(0), 3);
3361 TEST_EQ(mArStruct->d()->Get(1)->a()->Get(1), 4);
3362 TEST_NOTNULL(mArStruct->mutable_d()->GetMutablePointer(1));
3363 TEST_NOTNULL(mArStruct->mutable_d()->GetMutablePointer(1)->mutable_a());
3364 mArStruct->mutable_d()->GetMutablePointer(1)->mutable_a()->Mutate(1, 5);
3365 TEST_EQ(5, mArStruct->d()->Get(1)->a()->Get(1));
3366 TEST_EQ(MyGame::Example::TestEnum::B, mArStruct->d()->Get(0)->b());
3367 TEST_NOTNULL(mArStruct->d()->Get(0)->c());
3368 TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(0)->c()->Get(0));
3369 TEST_EQ(MyGame::Example::TestEnum::A, mArStruct->d()->Get(0)->c()->Get(1));
3370 TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(),
3371 mArStruct->d()->Get(0)->d()->Get(0));
3372 TEST_EQ(flatbuffers::numeric_limits<int64_t>::min(),
3373 mArStruct->d()->Get(0)->d()->Get(1));
3374 TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(1)->b());
3375 TEST_NOTNULL(mArStruct->d()->Get(1)->c());
3376 TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(1)->c()->Get(0));
3377 TEST_EQ(MyGame::Example::TestEnum::A, mArStruct->d()->Get(1)->c()->Get(1));
3378 TEST_EQ(flatbuffers::numeric_limits<int64_t>::min(),
3379 mArStruct->d()->Get(1)->d()->Get(0));
3380 TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(),
3381 mArStruct->d()->Get(1)->d()->Get(1));
3382 for (int i = 0; i < mArStruct->b()->size() - 1; i++)
3383 TEST_EQ(mArStruct->b()->Get(i), i + 1);
3385 TEST_EQ(0, reinterpret_cast<uintptr_t>(mArStruct->d()) % 8);
3386 TEST_EQ(0, reinterpret_cast<uintptr_t>(mArStruct->f()) % 8);
3388 // Check if default constructor set all memory zero
3389 const size_t arr_size = sizeof(MyGame::Example::ArrayStruct);
3390 char non_zero_memory[arr_size];
3391 // set memory chunk of size ArrayStruct to 1's
3392 std::memset(static_cast<void *>(non_zero_memory), 1, arr_size);
3393 // after placement-new it should be all 0's
3394 #if defined (_MSC_VER) && defined (_DEBUG)
3397 MyGame::Example::ArrayStruct *ap = new (non_zero_memory) MyGame::Example::ArrayStruct;
3398 #if defined (_MSC_VER) && defined (_DEBUG)
3399 #define new DEBUG_NEW
3402 for (size_t i = 0; i < arr_size; ++i) {
3403 TEST_EQ(non_zero_memory[i], 0);
3408 #if !defined(FLATBUFFERS_SPAN_MINIMAL) && (!defined(_MSC_VER) || _MSC_VER >= 1700)
3409 void FixedLengthArrayConstructorTest() {
3410 const int32_t nested_a[2] = { 1, 2 };
3411 MyGame::Example::TestEnum nested_c[2] = { MyGame::Example::TestEnum::A,
3412 MyGame::Example::TestEnum::B };
3413 const int64_t int64_2[2] = { -2, -1 };
3415 std::array<MyGame::Example::NestedStruct, 2> init_d = {
3416 { MyGame::Example::NestedStruct(nested_a, MyGame::Example::TestEnum::B,
3418 MyGame::Example::NestedStruct(nested_a, MyGame::Example::TestEnum::A,
3420 std::array<int64_t, 2>{ { 12, 13 } }) }
3423 MyGame::Example::ArrayStruct arr_struct(
3425 std::array<int32_t, 0xF>{
3426 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } },
3427 -17, init_d, 10, int64_2);
3428 TEST_EQ(arr_struct.a(), 8.125);
3429 TEST_EQ(arr_struct.b()->Get(2), 3);
3430 TEST_EQ(arr_struct.c(), -17);
3432 TEST_NOTNULL(arr_struct.d());
3433 const auto &arr_d_0 = *arr_struct.d()->Get(0);
3434 TEST_EQ(arr_d_0.a()->Get(0), 1);
3435 TEST_EQ(arr_d_0.a()->Get(1), 2);
3436 TEST_EQ(arr_d_0.b(), MyGame::Example::TestEnum::B);
3437 TEST_EQ(arr_d_0.c()->Get(0), MyGame::Example::TestEnum::A);
3438 TEST_EQ(arr_d_0.c()->Get(1), MyGame::Example::TestEnum::B);
3439 TEST_EQ(arr_d_0.d()->Get(0), -2);
3440 TEST_EQ(arr_d_0.d()->Get(1), -1);
3441 const auto &arr_d_1 = *arr_struct.d()->Get(1);
3442 TEST_EQ(arr_d_1.a()->Get(0), 1);
3443 TEST_EQ(arr_d_1.a()->Get(1), 2);
3444 TEST_EQ(arr_d_1.b(), MyGame::Example::TestEnum::A);
3445 TEST_EQ(arr_d_1.c()->Get(0), MyGame::Example::TestEnum::A);
3446 TEST_EQ(arr_d_1.c()->Get(1), MyGame::Example::TestEnum::B);
3447 TEST_EQ(arr_d_1.d()->Get(0), 12);
3448 TEST_EQ(arr_d_1.d()->Get(1), 13);
3450 TEST_EQ(arr_struct.e(), 10);
3451 TEST_EQ(arr_struct.f()->Get(0), -2);
3452 TEST_EQ(arr_struct.f()->Get(1), -1);
3455 void FixedLengthArrayConstructorTest() {
3459 void NativeTypeTest() {
3462 Geometry::ApplicationDataT src_data;
3463 src_data.vectors.reserve(N);
3465 for (int i = 0; i < N; ++i) {
3466 src_data.vectors.push_back(
3467 Native::Vector3D(10 * i + 0.1f, 10 * i + 0.2f, 10 * i + 0.3f));
3470 flatbuffers::FlatBufferBuilder fbb;
3471 fbb.Finish(Geometry::ApplicationData::Pack(fbb, &src_data));
3473 auto dstDataT = Geometry::UnPackApplicationData(fbb.GetBufferPointer());
3475 for (int i = 0; i < N; ++i) {
3476 Native::Vector3D &v = dstDataT->vectors[i];
3477 TEST_EQ(v.x, 10 * i + 0.1f);
3478 TEST_EQ(v.y, 10 * i + 0.2f);
3479 TEST_EQ(v.z, 10 * i + 0.3f);
3483 void FixedLengthArrayJsonTest(bool binary) {
3484 // VS10 does not support typed enums, exclude from tests
3485 #if !defined(_MSC_VER) || _MSC_VER >= 1700
3486 // load FlatBuffer schema (.fbs) and JSON from disk
3487 std::string schemafile;
3488 std::string jsonfile;
3490 flatbuffers::LoadFile(
3491 (test_data_path + "arrays_test." + (binary ? "bfbs" : "fbs")).c_str(),
3492 binary, &schemafile),
3494 TEST_EQ(flatbuffers::LoadFile((test_data_path + "arrays_test.golden").c_str(),
3498 // parse schema first, so we can use it to parse the data after
3499 flatbuffers::Parser parserOrg, parserGen;
3501 flatbuffers::Verifier verifier(
3502 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
3504 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
3505 TEST_EQ(parserOrg.Deserialize((const uint8_t *)schemafile.c_str(),
3508 TEST_EQ(parserGen.Deserialize((const uint8_t *)schemafile.c_str(),
3512 TEST_EQ(parserOrg.Parse(schemafile.c_str()), true);
3513 TEST_EQ(parserGen.Parse(schemafile.c_str()), true);
3515 TEST_EQ(parserOrg.Parse(jsonfile.c_str()), true);
3517 // First, verify it, just in case:
3518 flatbuffers::Verifier verifierOrg(parserOrg.builder_.GetBufferPointer(),
3519 parserOrg.builder_.GetSize());
3520 TEST_EQ(VerifyArrayTableBuffer(verifierOrg), true);
3523 std::string jsonGen;
3525 GenerateText(parserOrg, parserOrg.builder_.GetBufferPointer(), &jsonGen),
3529 TEST_EQ(parserGen.Parse(jsonGen.c_str()), true);
3531 // Verify buffer from generated JSON
3532 flatbuffers::Verifier verifierGen(parserGen.builder_.GetBufferPointer(),
3533 parserGen.builder_.GetSize());
3534 TEST_EQ(VerifyArrayTableBuffer(verifierGen), true);
3536 // Compare generated buffer to original
3537 TEST_EQ(parserOrg.builder_.GetSize(), parserGen.builder_.GetSize());
3538 TEST_EQ(std::memcmp(parserOrg.builder_.GetBufferPointer(),
3539 parserGen.builder_.GetBufferPointer(),
3540 parserOrg.builder_.GetSize()),
3547 void TestEmbeddedBinarySchema() {
3548 // load JSON from disk
3549 std::string jsonfile;
3550 TEST_EQ(flatbuffers::LoadFile(
3551 (test_data_path + "monsterdata_test.golden").c_str(), false,
3555 // parse schema first, so we can use it to parse the data after
3556 flatbuffers::Parser parserOrg, parserGen;
3557 flatbuffers::Verifier verifier(MyGame::Example::MonsterBinarySchema::data(),
3558 MyGame::Example::MonsterBinarySchema::size());
3559 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
3560 TEST_EQ(parserOrg.Deserialize(MyGame::Example::MonsterBinarySchema::data(),
3561 MyGame::Example::MonsterBinarySchema::size()),
3563 TEST_EQ(parserGen.Deserialize(MyGame::Example::MonsterBinarySchema::data(),
3564 MyGame::Example::MonsterBinarySchema::size()),
3566 TEST_EQ(parserOrg.Parse(jsonfile.c_str()), true);
3568 // First, verify it, just in case:
3569 flatbuffers::Verifier verifierOrg(parserOrg.builder_.GetBufferPointer(),
3570 parserOrg.builder_.GetSize());
3571 TEST_EQ(VerifyMonsterBuffer(verifierOrg), true);
3574 std::string jsonGen;
3576 GenerateText(parserOrg, parserOrg.builder_.GetBufferPointer(), &jsonGen),
3580 TEST_EQ(parserGen.Parse(jsonGen.c_str()), true);
3582 // Verify buffer from generated JSON
3583 flatbuffers::Verifier verifierGen(parserGen.builder_.GetBufferPointer(),
3584 parserGen.builder_.GetSize());
3585 TEST_EQ(VerifyMonsterBuffer(verifierGen), true);
3587 // Compare generated buffer to original
3588 TEST_EQ(parserOrg.builder_.GetSize(), parserGen.builder_.GetSize());
3589 TEST_EQ(std::memcmp(parserOrg.builder_.GetBufferPointer(),
3590 parserGen.builder_.GetBufferPointer(),
3591 parserOrg.builder_.GetSize()),
3595 void OptionalScalarsTest() {
3596 // Simple schemas and a "has optional scalar" sentinal.
3597 std::vector<std::string> schemas;
3598 schemas.push_back("table Monster { mana : int; }");
3599 schemas.push_back("table Monster { mana : int = 42; }");
3600 schemas.push_back("table Monster { mana : int = null; }");
3601 schemas.push_back("table Monster { mana : long; }");
3602 schemas.push_back("table Monster { mana : long = 42; }");
3603 schemas.push_back("table Monster { mana : long = null; }");
3604 schemas.push_back("table Monster { mana : float; }");
3605 schemas.push_back("table Monster { mana : float = 42; }");
3606 schemas.push_back("table Monster { mana : float = null; }");
3607 schemas.push_back("table Monster { mana : double; }");
3608 schemas.push_back("table Monster { mana : double = 42; }");
3609 schemas.push_back("table Monster { mana : double = null; }");
3610 schemas.push_back("table Monster { mana : bool; }");
3611 schemas.push_back("table Monster { mana : bool = 42; }");
3612 schemas.push_back("table Monster { mana : bool = null; }");
3613 schemas.push_back("enum Enum: int {A=0, B=1} "
3614 "table Monster { mana : Enum; }");
3615 schemas.push_back("enum Enum: int {A=0, B=1} "
3616 "table Monster { mana : Enum = B; }");
3617 schemas.push_back("enum Enum: int {A=0, B=1} "
3618 "table Monster { mana : Enum = null; }");
3620 // Check the FieldDef is correctly set.
3621 for (auto schema = schemas.begin(); schema < schemas.end(); schema++) {
3622 const bool has_null = schema->find("null") != std::string::npos;
3623 flatbuffers::Parser parser;
3624 TEST_ASSERT(parser.Parse(schema->c_str()));
3625 const auto *mana = parser.structs_.Lookup("Monster")->fields.Lookup("mana");
3626 TEST_EQ(mana->optional, has_null);
3629 // Test if nullable scalars are allowed for each language.
3630 for (unsigned lang = 1; lang < flatbuffers::IDLOptions::kMAX; lang <<= 1) {
3631 flatbuffers::IDLOptions opts;
3632 opts.lang_to_generate = lang;
3633 if (false == flatbuffers::Parser::SupportsOptionalScalars(opts)) {
3636 for (auto schema = schemas.begin(); schema < schemas.end(); schema++) {
3637 flatbuffers::Parser parser(opts);
3638 auto done = parser.Parse(schema->c_str());
3639 TEST_EQ_STR(parser.error_.c_str(), "");
3644 // test C++ nullable
3645 flatbuffers::FlatBufferBuilder fbb;
3646 FinishScalarStuffBuffer(
3647 fbb, optional_scalars::CreateScalarStuff(fbb, 1, static_cast<int8_t>(2)));
3648 auto opts = optional_scalars::GetMutableScalarStuff(fbb.GetBufferPointer());
3649 TEST_ASSERT(!opts->maybe_bool());
3650 TEST_ASSERT(!opts->maybe_f32().has_value());
3651 TEST_ASSERT(opts->maybe_i8().has_value());
3652 TEST_EQ(opts->maybe_i8().value(), 2);
3653 TEST_ASSERT(opts->mutate_maybe_i8(3));
3654 TEST_ASSERT(opts->maybe_i8().has_value());
3655 TEST_EQ(opts->maybe_i8().value(), 3);
3656 TEST_ASSERT(!opts->mutate_maybe_i16(-10));
3658 optional_scalars::ScalarStuffT obj;
3659 TEST_ASSERT(!obj.maybe_bool);
3660 TEST_ASSERT(!obj.maybe_f32.has_value());
3661 opts->UnPackTo(&obj);
3662 TEST_ASSERT(!obj.maybe_bool);
3663 TEST_ASSERT(!obj.maybe_f32.has_value());
3664 TEST_ASSERT(obj.maybe_i8.has_value() && obj.maybe_i8.value() == 3);
3665 TEST_ASSERT(obj.maybe_i8 && *obj.maybe_i8 == 3);
3667 obj.maybe_enum = optional_scalars::OptionalByte_Two;
3670 FinishScalarStuffBuffer(fbb, optional_scalars::ScalarStuff::Pack(fbb, &obj));
3671 opts = optional_scalars::GetMutableScalarStuff(fbb.GetBufferPointer());
3672 TEST_ASSERT(opts->maybe_i8().has_value());
3673 TEST_EQ(opts->maybe_i8().value(), 3);
3674 TEST_ASSERT(opts->maybe_i32().has_value());
3675 TEST_EQ(opts->maybe_i32().value(), -1);
3676 TEST_EQ(opts->maybe_enum().value(), optional_scalars::OptionalByte_Two);
3677 TEST_ASSERT(opts->maybe_i32() == flatbuffers::Optional<int64_t>(-1));
3680 void ParseFlexbuffersFromJsonWithNullTest() {
3681 // Test nulls are handled appropriately through flexbuffers to exercise other
3682 // code paths of ParseSingleValue in the optional scalars change.
3683 // TODO(cneo): Json -> Flatbuffers test once some language can generate code
3684 // with optional scalars.
3686 char json[] = "{\"opt_field\": 123 }";
3687 flatbuffers::Parser parser;
3688 flexbuffers::Builder flexbuild;
3689 parser.ParseFlexBuffer(json, nullptr, &flexbuild);
3690 auto root = flexbuffers::GetRoot(flexbuild.GetBuffer());
3691 TEST_EQ(root.AsMap()["opt_field"].AsInt64(), 123);
3694 char json[] = "{\"opt_field\": 123.4 }";
3695 flatbuffers::Parser parser;
3696 flexbuffers::Builder flexbuild;
3697 parser.ParseFlexBuffer(json, nullptr, &flexbuild);
3698 auto root = flexbuffers::GetRoot(flexbuild.GetBuffer());
3699 TEST_EQ(root.AsMap()["opt_field"].AsDouble(), 123.4);
3702 char json[] = "{\"opt_field\": null }";
3703 flatbuffers::Parser parser;
3704 flexbuffers::Builder flexbuild;
3705 parser.ParseFlexBuffer(json, nullptr, &flexbuild);
3706 auto root = flexbuffers::GetRoot(flexbuild.GetBuffer());
3707 TEST_ASSERT(!root.AsMap().IsTheEmptyMap());
3708 TEST_ASSERT(root.AsMap()["opt_field"].IsNull());
3709 TEST_EQ(root.ToString(), std::string("{ opt_field: null }"));
3713 int FlatBufferTests() {
3716 // Run our various test suites:
3719 auto flatbuf1 = CreateFlatBufferTest(rawbuf);
3720 #if !defined(FLATBUFFERS_CPP98_STL)
3721 auto flatbuf = std::move(flatbuf1); // Test move assignment.
3723 auto &flatbuf = flatbuf1;
3724 #endif // !defined(FLATBUFFERS_CPP98_STL)
3726 TriviallyCopyableTest();
3728 AccessFlatBufferTest(reinterpret_cast<const uint8_t *>(rawbuf.c_str()),
3730 AccessFlatBufferTest(flatbuf.data(), flatbuf.size());
3732 MutateFlatBuffersTest(flatbuf.data(), flatbuf.size());
3734 ObjectFlatBuffersTest(flatbuf.data());
3736 MiniReflectFlatBuffersTest(flatbuf.data());
3737 MiniReflectFixedLengthArrayTest();
3741 #ifndef FLATBUFFERS_NO_FILE_TESTS
3742 #ifdef FLATBUFFERS_TEST_PATH_PREFIX
3743 test_data_path = FLATBUFFERS_STRING(FLATBUFFERS_TEST_PATH_PREFIX) +
3746 ParseAndGenerateTextTest(false);
3747 ParseAndGenerateTextTest(true);
3748 FixedLengthArrayJsonTest(false);
3749 FixedLengthArrayJsonTest(true);
3750 ReflectionTest(flatbuf.data(), flatbuf.size());
3752 ParseProtoTestWithSuffix();
3753 ParseProtoTestWithIncludes();
3755 UnionDeprecationTest();
3757 LoadVerifyBinaryTest();
3758 GenerateTableTextTest();
3759 TestEmbeddedBinarySchema();
3771 EnumOutOfRangeTest();
3772 IntegerOutOfRangeTest();
3773 IntegerBoundaryTest();
3775 UnicodeTestAllowNonUTF8();
3776 UnicodeTestGenerateTextFailsOnNonUTF8();
3777 UnicodeSurrogatesTest();
3778 UnicodeInvalidSurrogatesTest();
3780 UnknownFieldsTest();
3782 InvalidNestedFlatbufferTest();
3784 ParseProtoBufAsciiTest();
3787 CreateSharedStringTest();
3791 FlexBuffersDeprecatedTest();
3792 UninitializedVectorTest();
3793 EqualOperatorTest();
3798 TestMonsterExtraFloats();
3799 FixedLengthArrayTest();
3801 OptionalScalarsTest();
3802 ParseFlexbuffersFromJsonWithNullTest();
3803 FlatbuffersSpanTest();
3804 FixedLengthArrayConstructorTest();
3808 int main(int /*argc*/, const char * /*argv*/[]) {
3811 std::string req_locale;
3812 if (flatbuffers::ReadEnvironmentVariable("FLATBUFFERS_TEST_LOCALE",
3814 TEST_OUTPUT_LINE("The environment variable FLATBUFFERS_TEST_LOCALE=%s",
3815 req_locale.c_str());
3816 req_locale = flatbuffers::RemoveStringQuotes(req_locale);
3817 std::string the_locale;
3819 flatbuffers::SetGlobalTestLocale(req_locale.c_str(), &the_locale));
3820 TEST_OUTPUT_LINE("The global C-locale changed: %s", the_locale.c_str());
3824 FlatBufferBuilderTest();
3826 if (!testing_fails) {
3827 TEST_OUTPUT_LINE("ALL TESTS PASSED");
3829 TEST_OUTPUT_LINE("%d FAILED TESTS", testing_fails);
3831 return CloseTestEngine();