1 /*
2 * Copyright 2014 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <stdint.h>
17
18 #include <cmath>
19 #include <memory>
20 #include <string>
21
22 #include "flatbuffers/flatbuffers.h"
23 #include "flatbuffers/idl.h"
24 #include "flatbuffers/minireflect.h"
25 #include "flatbuffers/registry.h"
26 #include "flatbuffers/stl_emulation.h"
27 #include "flatbuffers/util.h"
28 #include "monster_test_generated.h"
29 #include "namespace_test/namespace_test1_generated.h"
30 #include "namespace_test/namespace_test2_generated.h"
31 #include "optional_scalars_generated.h"
32 #include "union_vector/union_vector_generated.h"
33 #if !defined(_MSC_VER) || _MSC_VER >= 1700
34 # include "arrays_test_generated.h"
35 # include "evolution_test/evolution_v1_generated.h"
36 # include "evolution_test/evolution_v2_generated.h"
37 # include "monster_extra_generated.h"
38 #endif
39
40 #include "flatbuffers/flexbuffers.h"
41 #include "monster_test_bfbs_generated.h" // Generated using --bfbs-comments --bfbs-builtins --cpp --bfbs-gen-embed
42 #include "native_type_test_generated.h"
43 #include "test_assert.h"
44
45 void FlatBufferBuilderTest();
46
47 namespace {
48
49 // clang-format off
50 // Check that char* and uint8_t* are interoperable types.
51 // The reinterpret_cast<> between the pointers are used to simplify data loading.
52 static_assert(flatbuffers::is_same<uint8_t, char>::value ||
53 flatbuffers::is_same<uint8_t, unsigned char>::value,
54 "unexpected uint8_t type");
55
56 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
57 // Ensure IEEE-754 support if tests of floats with NaN/Inf will run.
58 static_assert(std::numeric_limits<float>::is_iec559 &&
59 std::numeric_limits<double>::is_iec559,
60 "IEC-559 (IEEE-754) standard required");
61 #endif
62 // clang-format on
63
64 // Shortcuts for the infinity.
65 static const auto infinity_f = std::numeric_limits<float>::infinity();
66 static const auto infinity_d = std::numeric_limits<double>::infinity();
67
68 using namespace MyGame::Example;
69
70 // Include simple random number generator to ensure results will be the
71 // same cross platform.
72 // http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator
73 uint32_t lcg_seed = 48271;
lcg_rand()74 uint32_t lcg_rand() {
75 return lcg_seed =
76 (static_cast<uint64_t>(lcg_seed) * 279470273UL) % 4294967291UL;
77 }
lcg_reset()78 void lcg_reset() { lcg_seed = 48271; }
79
80 std::string test_data_path =
81 #ifdef BAZEL_TEST_DATA_PATH
82 "../com_github_google_flatbuffers/tests/";
83 #else
84 "tests/";
85 #endif
86
87 // example of how to build up a serialized buffer algorithmically:
CreateFlatBufferTest(std::string & buffer)88 flatbuffers::DetachedBuffer CreateFlatBufferTest(std::string &buffer) {
89 flatbuffers::FlatBufferBuilder builder;
90
91 auto vec = Vec3(1, 2, 3, 0, Color_Red, Test(10, 20));
92
93 auto name = builder.CreateString("MyMonster");
94
95 // Use the initializer_list specialization of CreateVector.
96 auto inventory =
97 builder.CreateVector<uint8_t>({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
98
99 // Alternatively, create the vector first, and fill in data later:
100 // unsigned char *inv_buf = nullptr;
101 // auto inventory = builder.CreateUninitializedVector<unsigned char>(
102 // 10, &inv_buf);
103 // memcpy(inv_buf, inv_data, 10);
104
105 Test tests[] = { Test(10, 20), Test(30, 40) };
106 auto testv = builder.CreateVectorOfStructs(tests, 2);
107
108 // Create a vector of structures from a lambda.
109 auto testv2 = builder.CreateVectorOfStructs<Test>(
110 2, [&](size_t i, Test *s) -> void { *s = tests[i]; });
111
112 // create monster with very few fields set:
113 // (same functionality as CreateMonster below, but sets fields manually)
114 flatbuffers::Offset<Monster> mlocs[3];
115 auto fred = builder.CreateString("Fred");
116 auto barney = builder.CreateString("Barney");
117 auto wilma = builder.CreateString("Wilma");
118 MonsterBuilder mb1(builder);
119 mb1.add_name(fred);
120 mlocs[0] = mb1.Finish();
121 MonsterBuilder mb2(builder);
122 mb2.add_name(barney);
123 mb2.add_hp(1000);
124 mlocs[1] = mb2.Finish();
125 MonsterBuilder mb3(builder);
126 mb3.add_name(wilma);
127 mlocs[2] = mb3.Finish();
128
129 // Create an array of strings. Also test string pooling, and lambdas.
130 auto vecofstrings =
131 builder.CreateVector<flatbuffers::Offset<flatbuffers::String>>(
132 4,
133 [](size_t i, flatbuffers::FlatBufferBuilder *b)
134 -> flatbuffers::Offset<flatbuffers::String> {
135 static const char *names[] = { "bob", "fred", "bob", "fred" };
136 return b->CreateSharedString(names[i]);
137 },
138 &builder);
139
140 // Creating vectors of strings in one convenient call.
141 std::vector<std::string> names2;
142 names2.push_back("jane");
143 names2.push_back("mary");
144 auto vecofstrings2 = builder.CreateVectorOfStrings(names2);
145
146 // Creating vectors from types that are different from std::string
147 std::vector<const char *> names3;
148 names3.push_back("foo");
149 names3.push_back("bar");
150 builder.CreateVectorOfStrings(names3); // Also an accepted type
151
152 #ifdef FLATBUFFERS_HAS_STRING_VIEW
153 std::vector<flatbuffers::string_view> names4;
154 names3.push_back("baz");
155 names3.push_back("quux");
156 builder.CreateVectorOfStrings(names4); // Also an accepted type
157 #endif
158
159 // Make sure the template deduces an initializer as std::vector<std::string>
160 builder.CreateVectorOfStrings({ "hello", "world" });
161
162 // Create many vectors of strings
163 std::vector<std::string> manyNames;
164 for (auto i = 0; i < 100; i++) { manyNames.push_back("john_doe"); }
165 auto manyNamesVec = builder.CreateVectorOfStrings(manyNames);
166 TEST_EQ(false, manyNamesVec.IsNull());
167 auto manyNamesVec2 =
168 builder.CreateVectorOfStrings(manyNames.cbegin(), manyNames.cend());
169 TEST_EQ(false, manyNamesVec2.IsNull());
170
171 // Create an array of sorted tables, can be used with binary search when read:
172 auto vecoftables = builder.CreateVectorOfSortedTables(mlocs, 3);
173
174 // Create an array of sorted structs,
175 // can be used with binary search when read:
176 std::vector<Ability> abilities;
177 abilities.push_back(Ability(4, 40));
178 abilities.push_back(Ability(3, 30));
179 abilities.push_back(Ability(2, 20));
180 abilities.push_back(Ability(0, 0));
181 auto vecofstructs = builder.CreateVectorOfSortedStructs(&abilities);
182
183 flatbuffers::Offset<Stat> mlocs_stats[1];
184 auto miss = builder.CreateString("miss");
185 StatBuilder mb_miss(builder);
186 mb_miss.add_id(miss);
187 mb_miss.add_val(0);
188 mb_miss.add_count(0); // key
189 mlocs_stats[0] = mb_miss.Finish();
190 auto vec_of_stats = builder.CreateVectorOfSortedTables(mlocs_stats, 1);
191
192 // Create a nested FlatBuffer.
193 // Nested FlatBuffers are stored in a ubyte vector, which can be convenient
194 // since they can be memcpy'd around much easier than other FlatBuffer
195 // values. They have little overhead compared to storing the table directly.
196 // As a test, create a mostly empty Monster buffer:
197 flatbuffers::FlatBufferBuilder nested_builder;
198 auto nmloc = CreateMonster(nested_builder, nullptr, 0, 0,
199 nested_builder.CreateString("NestedMonster"));
200 FinishMonsterBuffer(nested_builder, nmloc);
201 // Now we can store the buffer in the parent. Note that by default, vectors
202 // are only aligned to their elements or size field, so in this case if the
203 // buffer contains 64-bit elements, they may not be correctly aligned. We fix
204 // that with:
205 builder.ForceVectorAlignment(nested_builder.GetSize(), sizeof(uint8_t),
206 nested_builder.GetBufferMinAlignment());
207 // If for whatever reason you don't have the nested_builder available, you
208 // can substitute flatbuffers::largest_scalar_t (64-bit) for the alignment, or
209 // the largest force_align value in your schema if you're using it.
210 auto nested_flatbuffer_vector = builder.CreateVector(
211 nested_builder.GetBufferPointer(), nested_builder.GetSize());
212
213 // Test a nested FlexBuffer:
214 flexbuffers::Builder flexbuild;
215 flexbuild.Int(1234);
216 flexbuild.Finish();
217 auto flex = builder.CreateVector(flexbuild.GetBuffer());
218 // Test vector of enums.
219 Color colors[] = { Color_Blue, Color_Green };
220 // We use this special creation function because we have an array of
221 // pre-C++11 (enum class) enums whose size likely is int, yet its declared
222 // type in the schema is byte.
223 auto vecofcolors = builder.CreateVectorScalarCast<uint8_t, Color>(colors, 2);
224
225 // shortcut for creating monster with all fields set:
226 auto mloc = CreateMonster(
227 builder, &vec, 150, 80, name, inventory, Color_Blue, Any_Monster,
228 mlocs[1].Union(), // Store a union.
229 testv, vecofstrings, vecoftables, 0, nested_flatbuffer_vector, 0, false,
230 0, 0, 0, 0, 0, 0, 0, 0, 0, 3.14159f, 3.0f, 0.0f, vecofstrings2,
231 vecofstructs, flex, testv2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
232 AnyUniqueAliases_NONE, 0, AnyAmbiguousAliases_NONE, 0, vecofcolors,
233 MyGame::Example::Race_None, 0, vec_of_stats);
234
235 FinishMonsterBuffer(builder, mloc);
236
237 // clang-format off
238 #ifdef FLATBUFFERS_TEST_VERBOSE
239 // print byte data for debugging:
240 auto p = builder.GetBufferPointer();
241 for (flatbuffers::uoffset_t i = 0; i < builder.GetSize(); i++)
242 printf("%d ", p[i]);
243 #endif
244 // clang-format on
245
246 // return the buffer for the caller to use.
247 auto bufferpointer =
248 reinterpret_cast<const char *>(builder.GetBufferPointer());
249 buffer.assign(bufferpointer, bufferpointer + builder.GetSize());
250
251 return builder.Release();
252 }
253
254 // example of accessing a buffer loaded in memory:
AccessFlatBufferTest(const uint8_t * flatbuf,size_t length,bool pooled=true)255 void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length,
256 bool pooled = true) {
257 // First, verify the buffers integrity (optional)
258 flatbuffers::Verifier verifier(flatbuf, length);
259 std::vector<uint8_t> flex_reuse_tracker;
260 verifier.SetFlexReuseTracker(&flex_reuse_tracker);
261 TEST_EQ(VerifyMonsterBuffer(verifier), true);
262
263 // clang-format off
264 #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
265 std::vector<uint8_t> test_buff;
266 test_buff.resize(length * 2);
267 std::memcpy(&test_buff[0], flatbuf, length);
268 std::memcpy(&test_buff[length], flatbuf, length);
269
270 flatbuffers::Verifier verifier1(&test_buff[0], length);
271 TEST_EQ(VerifyMonsterBuffer(verifier1), true);
272 TEST_EQ(verifier1.GetComputedSize(), length);
273
274 flatbuffers::Verifier verifier2(&test_buff[length], length);
275 TEST_EQ(VerifyMonsterBuffer(verifier2), true);
276 TEST_EQ(verifier2.GetComputedSize(), length);
277 #endif
278 // clang-format on
279
280 TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0);
281 TEST_EQ(MonsterBufferHasIdentifier(flatbuf), true);
282 TEST_EQ(strcmp(MonsterExtension(), "mon"), 0);
283
284 // Access the buffer from the root.
285 auto monster = GetMonster(flatbuf);
286
287 TEST_EQ(monster->hp(), 80);
288 TEST_EQ(monster->mana(), 150); // default
289 TEST_EQ_STR(monster->name()->c_str(), "MyMonster");
290 // Can't access the following field, it is deprecated in the schema,
291 // which means accessors are not generated:
292 // monster.friendly()
293
294 auto pos = monster->pos();
295 TEST_NOTNULL(pos);
296 TEST_EQ(pos->z(), 3);
297 TEST_EQ(pos->test3().a(), 10);
298 TEST_EQ(pos->test3().b(), 20);
299
300 auto inventory = monster->inventory();
301 TEST_EQ(VectorLength(inventory), 10UL); // Works even if inventory is null.
302 TEST_NOTNULL(inventory);
303 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
304 // Check compatibilty of iterators with STL.
305 std::vector<unsigned char> inv_vec(inventory->begin(), inventory->end());
306 size_t n = 0;
307 for (auto it = inventory->begin(); it != inventory->end(); ++it, ++n) {
308 auto indx = it - inventory->begin();
309 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
310 TEST_EQ(*it, inv_data[indx]);
311 }
312 TEST_EQ(n, inv_vec.size());
313
314 n = 0;
315 for (auto it = inventory->cbegin(); it != inventory->cend(); ++it, ++n) {
316 auto indx = it - inventory->cbegin();
317 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
318 TEST_EQ(*it, inv_data[indx]);
319 }
320 TEST_EQ(n, inv_vec.size());
321
322 n = 0;
323 for (auto it = inventory->rbegin(); it != inventory->rend(); ++it, ++n) {
324 auto indx = inventory->rend() - it - 1;
325 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
326 TEST_EQ(*it, inv_data[indx]);
327 }
328 TEST_EQ(n, inv_vec.size());
329
330 n = 0;
331 for (auto it = inventory->crbegin(); it != inventory->crend(); ++it, ++n) {
332 auto indx = inventory->crend() - it - 1;
333 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
334 TEST_EQ(*it, inv_data[indx]);
335 }
336 TEST_EQ(n, inv_vec.size());
337
338 TEST_EQ(monster->color(), Color_Blue);
339
340 // Example of accessing a union:
341 TEST_EQ(monster->test_type(), Any_Monster); // First make sure which it is.
342 auto monster2 = reinterpret_cast<const Monster *>(monster->test());
343 TEST_NOTNULL(monster2);
344 TEST_EQ_STR(monster2->name()->c_str(), "Fred");
345
346 // Example of accessing a vector of strings:
347 auto vecofstrings = monster->testarrayofstring();
348 TEST_EQ(vecofstrings->size(), 4U);
349 TEST_EQ_STR(vecofstrings->Get(0)->c_str(), "bob");
350 TEST_EQ_STR(vecofstrings->Get(1)->c_str(), "fred");
351 if (pooled) {
352 // These should have pointer equality because of string pooling.
353 TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str());
354 TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str());
355 }
356
357 auto vecofstrings2 = monster->testarrayofstring2();
358 if (vecofstrings2) {
359 TEST_EQ(vecofstrings2->size(), 2U);
360 TEST_EQ_STR(vecofstrings2->Get(0)->c_str(), "jane");
361 TEST_EQ_STR(vecofstrings2->Get(1)->c_str(), "mary");
362 }
363
364 // Example of accessing a vector of tables:
365 auto vecoftables = monster->testarrayoftables();
366 TEST_EQ(vecoftables->size(), 3U);
367 for (auto it = vecoftables->begin(); it != vecoftables->end(); ++it) {
368 TEST_EQ(strlen(it->name()->c_str()) >= 4, true);
369 }
370 TEST_EQ_STR(vecoftables->Get(0)->name()->c_str(), "Barney");
371 TEST_EQ(vecoftables->Get(0)->hp(), 1000);
372 TEST_EQ_STR(vecoftables->Get(1)->name()->c_str(), "Fred");
373 TEST_EQ_STR(vecoftables->Get(2)->name()->c_str(), "Wilma");
374 TEST_NOTNULL(vecoftables->LookupByKey("Barney"));
375 TEST_NOTNULL(vecoftables->LookupByKey("Fred"));
376 TEST_NOTNULL(vecoftables->LookupByKey("Wilma"));
377
378 // Test accessing a vector of sorted structs
379 auto vecofstructs = monster->testarrayofsortedstruct();
380 if (vecofstructs) { // not filled in monster_test.bfbs
381 for (flatbuffers::uoffset_t i = 0; i < vecofstructs->size() - 1; i++) {
382 auto left = vecofstructs->Get(i);
383 auto right = vecofstructs->Get(i + 1);
384 TEST_EQ(true, (left->KeyCompareLessThan(right)));
385 }
386 TEST_NOTNULL(vecofstructs->LookupByKey(0)); // test default value
387 TEST_NOTNULL(vecofstructs->LookupByKey(3));
388 TEST_EQ(static_cast<const Ability *>(nullptr),
389 vecofstructs->LookupByKey(5));
390 }
391
392 if (auto vec_of_stat = monster->scalar_key_sorted_tables()) {
393 auto stat_0 = vec_of_stat->LookupByKey(static_cast<uint16_t>(0u));
394 TEST_NOTNULL(stat_0);
395 TEST_NOTNULL(stat_0->id());
396 TEST_EQ(0, stat_0->count());
397 TEST_EQ_STR("miss", stat_0->id()->c_str());
398 }
399
400 // Test nested FlatBuffers if available:
401 auto nested_buffer = monster->testnestedflatbuffer();
402 if (nested_buffer) {
403 // nested_buffer is a vector of bytes you can memcpy. However, if you
404 // actually want to access the nested data, this is a convenient
405 // accessor that directly gives you the root table:
406 auto nested_monster = monster->testnestedflatbuffer_nested_root();
407 TEST_EQ_STR(nested_monster->name()->c_str(), "NestedMonster");
408 }
409
410 // Test flexbuffer if available:
411 auto flex = monster->flex();
412 // flex is a vector of bytes you can memcpy etc.
413 TEST_EQ(flex->size(), 4); // Encoded FlexBuffer bytes.
414 // However, if you actually want to access the nested data, this is a
415 // convenient accessor that directly gives you the root value:
416 TEST_EQ(monster->flex_flexbuffer_root().AsInt16(), 1234);
417
418 // Test vector of enums:
419 auto colors = monster->vector_of_enums();
420 if (colors) {
421 TEST_EQ(colors->size(), 2);
422 TEST_EQ(colors->Get(0), Color_Blue);
423 TEST_EQ(colors->Get(1), Color_Green);
424 }
425
426 // Since Flatbuffers uses explicit mechanisms to override the default
427 // compiler alignment, double check that the compiler indeed obeys them:
428 // (Test consists of a short and byte):
429 TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
430 TEST_EQ(sizeof(Test), 4UL);
431
432 const flatbuffers::Vector<const Test *> *tests_array[] = {
433 monster->test4(),
434 monster->test5(),
435 };
436 for (size_t i = 0; i < sizeof(tests_array) / sizeof(tests_array[0]); ++i) {
437 auto tests = tests_array[i];
438 TEST_NOTNULL(tests);
439 auto test_0 = tests->Get(0);
440 auto test_1 = tests->Get(1);
441 TEST_EQ(test_0->a(), 10);
442 TEST_EQ(test_0->b(), 20);
443 TEST_EQ(test_1->a(), 30);
444 TEST_EQ(test_1->b(), 40);
445 for (auto it = tests->begin(); it != tests->end(); ++it) {
446 TEST_EQ(it->a() == 10 || it->a() == 30, true); // Just testing iterators.
447 }
448 }
449
450 // Checking for presence of fields:
451 TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_HP), true);
452 TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_MANA), false);
453
454 // Obtaining a buffer from a root:
455 TEST_EQ(GetBufferStartFromRootPointer(monster), flatbuf);
456 }
457
458 // Change a FlatBuffer in-place, after it has been constructed.
MutateFlatBuffersTest(uint8_t * flatbuf,std::size_t length)459 void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
460 // Get non-const pointer to root.
461 auto monster = GetMutableMonster(flatbuf);
462
463 // Each of these tests mutates, then tests, then set back to the original,
464 // so we can test that the buffer in the end still passes our original test.
465 auto hp_ok = monster->mutate_hp(10);
466 TEST_EQ(hp_ok, true); // Field was present.
467 TEST_EQ(monster->hp(), 10);
468 // Mutate to default value
469 auto hp_ok_default = monster->mutate_hp(100);
470 TEST_EQ(hp_ok_default, true); // Field was present.
471 TEST_EQ(monster->hp(), 100);
472 // Test that mutate to default above keeps field valid for further mutations
473 auto hp_ok_2 = monster->mutate_hp(20);
474 TEST_EQ(hp_ok_2, true);
475 TEST_EQ(monster->hp(), 20);
476 monster->mutate_hp(80);
477
478 // Monster originally at 150 mana (default value)
479 auto mana_default_ok = monster->mutate_mana(150); // Mutate to default value.
480 TEST_EQ(mana_default_ok,
481 true); // Mutation should succeed, because default value.
482 TEST_EQ(monster->mana(), 150);
483 auto mana_ok = monster->mutate_mana(10);
484 TEST_EQ(mana_ok, false); // Field was NOT present, because default value.
485 TEST_EQ(monster->mana(), 150);
486
487 // Mutate structs.
488 auto pos = monster->mutable_pos();
489 auto test3 = pos->mutable_test3(); // Struct inside a struct.
490 test3.mutate_a(50); // Struct fields never fail.
491 TEST_EQ(test3.a(), 50);
492 test3.mutate_a(10);
493
494 // Mutate vectors.
495 auto inventory = monster->mutable_inventory();
496 inventory->Mutate(9, 100);
497 TEST_EQ(inventory->Get(9), 100);
498 inventory->Mutate(9, 9);
499
500 auto tables = monster->mutable_testarrayoftables();
501 auto first = tables->GetMutableObject(0);
502 TEST_EQ(first->hp(), 1000);
503 first->mutate_hp(0);
504 TEST_EQ(first->hp(), 0);
505 first->mutate_hp(1000);
506
507 // Mutate via LookupByKey
508 TEST_NOTNULL(tables->MutableLookupByKey("Barney"));
509 TEST_EQ(static_cast<Monster *>(nullptr),
510 tables->MutableLookupByKey("DoesntExist"));
511 TEST_EQ(tables->MutableLookupByKey("Barney")->hp(), 1000);
512 TEST_EQ(tables->MutableLookupByKey("Barney")->mutate_hp(0), true);
513 TEST_EQ(tables->LookupByKey("Barney")->hp(), 0);
514 TEST_EQ(tables->MutableLookupByKey("Barney")->mutate_hp(1000), true);
515
516 // Run the verifier and the regular test to make sure we didn't trample on
517 // anything.
518 AccessFlatBufferTest(flatbuf, length);
519 }
520
521 // Utility function to check a Monster object.
CheckMonsterObject(MonsterT * monster2)522 void CheckMonsterObject(MonsterT *monster2) {
523 TEST_EQ(monster2->hp, 80);
524 TEST_EQ(monster2->mana, 150); // default
525 TEST_EQ_STR(monster2->name.c_str(), "MyMonster");
526
527 auto &pos = monster2->pos;
528 TEST_NOTNULL(pos);
529 TEST_EQ(pos->z(), 3);
530 TEST_EQ(pos->test3().a(), 10);
531 TEST_EQ(pos->test3().b(), 20);
532
533 auto &inventory = monster2->inventory;
534 TEST_EQ(inventory.size(), 10UL);
535 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
536 for (auto it = inventory.begin(); it != inventory.end(); ++it)
537 TEST_EQ(*it, inv_data[it - inventory.begin()]);
538
539 TEST_EQ(monster2->color, Color_Blue);
540
541 auto monster3 = monster2->test.AsMonster();
542 TEST_NOTNULL(monster3);
543 TEST_EQ_STR(monster3->name.c_str(), "Fred");
544
545 auto &vecofstrings = monster2->testarrayofstring;
546 TEST_EQ(vecofstrings.size(), 4U);
547 TEST_EQ_STR(vecofstrings[0].c_str(), "bob");
548 TEST_EQ_STR(vecofstrings[1].c_str(), "fred");
549
550 auto &vecofstrings2 = monster2->testarrayofstring2;
551 TEST_EQ(vecofstrings2.size(), 2U);
552 TEST_EQ_STR(vecofstrings2[0].c_str(), "jane");
553 TEST_EQ_STR(vecofstrings2[1].c_str(), "mary");
554
555 auto &vecoftables = monster2->testarrayoftables;
556 TEST_EQ(vecoftables.size(), 3U);
557 TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney");
558 TEST_EQ(vecoftables[0]->hp, 1000);
559 TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred");
560 TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma");
561
562 auto &tests = monster2->test4;
563 TEST_EQ(tests[0].a(), 10);
564 TEST_EQ(tests[0].b(), 20);
565 TEST_EQ(tests[1].a(), 30);
566 TEST_EQ(tests[1].b(), 40);
567 }
568
569 // Unpack a FlatBuffer into objects.
ObjectFlatBuffersTest(uint8_t * flatbuf)570 void ObjectFlatBuffersTest(uint8_t *flatbuf) {
571 // Optional: we can specify resolver and rehasher functions to turn hashed
572 // strings into object pointers and back, to implement remote references
573 // and such.
574 auto resolver = flatbuffers::resolver_function_t(
575 [](void **pointer_adr, flatbuffers::hash_value_t hash) {
576 (void)pointer_adr;
577 (void)hash;
578 // Don't actually do anything, leave variable null.
579 });
580 auto rehasher = flatbuffers::rehasher_function_t(
581 [](void *pointer) -> flatbuffers::hash_value_t {
582 (void)pointer;
583 return 0;
584 });
585
586 // Turn a buffer into C++ objects.
587 auto monster1 = UnPackMonster(flatbuf, &resolver);
588
589 // Re-serialize the data.
590 flatbuffers::FlatBufferBuilder fbb1;
591 fbb1.Finish(CreateMonster(fbb1, monster1.get(), &rehasher),
592 MonsterIdentifier());
593
594 // Unpack again, and re-serialize again.
595 auto monster2 = UnPackMonster(fbb1.GetBufferPointer(), &resolver);
596 flatbuffers::FlatBufferBuilder fbb2;
597 fbb2.Finish(CreateMonster(fbb2, monster2.get(), &rehasher),
598 MonsterIdentifier());
599
600 // Now we've gone full round-trip, the two buffers should match.
601 const auto len1 = fbb1.GetSize();
602 const auto len2 = fbb2.GetSize();
603 TEST_EQ(len1, len2);
604 TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(), len1), 0);
605
606 // Test it with the original buffer test to make sure all data survived.
607 AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false);
608
609 // Test accessing fields, similar to AccessFlatBufferTest above.
610 CheckMonsterObject(monster2.get());
611
612 // Test object copy.
613 auto monster3 = *monster2;
614 flatbuffers::FlatBufferBuilder fbb3;
615 fbb3.Finish(CreateMonster(fbb3, &monster3, &rehasher), MonsterIdentifier());
616 const auto len3 = fbb3.GetSize();
617 TEST_EQ(len2, len3);
618 TEST_EQ(memcmp(fbb2.GetBufferPointer(), fbb3.GetBufferPointer(), len2), 0);
619 // Delete monster1 and monster2, then test accessing fields in monster3.
620 monster1.reset();
621 monster2.reset();
622 CheckMonsterObject(&monster3);
623 }
624
625 // Prefix a FlatBuffer with a size field.
SizePrefixedTest()626 void SizePrefixedTest() {
627 // Create size prefixed buffer.
628 flatbuffers::FlatBufferBuilder fbb;
629 FinishSizePrefixedMonsterBuffer(
630 fbb, CreateMonster(fbb, nullptr, 200, 300, fbb.CreateString("bob")));
631
632 // Verify it.
633 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
634 TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier), true);
635
636 // Access it.
637 auto m = GetSizePrefixedMonster(fbb.GetBufferPointer());
638 TEST_EQ(m->mana(), 200);
639 TEST_EQ(m->hp(), 300);
640 TEST_EQ_STR(m->name()->c_str(), "bob");
641 }
642
TriviallyCopyableTest()643 void TriviallyCopyableTest() {
644 // clang-format off
645 #if __GNUG__ && __GNUC__ < 5 && \
646 !(defined(__clang__) && __clang_major__ >= 16)
647 TEST_EQ(__has_trivial_copy(Vec3), true);
648 #else
649 #if __cplusplus >= 201103L
650 TEST_EQ(std::is_trivially_copyable<Vec3>::value, true);
651 #endif
652 #endif
653 // clang-format on
654 }
655
656 // Check stringify of an default enum value to json
JsonDefaultTest()657 void JsonDefaultTest() {
658 // load FlatBuffer schema (.fbs) from disk
659 std::string schemafile;
660 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
661 false, &schemafile),
662 true);
663 // parse schema first, so we can use it to parse the data after
664 flatbuffers::Parser parser;
665 auto include_test_path =
666 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
667 const char *include_directories[] = { test_data_path.c_str(),
668 include_test_path.c_str(), nullptr };
669
670 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
671 // create incomplete monster and store to json
672 parser.opts.output_default_scalars_in_json = true;
673 parser.opts.output_enum_identifiers = true;
674 flatbuffers::FlatBufferBuilder builder;
675 auto name = builder.CreateString("default_enum");
676 MonsterBuilder color_monster(builder);
677 color_monster.add_name(name);
678 FinishMonsterBuffer(builder, color_monster.Finish());
679 std::string jsongen;
680 auto result = GenerateText(parser, builder.GetBufferPointer(), &jsongen);
681 TEST_EQ(result, true);
682 // default value of the "color" field is Blue
683 TEST_EQ(std::string::npos != jsongen.find("color: \"Blue\""), true);
684 // default value of the "testf" field is 3.14159
685 TEST_EQ(std::string::npos != jsongen.find("testf: 3.14159"), true);
686 }
687
JsonEnumsTest()688 void JsonEnumsTest() {
689 // load FlatBuffer schema (.fbs) from disk
690 std::string schemafile;
691 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
692 false, &schemafile),
693 true);
694 // parse schema first, so we can use it to parse the data after
695 flatbuffers::Parser parser;
696 auto include_test_path =
697 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
698 const char *include_directories[] = { test_data_path.c_str(),
699 include_test_path.c_str(), nullptr };
700 parser.opts.output_enum_identifiers = true;
701 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
702 flatbuffers::FlatBufferBuilder builder;
703 auto name = builder.CreateString("bitflag_enum");
704 MonsterBuilder color_monster(builder);
705 color_monster.add_name(name);
706 color_monster.add_color(Color(Color_Blue | Color_Red));
707 FinishMonsterBuffer(builder, color_monster.Finish());
708 std::string jsongen;
709 auto result = GenerateText(parser, builder.GetBufferPointer(), &jsongen);
710 TEST_EQ(result, true);
711 TEST_EQ(std::string::npos != jsongen.find("color: \"Red Blue\""), true);
712 // Test forward compatibility with 'output_enum_identifiers = true'.
713 // Current Color doesn't have '(1u << 2)' field, let's add it.
714 builder.Clear();
715 std::string future_json;
716 auto future_name = builder.CreateString("future bitflag_enum");
717 MonsterBuilder future_color(builder);
718 future_color.add_name(future_name);
719 future_color.add_color(
720 static_cast<Color>((1u << 2) | Color_Blue | Color_Red));
721 FinishMonsterBuffer(builder, future_color.Finish());
722 result = GenerateText(parser, builder.GetBufferPointer(), &future_json);
723 TEST_EQ(result, true);
724 TEST_EQ(std::string::npos != future_json.find("color: 13"), true);
725 }
726
JsonOptionalTest(bool default_scalars)727 void JsonOptionalTest(bool default_scalars) {
728 // load FlatBuffer schema (.fbs) and JSON from disk
729 std::string schemafile;
730 std::string jsonfile;
731 TEST_EQ(
732 flatbuffers::LoadFile((test_data_path + "optional_scalars.fbs").c_str(),
733 false, &schemafile),
734 true);
735 TEST_EQ(flatbuffers::LoadFile((test_data_path + "optional_scalars" +
736 (default_scalars ? "_defaults" : "") + ".json")
737 .c_str(),
738 false, &jsonfile),
739 true);
740
741 auto include_test_path =
742 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
743 const char *include_directories[] = { test_data_path.c_str(),
744 include_test_path.c_str(), nullptr };
745
746 // parse schema first, so we can use it to parse the data after
747 flatbuffers::Parser parser;
748 parser.opts.output_default_scalars_in_json = default_scalars;
749 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
750 TEST_EQ(parser.ParseJson(jsonfile.c_str()), true);
751
752 // here, parser.builder_ contains a binary buffer that is the parsed data.
753
754 // First, verify it, just in case:
755 flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
756 parser.builder_.GetSize());
757 TEST_EQ(optional_scalars::VerifyScalarStuffBuffer(verifier), true);
758
759 // to ensure it is correct, we now generate text back from the binary,
760 // and compare the two:
761 std::string jsongen;
762 auto result =
763 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
764 TEST_EQ(result, true);
765 TEST_EQ_STR(jsongen.c_str(), jsonfile.c_str());
766 }
767
768 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
769 // The IEEE-754 quiet_NaN is not simple binary constant.
770 // All binary NaN bit strings have all the bits of the biased exponent field E
771 // set to 1. A quiet NaN bit string should be encoded with the first bit d[1]
772 // of the trailing significand field T being 1 (d[0] is implicit bit).
773 // It is assumed that endianness of floating-point is same as integer.
is_quiet_nan_impl(T v)774 template<typename T, typename U, U qnan_base> bool is_quiet_nan_impl(T v) {
775 static_assert(sizeof(T) == sizeof(U), "unexpected");
776 U b = 0;
777 std::memcpy(&b, &v, sizeof(T));
778 return ((b & qnan_base) == qnan_base);
779 }
780 # if defined(__mips__) || defined(__hppa__)
is_quiet_nan(float v)781 bool is_quiet_nan(float v) {
782 return is_quiet_nan_impl<float, uint32_t, 0x7FC00000u>(v) ||
783 is_quiet_nan_impl<float, uint32_t, 0x7FBFFFFFu>(v);
784 }
is_quiet_nan(double v)785 bool is_quiet_nan(double v) {
786 return is_quiet_nan_impl<double, uint64_t, 0x7FF8000000000000ul>(v) ||
787 is_quiet_nan_impl<double, uint64_t, 0x7FF7FFFFFFFFFFFFu>(v);
788 }
789 # else
is_quiet_nan(float v)790 bool is_quiet_nan(float v) {
791 return is_quiet_nan_impl<float, uint32_t, 0x7FC00000u>(v);
792 }
is_quiet_nan(double v)793 bool is_quiet_nan(double v) {
794 return is_quiet_nan_impl<double, uint64_t, 0x7FF8000000000000ul>(v);
795 }
796 # endif
797
TestMonsterExtraFloats()798 void TestMonsterExtraFloats() {
799 TEST_EQ(is_quiet_nan(1.0), false);
800 TEST_EQ(is_quiet_nan(infinity_d), false);
801 TEST_EQ(is_quiet_nan(-infinity_f), false);
802 TEST_EQ(is_quiet_nan(std::numeric_limits<float>::quiet_NaN()), true);
803 TEST_EQ(is_quiet_nan(std::numeric_limits<double>::quiet_NaN()), true);
804
805 using namespace flatbuffers;
806 using namespace MyGame;
807 // Load FlatBuffer schema (.fbs) from disk.
808 std::string schemafile;
809 TEST_EQ(LoadFile((test_data_path + "monster_extra.fbs").c_str(), false,
810 &schemafile),
811 true);
812 // Parse schema first, so we can use it to parse the data after.
813 Parser parser;
814 auto include_test_path = ConCatPathFileName(test_data_path, "include_test");
815 const char *include_directories[] = { test_data_path.c_str(),
816 include_test_path.c_str(), nullptr };
817 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
818 // Create empty extra and store to json.
819 parser.opts.output_default_scalars_in_json = true;
820 parser.opts.output_enum_identifiers = true;
821 FlatBufferBuilder builder;
822 const auto def_root = MonsterExtraBuilder(builder).Finish();
823 FinishMonsterExtraBuffer(builder, def_root);
824 const auto def_obj = builder.GetBufferPointer();
825 const auto def_extra = GetMonsterExtra(def_obj);
826 TEST_NOTNULL(def_extra);
827 TEST_EQ(is_quiet_nan(def_extra->f0()), true);
828 TEST_EQ(is_quiet_nan(def_extra->f1()), true);
829 TEST_EQ(def_extra->f2(), +infinity_f);
830 TEST_EQ(def_extra->f3(), -infinity_f);
831 TEST_EQ(is_quiet_nan(def_extra->d0()), true);
832 TEST_EQ(is_quiet_nan(def_extra->d1()), true);
833 TEST_EQ(def_extra->d2(), +infinity_d);
834 TEST_EQ(def_extra->d3(), -infinity_d);
835 std::string jsongen;
836 auto result = GenerateText(parser, def_obj, &jsongen);
837 TEST_EQ(result, true);
838 // Check expected default values.
839 TEST_EQ(std::string::npos != jsongen.find("f0: nan"), true);
840 TEST_EQ(std::string::npos != jsongen.find("f1: nan"), true);
841 TEST_EQ(std::string::npos != jsongen.find("f2: inf"), true);
842 TEST_EQ(std::string::npos != jsongen.find("f3: -inf"), true);
843 TEST_EQ(std::string::npos != jsongen.find("d0: nan"), true);
844 TEST_EQ(std::string::npos != jsongen.find("d1: nan"), true);
845 TEST_EQ(std::string::npos != jsongen.find("d2: inf"), true);
846 TEST_EQ(std::string::npos != jsongen.find("d3: -inf"), true);
847 // Parse 'mosterdata_extra.json'.
848 const auto extra_base = test_data_path + "monsterdata_extra";
849 jsongen = "";
850 TEST_EQ(LoadFile((extra_base + ".json").c_str(), false, &jsongen), true);
851 TEST_EQ(parser.Parse(jsongen.c_str()), true);
852 const auto test_file = parser.builder_.GetBufferPointer();
853 const auto test_size = parser.builder_.GetSize();
854 Verifier verifier(test_file, test_size);
855 TEST_ASSERT(VerifyMonsterExtraBuffer(verifier));
856 const auto extra = GetMonsterExtra(test_file);
857 TEST_NOTNULL(extra);
858 TEST_EQ(is_quiet_nan(extra->f0()), true);
859 TEST_EQ(is_quiet_nan(extra->f1()), true);
860 TEST_EQ(extra->f2(), +infinity_f);
861 TEST_EQ(extra->f3(), -infinity_f);
862 TEST_EQ(is_quiet_nan(extra->d0()), true);
863 TEST_EQ(extra->d1(), +infinity_d);
864 TEST_EQ(extra->d2(), -infinity_d);
865 TEST_EQ(is_quiet_nan(extra->d3()), true);
866 TEST_NOTNULL(extra->fvec());
867 TEST_EQ(extra->fvec()->size(), 4);
868 TEST_EQ(extra->fvec()->Get(0), 1.0f);
869 TEST_EQ(extra->fvec()->Get(1), -infinity_f);
870 TEST_EQ(extra->fvec()->Get(2), +infinity_f);
871 TEST_EQ(is_quiet_nan(extra->fvec()->Get(3)), true);
872 TEST_NOTNULL(extra->dvec());
873 TEST_EQ(extra->dvec()->size(), 4);
874 TEST_EQ(extra->dvec()->Get(0), 2.0);
875 TEST_EQ(extra->dvec()->Get(1), +infinity_d);
876 TEST_EQ(extra->dvec()->Get(2), -infinity_d);
877 TEST_EQ(is_quiet_nan(extra->dvec()->Get(3)), true);
878 }
879 #else
TestMonsterExtraFloats()880 void TestMonsterExtraFloats() {}
881 #endif
882
883 // example of parsing text straight into a buffer, and generating
884 // text back from it:
ParseAndGenerateTextTest(bool binary)885 void ParseAndGenerateTextTest(bool binary) {
886 // load FlatBuffer schema (.fbs) and JSON from disk
887 std::string schemafile;
888 std::string jsonfile;
889 TEST_EQ(flatbuffers::LoadFile(
890 (test_data_path + "monster_test." + (binary ? "bfbs" : "fbs"))
891 .c_str(),
892 binary, &schemafile),
893 true);
894 TEST_EQ(flatbuffers::LoadFile(
895 (test_data_path + "monsterdata_test.golden").c_str(), false,
896 &jsonfile),
897 true);
898
899 auto include_test_path =
900 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
901 const char *include_directories[] = { test_data_path.c_str(),
902 include_test_path.c_str(), nullptr };
903
904 // parse schema first, so we can use it to parse the data after
905 flatbuffers::Parser parser;
906 if (binary) {
907 flatbuffers::Verifier verifier(
908 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
909 schemafile.size());
910 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
911 // auto schema = reflection::GetSchema(schemafile.c_str());
912 TEST_EQ(parser.Deserialize(
913 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
914 schemafile.size()),
915 true);
916 } else {
917 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
918 }
919 TEST_EQ(parser.ParseJson(jsonfile.c_str()), true);
920
921 // here, parser.builder_ contains a binary buffer that is the parsed data.
922
923 // First, verify it, just in case:
924 flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
925 parser.builder_.GetSize());
926 TEST_EQ(VerifyMonsterBuffer(verifier), true);
927
928 AccessFlatBufferTest(parser.builder_.GetBufferPointer(),
929 parser.builder_.GetSize(), false);
930
931 // to ensure it is correct, we now generate text back from the binary,
932 // and compare the two:
933 std::string jsongen;
934 auto result =
935 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
936 TEST_EQ(result, true);
937 TEST_EQ_STR(jsongen.c_str(), jsonfile.c_str());
938
939 // We can also do the above using the convenient Registry that knows about
940 // a set of file_identifiers mapped to schemas.
941 flatbuffers::Registry registry;
942 // Make sure schemas can find their includes.
943 registry.AddIncludeDirectory(test_data_path.c_str());
944 registry.AddIncludeDirectory(include_test_path.c_str());
945 // Call this with many schemas if possible.
946 registry.Register(MonsterIdentifier(),
947 (test_data_path + "monster_test.fbs").c_str());
948 // Now we got this set up, we can parse by just specifying the identifier,
949 // the correct schema will be loaded on the fly:
950 auto buf = registry.TextToFlatBuffer(jsonfile.c_str(), MonsterIdentifier());
951 // If this fails, check registry.lasterror_.
952 TEST_NOTNULL(buf.data());
953 // Test the buffer, to be sure:
954 AccessFlatBufferTest(buf.data(), buf.size(), false);
955 // We can use the registry to turn this back into text, in this case it
956 // will get the file_identifier from the binary:
957 std::string text;
958 auto ok = registry.FlatBufferToText(buf.data(), buf.size(), &text);
959 // If this fails, check registry.lasterror_.
960 TEST_EQ(ok, true);
961 TEST_EQ_STR(text.c_str(), jsonfile.c_str());
962
963 // Generate text for UTF-8 strings without escapes.
964 std::string jsonfile_utf8;
965 TEST_EQ(flatbuffers::LoadFile((test_data_path + "unicode_test.json").c_str(),
966 false, &jsonfile_utf8),
967 true);
968 TEST_EQ(parser.Parse(jsonfile_utf8.c_str(), include_directories), true);
969 // To ensure it is correct, generate utf-8 text back from the binary.
970 std::string jsongen_utf8;
971 // request natural printing for utf-8 strings
972 parser.opts.natural_utf8 = true;
973 parser.opts.strict_json = true;
974 TEST_EQ(
975 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen_utf8),
976 true);
977 TEST_EQ_STR(jsongen_utf8.c_str(), jsonfile_utf8.c_str());
978 }
979
ReflectionTest(uint8_t * flatbuf,size_t length)980 void ReflectionTest(uint8_t *flatbuf, size_t length) {
981 // Load a binary schema.
982 std::string bfbsfile;
983 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.bfbs").c_str(),
984 true, &bfbsfile),
985 true);
986
987 // Verify it, just in case:
988 flatbuffers::Verifier verifier(
989 reinterpret_cast<const uint8_t *>(bfbsfile.c_str()), bfbsfile.length());
990 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
991
992 // Make sure the schema is what we expect it to be.
993 auto &schema = *reflection::GetSchema(bfbsfile.c_str());
994 auto root_table = schema.root_table();
995
996 // Check the declaration files.
997 TEST_EQ_STR(root_table->name()->c_str(), "MyGame.Example.Monster");
998 TEST_EQ_STR(root_table->declaration_file()->c_str(), "//monster_test.fbs");
999 TEST_EQ_STR(
1000 schema.objects()->LookupByKey("TableA")->declaration_file()->c_str(),
1001 "//include_test/include_test1.fbs");
1002 TEST_EQ_STR(schema.objects()
1003 ->LookupByKey("MyGame.OtherNameSpace.Unused")
1004 ->declaration_file()
1005 ->c_str(),
1006 "//include_test/sub/include_test2.fbs");
1007 TEST_EQ_STR(schema.enums()
1008 ->LookupByKey("MyGame.OtherNameSpace.FromInclude")
1009 ->declaration_file()
1010 ->c_str(),
1011 "//include_test/sub/include_test2.fbs");
1012
1013 // Check scheam filenames and their includes.
1014 TEST_EQ(schema.fbs_files()->size(), 3);
1015
1016 const auto fbs0 = schema.fbs_files()->Get(0);
1017 TEST_EQ_STR(fbs0->filename()->c_str(), "//include_test/include_test1.fbs");
1018 const auto fbs0_includes = fbs0->included_filenames();
1019 TEST_EQ(fbs0_includes->size(), 2);
1020
1021 // TODO(caspern): Should we force or disallow inclusion of self?
1022 TEST_EQ_STR(fbs0_includes->Get(0)->c_str(),
1023 "//include_test/include_test1.fbs");
1024 TEST_EQ_STR(fbs0_includes->Get(1)->c_str(),
1025 "//include_test/sub/include_test2.fbs");
1026
1027 const auto fbs1 = schema.fbs_files()->Get(1);
1028 TEST_EQ_STR(fbs1->filename()->c_str(),
1029 "//include_test/sub/include_test2.fbs");
1030 const auto fbs1_includes = fbs1->included_filenames();
1031 TEST_EQ(fbs1_includes->size(), 2);
1032 TEST_EQ_STR(fbs1_includes->Get(0)->c_str(),
1033 "//include_test/include_test1.fbs");
1034 TEST_EQ_STR(fbs1_includes->Get(1)->c_str(),
1035 "//include_test/sub/include_test2.fbs");
1036
1037 const auto fbs2 = schema.fbs_files()->Get(2);
1038 TEST_EQ_STR(fbs2->filename()->c_str(), "//monster_test.fbs");
1039 const auto fbs2_includes = fbs2->included_filenames();
1040 TEST_EQ(fbs2_includes->size(), 1);
1041 TEST_EQ_STR(fbs2_includes->Get(0)->c_str(),
1042 "//include_test/include_test1.fbs");
1043
1044 // Check Root table fields
1045 auto fields = root_table->fields();
1046 auto hp_field_ptr = fields->LookupByKey("hp");
1047 TEST_NOTNULL(hp_field_ptr);
1048 auto &hp_field = *hp_field_ptr;
1049 TEST_EQ_STR(hp_field.name()->c_str(), "hp");
1050 TEST_EQ(hp_field.id(), 2);
1051 TEST_EQ(hp_field.type()->base_type(), reflection::Short);
1052
1053 auto friendly_field_ptr = fields->LookupByKey("friendly");
1054 TEST_NOTNULL(friendly_field_ptr);
1055 TEST_NOTNULL(friendly_field_ptr->attributes());
1056 TEST_NOTNULL(friendly_field_ptr->attributes()->LookupByKey("priority"));
1057
1058 // Make sure the table index is what we expect it to be.
1059 auto pos_field_ptr = fields->LookupByKey("pos");
1060 TEST_NOTNULL(pos_field_ptr);
1061 TEST_EQ(pos_field_ptr->type()->base_type(), reflection::Obj);
1062 auto pos_table_ptr = schema.objects()->Get(pos_field_ptr->type()->index());
1063 TEST_NOTNULL(pos_table_ptr);
1064 TEST_EQ_STR(pos_table_ptr->name()->c_str(), "MyGame.Example.Vec3");
1065
1066 // Test nullability of fields: hp is a 0-default scalar, pos is a struct =>
1067 // optional, and name is a required string => not optional.
1068 TEST_EQ(hp_field.optional(), false);
1069 TEST_EQ(pos_field_ptr->optional(), true);
1070 TEST_EQ(fields->LookupByKey("name")->optional(), false);
1071
1072 // Now use it to dynamically access a buffer.
1073 auto &root = *flatbuffers::GetAnyRoot(flatbuf);
1074
1075 // Verify the buffer first using reflection based verification
1076 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), flatbuf, length),
1077 true);
1078
1079 auto hp = flatbuffers::GetFieldI<uint16_t>(root, hp_field);
1080 TEST_EQ(hp, 80);
1081
1082 // Rather than needing to know the type, we can also get the value of
1083 // any field as an int64_t/double/string, regardless of what it actually is.
1084 auto hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
1085 TEST_EQ(hp_int64, 80);
1086 auto hp_double = flatbuffers::GetAnyFieldF(root, hp_field);
1087 TEST_EQ(hp_double, 80.0);
1088 auto hp_string = flatbuffers::GetAnyFieldS(root, hp_field, &schema);
1089 TEST_EQ_STR(hp_string.c_str(), "80");
1090
1091 // Get struct field through reflection
1092 auto pos_struct = flatbuffers::GetFieldStruct(root, *pos_field_ptr);
1093 TEST_NOTNULL(pos_struct);
1094 TEST_EQ(flatbuffers::GetAnyFieldF(*pos_struct,
1095 *pos_table_ptr->fields()->LookupByKey("z")),
1096 3.0f);
1097
1098 auto test3_field = pos_table_ptr->fields()->LookupByKey("test3");
1099 auto test3_struct = flatbuffers::GetFieldStruct(*pos_struct, *test3_field);
1100 TEST_NOTNULL(test3_struct);
1101 auto test3_object = schema.objects()->Get(test3_field->type()->index());
1102
1103 TEST_EQ(flatbuffers::GetAnyFieldF(*test3_struct,
1104 *test3_object->fields()->LookupByKey("a")),
1105 10);
1106
1107 // We can also modify it.
1108 flatbuffers::SetField<uint16_t>(&root, hp_field, 200);
1109 hp = flatbuffers::GetFieldI<uint16_t>(root, hp_field);
1110 TEST_EQ(hp, 200);
1111
1112 // We can also set fields generically:
1113 flatbuffers::SetAnyFieldI(&root, hp_field, 300);
1114 hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
1115 TEST_EQ(hp_int64, 300);
1116 flatbuffers::SetAnyFieldF(&root, hp_field, 300.5);
1117 hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
1118 TEST_EQ(hp_int64, 300);
1119 flatbuffers::SetAnyFieldS(&root, hp_field, "300");
1120 hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
1121 TEST_EQ(hp_int64, 300);
1122
1123 // Test buffer is valid after the modifications
1124 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), flatbuf, length),
1125 true);
1126
1127 // Reset it, for further tests.
1128 flatbuffers::SetField<uint16_t>(&root, hp_field, 80);
1129
1130 // More advanced functionality: changing the size of items in-line!
1131 // First we put the FlatBuffer inside an std::vector.
1132 std::vector<uint8_t> resizingbuf(flatbuf, flatbuf + length);
1133 // Find the field we want to modify.
1134 auto &name_field = *fields->LookupByKey("name");
1135 // Get the root.
1136 // This time we wrap the result from GetAnyRoot in a smartpointer that
1137 // will keep rroot valid as resizingbuf resizes.
1138 auto rroot = flatbuffers::piv(flatbuffers::GetAnyRoot(resizingbuf.data()),
1139 resizingbuf);
1140 SetString(schema, "totally new string", GetFieldS(**rroot, name_field),
1141 &resizingbuf);
1142 // Here resizingbuf has changed, but rroot is still valid.
1143 TEST_EQ_STR(GetFieldS(**rroot, name_field)->c_str(), "totally new string");
1144 // Now lets extend a vector by 100 elements (10 -> 110).
1145 auto &inventory_field = *fields->LookupByKey("inventory");
1146 auto rinventory = flatbuffers::piv(
1147 flatbuffers::GetFieldV<uint8_t>(**rroot, inventory_field), resizingbuf);
1148 flatbuffers::ResizeVector<uint8_t>(schema, 110, 50, *rinventory,
1149 &resizingbuf);
1150 // rinventory still valid, so lets read from it.
1151 TEST_EQ(rinventory->Get(10), 50);
1152
1153 // For reflection uses not covered already, there is a more powerful way:
1154 // we can simply generate whatever object we want to add/modify in a
1155 // FlatBuffer of its own, then add that to an existing FlatBuffer:
1156 // As an example, let's add a string to an array of strings.
1157 // First, find our field:
1158 auto &testarrayofstring_field = *fields->LookupByKey("testarrayofstring");
1159 // Find the vector value:
1160 auto rtestarrayofstring = flatbuffers::piv(
1161 flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>(
1162 **rroot, testarrayofstring_field),
1163 resizingbuf);
1164 // It's a vector of 2 strings, to which we add one more, initialized to
1165 // offset 0.
1166 flatbuffers::ResizeVector<flatbuffers::Offset<flatbuffers::String>>(
1167 schema, 3, 0, *rtestarrayofstring, &resizingbuf);
1168 // Here we just create a buffer that contans a single string, but this
1169 // could also be any complex set of tables and other values.
1170 flatbuffers::FlatBufferBuilder stringfbb;
1171 stringfbb.Finish(stringfbb.CreateString("hank"));
1172 // Add the contents of it to our existing FlatBuffer.
1173 // We do this last, so the pointer doesn't get invalidated (since it is
1174 // at the end of the buffer):
1175 auto string_ptr = flatbuffers::AddFlatBuffer(
1176 resizingbuf, stringfbb.GetBufferPointer(), stringfbb.GetSize());
1177 // Finally, set the new value in the vector.
1178 rtestarrayofstring->MutateOffset(2, string_ptr);
1179 TEST_EQ_STR(rtestarrayofstring->Get(0)->c_str(), "bob");
1180 TEST_EQ_STR(rtestarrayofstring->Get(2)->c_str(), "hank");
1181 // Test integrity of all resize operations above.
1182 flatbuffers::Verifier resize_verifier(
1183 reinterpret_cast<const uint8_t *>(resizingbuf.data()),
1184 resizingbuf.size());
1185 TEST_EQ(VerifyMonsterBuffer(resize_verifier), true);
1186
1187 // Test buffer is valid using reflection as well
1188 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), resizingbuf.data(),
1189 resizingbuf.size()),
1190 true);
1191
1192 // As an additional test, also set it on the name field.
1193 // Note: unlike the name change above, this just overwrites the offset,
1194 // rather than changing the string in-place.
1195 SetFieldT(*rroot, name_field, string_ptr);
1196 TEST_EQ_STR(GetFieldS(**rroot, name_field)->c_str(), "hank");
1197
1198 // Using reflection, rather than mutating binary FlatBuffers, we can also copy
1199 // tables and other things out of other FlatBuffers into a FlatBufferBuilder,
1200 // either part or whole.
1201 flatbuffers::FlatBufferBuilder fbb;
1202 auto root_offset = flatbuffers::CopyTable(
1203 fbb, schema, *root_table, *flatbuffers::GetAnyRoot(flatbuf), true);
1204 fbb.Finish(root_offset, MonsterIdentifier());
1205 // Test that it was copied correctly:
1206 AccessFlatBufferTest(fbb.GetBufferPointer(), fbb.GetSize());
1207
1208 // Test buffer is valid using reflection as well
1209 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(),
1210 fbb.GetBufferPointer(), fbb.GetSize()),
1211 true);
1212 }
1213
MiniReflectFlatBuffersTest(uint8_t * flatbuf)1214 void MiniReflectFlatBuffersTest(uint8_t *flatbuf) {
1215 auto s =
1216 flatbuffers::FlatBufferToString(flatbuf, Monster::MiniReflectTypeTable());
1217 TEST_EQ_STR(
1218 s.c_str(),
1219 "{ "
1220 "pos: { x: 1.0, y: 2.0, z: 3.0, test1: 0.0, test2: Red, test3: "
1221 "{ a: 10, b: 20 } }, "
1222 "hp: 80, "
1223 "name: \"MyMonster\", "
1224 "inventory: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "
1225 "test_type: Monster, "
1226 "test: { name: \"Fred\" }, "
1227 "test4: [ { a: 10, b: 20 }, { a: 30, b: 40 } ], "
1228 "testarrayofstring: [ \"bob\", \"fred\", \"bob\", \"fred\" ], "
1229 "testarrayoftables: [ { hp: 1000, name: \"Barney\" }, { name: \"Fred\" "
1230 "}, "
1231 "{ name: \"Wilma\" } ], "
1232 // TODO(wvo): should really print this nested buffer correctly.
1233 "testnestedflatbuffer: [ 20, 0, 0, 0, 77, 79, 78, 83, 12, 0, 12, 0, 0, "
1234 "0, "
1235 "4, 0, 6, 0, 8, 0, 12, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 13, 0, 0, 0, 78, "
1236 "101, 115, 116, 101, 100, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0 ], "
1237 "testarrayofstring2: [ \"jane\", \"mary\" ], "
1238 "testarrayofsortedstruct: [ { id: 0, distance: 0 }, "
1239 "{ id: 2, distance: 20 }, { id: 3, distance: 30 }, "
1240 "{ id: 4, distance: 40 } ], "
1241 "flex: [ 210, 4, 5, 2 ], "
1242 "test5: [ { a: 10, b: 20 }, { a: 30, b: 40 } ], "
1243 "vector_of_enums: [ Blue, Green ], "
1244 "scalar_key_sorted_tables: [ { id: \"miss\" } ] "
1245 "}");
1246
1247 Test test(16, 32);
1248 Vec3 vec(1, 2, 3, 1.5, Color_Red, test);
1249 flatbuffers::FlatBufferBuilder vec_builder;
1250 vec_builder.Finish(vec_builder.CreateStruct(vec));
1251 auto vec_buffer = vec_builder.Release();
1252 auto vec_str = flatbuffers::FlatBufferToString(vec_buffer.data(),
1253 Vec3::MiniReflectTypeTable());
1254 TEST_EQ_STR(vec_str.c_str(),
1255 "{ x: 1.0, y: 2.0, z: 3.0, test1: 1.5, test2: Red, test3: { a: "
1256 "16, b: 32 } }");
1257 }
1258
MiniReflectFixedLengthArrayTest()1259 void MiniReflectFixedLengthArrayTest() {
1260 // VS10 does not support typed enums, exclude from tests
1261 #if !defined(_MSC_VER) || _MSC_VER >= 1700
1262 flatbuffers::FlatBufferBuilder fbb;
1263 MyGame::Example::ArrayStruct aStruct(2, 12, 1);
1264 auto aTable = MyGame::Example::CreateArrayTable(fbb, &aStruct);
1265 fbb.Finish(aTable);
1266
1267 auto flatbuf = fbb.Release();
1268 auto s = flatbuffers::FlatBufferToString(
1269 flatbuf.data(), MyGame::Example::ArrayTableTypeTable());
1270 TEST_EQ_STR(
1271 "{ "
1272 "a: { a: 2.0, "
1273 "b: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], "
1274 "c: 12, "
1275 "d: [ { a: [ 0, 0 ], b: A, c: [ A, A ], d: [ 0, 0 ] }, "
1276 "{ a: [ 0, 0 ], b: A, c: [ A, A ], d: [ 0, 0 ] } ], "
1277 "e: 1, f: [ 0, 0 ] } "
1278 "}",
1279 s.c_str());
1280 #endif
1281 }
1282
1283 // Parse a .proto schema, output as .fbs
ParseProtoTest()1284 void ParseProtoTest() {
1285 // load the .proto and the golden file from disk
1286 std::string protofile;
1287 std::string goldenfile;
1288 std::string goldenunionfile;
1289 TEST_EQ(
1290 flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(),
1291 false, &protofile),
1292 true);
1293 TEST_EQ(
1294 flatbuffers::LoadFile((test_data_path + "prototest/test.golden").c_str(),
1295 false, &goldenfile),
1296 true);
1297 TEST_EQ(flatbuffers::LoadFile(
1298 (test_data_path + "prototest/test_union.golden").c_str(), false,
1299 &goldenunionfile),
1300 true);
1301
1302 flatbuffers::IDLOptions opts;
1303 opts.include_dependence_headers = false;
1304 opts.proto_mode = true;
1305
1306 // Parse proto.
1307 flatbuffers::Parser parser(opts);
1308 auto protopath = test_data_path + "prototest/";
1309 const char *include_directories[] = { protopath.c_str(), nullptr };
1310 TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true);
1311
1312 // Generate fbs.
1313 auto fbs = flatbuffers::GenerateFBS(parser, "test");
1314
1315 // Ensure generated file is parsable.
1316 flatbuffers::Parser parser2;
1317 TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
1318 TEST_EQ_STR(fbs.c_str(), goldenfile.c_str());
1319
1320 // Parse proto with --oneof-union option.
1321 opts.proto_oneof_union = true;
1322 flatbuffers::Parser parser3(opts);
1323 TEST_EQ(parser3.Parse(protofile.c_str(), include_directories), true);
1324
1325 // Generate fbs.
1326 auto fbs_union = flatbuffers::GenerateFBS(parser3, "test");
1327
1328 // Ensure generated file is parsable.
1329 flatbuffers::Parser parser4;
1330 TEST_EQ(parser4.Parse(fbs_union.c_str(), nullptr), true);
1331 TEST_EQ_STR(fbs_union.c_str(), goldenunionfile.c_str());
1332 }
1333
1334 // Parse a .proto schema, output as .fbs
ParseProtoTestWithSuffix()1335 void ParseProtoTestWithSuffix() {
1336 // load the .proto and the golden file from disk
1337 std::string protofile;
1338 std::string goldenfile;
1339 std::string goldenunionfile;
1340 TEST_EQ(
1341 flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(),
1342 false, &protofile),
1343 true);
1344 TEST_EQ(flatbuffers::LoadFile(
1345 (test_data_path + "prototest/test_suffix.golden").c_str(), false,
1346 &goldenfile),
1347 true);
1348 TEST_EQ(flatbuffers::LoadFile(
1349 (test_data_path + "prototest/test_union_suffix.golden").c_str(),
1350 false, &goldenunionfile),
1351 true);
1352
1353 flatbuffers::IDLOptions opts;
1354 opts.include_dependence_headers = false;
1355 opts.proto_mode = true;
1356 opts.proto_namespace_suffix = "test_namespace_suffix";
1357
1358 // Parse proto.
1359 flatbuffers::Parser parser(opts);
1360 auto protopath = test_data_path + "prototest/";
1361 const char *include_directories[] = { protopath.c_str(), nullptr };
1362 TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true);
1363
1364 // Generate fbs.
1365 auto fbs = flatbuffers::GenerateFBS(parser, "test");
1366
1367 // Ensure generated file is parsable.
1368 flatbuffers::Parser parser2;
1369 TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
1370 TEST_EQ_STR(fbs.c_str(), goldenfile.c_str());
1371
1372 // Parse proto with --oneof-union option.
1373 opts.proto_oneof_union = true;
1374 flatbuffers::Parser parser3(opts);
1375 TEST_EQ(parser3.Parse(protofile.c_str(), include_directories), true);
1376
1377 // Generate fbs.
1378 auto fbs_union = flatbuffers::GenerateFBS(parser3, "test");
1379
1380 // Ensure generated file is parsable.
1381 flatbuffers::Parser parser4;
1382 TEST_EQ(parser4.Parse(fbs_union.c_str(), nullptr), true);
1383 TEST_EQ_STR(fbs_union.c_str(), goldenunionfile.c_str());
1384 }
1385
1386 // Parse a .proto schema, output as .fbs
ParseProtoTestWithIncludes()1387 void ParseProtoTestWithIncludes() {
1388 // load the .proto and the golden file from disk
1389 std::string protofile;
1390 std::string goldenfile;
1391 std::string goldenunionfile;
1392 std::string importprotofile;
1393 TEST_EQ(
1394 flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(),
1395 false, &protofile),
1396 true);
1397 TEST_EQ(flatbuffers::LoadFile(
1398 (test_data_path + "prototest/imported.proto").c_str(), false,
1399 &importprotofile),
1400 true);
1401 TEST_EQ(flatbuffers::LoadFile(
1402 (test_data_path + "prototest/test_include.golden").c_str(), false,
1403 &goldenfile),
1404 true);
1405 TEST_EQ(flatbuffers::LoadFile(
1406 (test_data_path + "prototest/test_union_include.golden").c_str(),
1407 false, &goldenunionfile),
1408 true);
1409
1410 flatbuffers::IDLOptions opts;
1411 opts.include_dependence_headers = true;
1412 opts.proto_mode = true;
1413
1414 // Parse proto.
1415 flatbuffers::Parser parser(opts);
1416 auto protopath = test_data_path + "prototest/";
1417 const char *include_directories[] = { protopath.c_str(), nullptr };
1418 TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true);
1419
1420 // Generate fbs.
1421 auto fbs = flatbuffers::GenerateFBS(parser, "test");
1422
1423 // Generate fbs from import.proto
1424 flatbuffers::Parser import_parser(opts);
1425 TEST_EQ(import_parser.Parse(importprotofile.c_str(), include_directories),
1426 true);
1427 auto import_fbs = flatbuffers::GenerateFBS(import_parser, "test");
1428
1429 // Ensure generated file is parsable.
1430 flatbuffers::Parser parser2;
1431 // Since `imported.fbs` isn't in the filesystem AbsolutePath can't figure it
1432 // out by itself. We manually construct it so Parser works.
1433 std::string imported_fbs = flatbuffers::PosixPath(
1434 flatbuffers::AbsolutePath(protopath) + "/imported.fbs");
1435 TEST_EQ(parser2.Parse(import_fbs.c_str(), include_directories,
1436 imported_fbs.c_str()),
1437 true);
1438 TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
1439 TEST_EQ_STR(fbs.c_str(), goldenfile.c_str());
1440
1441 // Parse proto with --oneof-union option.
1442 opts.proto_oneof_union = true;
1443 flatbuffers::Parser parser3(opts);
1444 TEST_EQ(parser3.Parse(protofile.c_str(), include_directories), true);
1445
1446 // Generate fbs.
1447 auto fbs_union = flatbuffers::GenerateFBS(parser3, "test");
1448
1449 // Ensure generated file is parsable.
1450 flatbuffers::Parser parser4;
1451 TEST_EQ(parser4.Parse(import_fbs.c_str(), nullptr, imported_fbs.c_str()),
1452 true);
1453 TEST_EQ(parser4.Parse(fbs_union.c_str(), nullptr), true);
1454 TEST_EQ_STR(fbs_union.c_str(), goldenunionfile.c_str());
1455 }
1456
1457 template<typename T>
CompareTableFieldValue(flatbuffers::Table * table,flatbuffers::voffset_t voffset,T val)1458 void CompareTableFieldValue(flatbuffers::Table *table,
1459 flatbuffers::voffset_t voffset, T val) {
1460 T read = table->GetField(voffset, static_cast<T>(0));
1461 TEST_EQ(read, val);
1462 }
1463
UtilConvertCase()1464 void UtilConvertCase() {
1465 {
1466 std::vector<std::tuple<std::string, flatbuffers::Case, std::string>>
1467 cases = {
1468 // Tests for the common cases
1469 { "the_quick_brown_fox", flatbuffers::Case::kUpperCamel,
1470 "TheQuickBrownFox" },
1471 { "the_quick_brown_fox", flatbuffers::Case::kLowerCamel,
1472 "theQuickBrownFox" },
1473 { "the_quick_brown_fox", flatbuffers::Case::kSnake,
1474 "the_quick_brown_fox" },
1475 { "the_quick_brown_fox", flatbuffers::Case::kScreamingSnake,
1476 "THE_QUICK_BROWN_FOX" },
1477 { "the_quick_brown_fox", flatbuffers::Case::kAllLower,
1478 "the_quick_brown_fox" },
1479 { "the_quick_brown_fox", flatbuffers::Case::kAllUpper,
1480 "THE_QUICK_BROWN_FOX" },
1481 { "the_quick_brown_fox", flatbuffers::Case::kUnknown,
1482 "the_quick_brown_fox" },
1483 { "the_quick_brown_fox", flatbuffers::Case::kKeep,
1484 "the_quick_brown_fox" },
1485 { "the_quick_brown_fox", flatbuffers::Case::kSnake2,
1486 "the_quick_brown_fox" },
1487
1488 // Tests for some snake_cases where the _ is oddly placed or missing.
1489 { "single", flatbuffers::Case::kUpperCamel, "Single" },
1490 { "Single", flatbuffers::Case::kUpperCamel, "Single" },
1491 { "_leading", flatbuffers::Case::kUpperCamel, "_leading" },
1492 { "trailing_", flatbuffers::Case::kUpperCamel, "Trailing_" },
1493 { "double__underscore", flatbuffers::Case::kUpperCamel,
1494 "Double_underscore" },
1495 { "single", flatbuffers::Case::kLowerCamel, "single" },
1496 { "Single", flatbuffers::Case::kLowerCamel, "Single" },
1497 { "_leading", flatbuffers::Case::kLowerCamel, "Leading" },
1498 { "trailing_", flatbuffers::Case::kLowerCamel, "trailing_" },
1499 { "double__underscore", flatbuffers::Case::kLowerCamel,
1500 "double_underscore" },
1501
1502 // Tests for some output snake_cases
1503 { "single", flatbuffers::Case::kSnake, "single" },
1504 { "single", flatbuffers::Case::kScreamingSnake, "SINGLE" },
1505 { "_leading", flatbuffers::Case::kScreamingSnake, "_LEADING" },
1506 { "trailing_", flatbuffers::Case::kScreamingSnake, "TRAILING_" },
1507 { "double__underscore", flatbuffers::Case::kScreamingSnake,
1508 "DOUBLE__UNDERSCORE" },
1509 };
1510
1511 for (auto &test_case : cases) {
1512 TEST_EQ(std::get<2>(test_case),
1513 flatbuffers::ConvertCase(std::get<0>(test_case),
1514 std::get<1>(test_case)));
1515 }
1516 }
1517
1518 // Tests for the non snake_case inputs.
1519 {
1520 std::vector<std::tuple<flatbuffers::Case, std::string, flatbuffers::Case,
1521 std::string>>
1522 cases = {
1523 { flatbuffers::Case::kUpperCamel, "TheQuickBrownFox",
1524 flatbuffers::Case::kSnake, "the_quick_brown_fox" },
1525 { flatbuffers::Case::kLowerCamel, "theQuickBrownFox",
1526 flatbuffers::Case::kSnake, "the_quick_brown_fox" },
1527 { flatbuffers::Case::kSnake, "the_quick_brown_fox",
1528 flatbuffers::Case::kSnake, "the_quick_brown_fox" },
1529 { flatbuffers::Case::kScreamingSnake, "THE_QUICK_BROWN_FOX",
1530 flatbuffers::Case::kSnake, "THE_QUICK_BROWN_FOX" },
1531 { flatbuffers::Case::kAllUpper, "SINGLE", flatbuffers::Case::kSnake,
1532 "SINGLE" },
1533 { flatbuffers::Case::kAllLower, "single", flatbuffers::Case::kSnake,
1534 "single" },
1535 { flatbuffers::Case::kUpperCamel, "ABCtest",
1536 flatbuffers::Case::kSnake, "abctest" },
1537 { flatbuffers::Case::kUpperCamel, "tHe_qUiCk_BrOwN_fOx",
1538 flatbuffers::Case::kKeep, "tHe_qUiCk_BrOwN_fOx" },
1539 { flatbuffers::Case::kLowerCamel, "theQuick12345Fox",
1540 flatbuffers::Case::kSnake, "the_quick_12345fox" },
1541 { flatbuffers::Case::kLowerCamel, "a12b34c45",
1542 flatbuffers::Case::kSnake, "a_12b_34c_45" },
1543 { flatbuffers::Case::kLowerCamel, "a12b34c45",
1544 flatbuffers::Case::kSnake2, "a12_b34_c45" },
1545 };
1546
1547 for (auto &test_case : cases) {
1548 TEST_EQ(std::get<3>(test_case),
1549 flatbuffers::ConvertCase(std::get<1>(test_case),
1550 std::get<2>(test_case),
1551 std::get<0>(test_case)));
1552 }
1553 }
1554 }
1555
1556 // Low level stress/fuzz test: serialize/deserialize a variety of
1557 // different kinds of data in different combinations
FuzzTest1()1558 void FuzzTest1() {
1559 // Values we're testing against: chosen to ensure no bits get chopped
1560 // off anywhere, and also be different from eachother.
1561 const uint8_t bool_val = true;
1562 const int8_t char_val = -127; // 0x81
1563 const uint8_t uchar_val = 0xFF;
1564 const int16_t short_val = -32222; // 0x8222;
1565 const uint16_t ushort_val = 0xFEEE;
1566 const int32_t int_val = 0x83333333;
1567 const uint32_t uint_val = 0xFDDDDDDD;
1568 const int64_t long_val = 0x8444444444444444LL;
1569 const uint64_t ulong_val = 0xFCCCCCCCCCCCCCCCULL;
1570 const float float_val = 3.14159f;
1571 const double double_val = 3.14159265359;
1572
1573 const int test_values_max = 11;
1574 const flatbuffers::voffset_t fields_per_object = 4;
1575 const int num_fuzz_objects = 10000; // The higher, the more thorough :)
1576
1577 flatbuffers::FlatBufferBuilder builder;
1578
1579 lcg_reset(); // Keep it deterministic.
1580
1581 flatbuffers::uoffset_t objects[num_fuzz_objects];
1582
1583 // Generate num_fuzz_objects random objects each consisting of
1584 // fields_per_object fields, each of a random type.
1585 for (int i = 0; i < num_fuzz_objects; i++) {
1586 auto start = builder.StartTable();
1587 for (flatbuffers::voffset_t f = 0; f < fields_per_object; f++) {
1588 int choice = lcg_rand() % test_values_max;
1589 auto off = flatbuffers::FieldIndexToOffset(f);
1590 switch (choice) {
1591 case 0: builder.AddElement<uint8_t>(off, bool_val, 0); break;
1592 case 1: builder.AddElement<int8_t>(off, char_val, 0); break;
1593 case 2: builder.AddElement<uint8_t>(off, uchar_val, 0); break;
1594 case 3: builder.AddElement<int16_t>(off, short_val, 0); break;
1595 case 4: builder.AddElement<uint16_t>(off, ushort_val, 0); break;
1596 case 5: builder.AddElement<int32_t>(off, int_val, 0); break;
1597 case 6: builder.AddElement<uint32_t>(off, uint_val, 0); break;
1598 case 7: builder.AddElement<int64_t>(off, long_val, 0); break;
1599 case 8: builder.AddElement<uint64_t>(off, ulong_val, 0); break;
1600 case 9: builder.AddElement<float>(off, float_val, 0); break;
1601 case 10: builder.AddElement<double>(off, double_val, 0); break;
1602 }
1603 }
1604 objects[i] = builder.EndTable(start);
1605 }
1606 builder.PreAlign<flatbuffers::largest_scalar_t>(0); // Align whole buffer.
1607
1608 lcg_reset(); // Reset.
1609
1610 uint8_t *eob = builder.GetCurrentBufferPointer() + builder.GetSize();
1611
1612 // Test that all objects we generated are readable and return the
1613 // expected values. We generate random objects in the same order
1614 // so this is deterministic.
1615 for (int i = 0; i < num_fuzz_objects; i++) {
1616 auto table = reinterpret_cast<flatbuffers::Table *>(eob - objects[i]);
1617 for (flatbuffers::voffset_t f = 0; f < fields_per_object; f++) {
1618 int choice = lcg_rand() % test_values_max;
1619 flatbuffers::voffset_t off = flatbuffers::FieldIndexToOffset(f);
1620 switch (choice) {
1621 case 0: CompareTableFieldValue(table, off, bool_val); break;
1622 case 1: CompareTableFieldValue(table, off, char_val); break;
1623 case 2: CompareTableFieldValue(table, off, uchar_val); break;
1624 case 3: CompareTableFieldValue(table, off, short_val); break;
1625 case 4: CompareTableFieldValue(table, off, ushort_val); break;
1626 case 5: CompareTableFieldValue(table, off, int_val); break;
1627 case 6: CompareTableFieldValue(table, off, uint_val); break;
1628 case 7: CompareTableFieldValue(table, off, long_val); break;
1629 case 8: CompareTableFieldValue(table, off, ulong_val); break;
1630 case 9: CompareTableFieldValue(table, off, float_val); break;
1631 case 10: CompareTableFieldValue(table, off, double_val); break;
1632 }
1633 }
1634 }
1635 }
1636
1637 // High level stress/fuzz test: generate a big schema and
1638 // matching json data in random combinations, then parse both,
1639 // generate json back from the binary, and compare with the original.
FuzzTest2()1640 void FuzzTest2() {
1641 lcg_reset(); // Keep it deterministic.
1642
1643 const int num_definitions = 30;
1644 const int num_struct_definitions = 5; // Subset of num_definitions.
1645 const int fields_per_definition = 15;
1646 const int instances_per_definition = 5;
1647 const int deprecation_rate = 10; // 1 in deprecation_rate fields will
1648 // be deprecated.
1649
1650 std::string schema = "namespace test;\n\n";
1651
1652 struct RndDef {
1653 std::string instances[instances_per_definition];
1654
1655 // Since we're generating schema and corresponding data in tandem,
1656 // this convenience function adds strings to both at once.
1657 static void Add(RndDef (&definitions_l)[num_definitions],
1658 std::string &schema_l, const int instances_per_definition_l,
1659 const char *schema_add, const char *instance_add,
1660 int definition) {
1661 schema_l += schema_add;
1662 for (int i = 0; i < instances_per_definition_l; i++)
1663 definitions_l[definition].instances[i] += instance_add;
1664 }
1665 };
1666
1667 // clang-format off
1668 #define AddToSchemaAndInstances(schema_add, instance_add) \
1669 RndDef::Add(definitions, schema, instances_per_definition, \
1670 schema_add, instance_add, definition)
1671
1672 #define Dummy() \
1673 RndDef::Add(definitions, schema, instances_per_definition, \
1674 "byte", "1", definition)
1675 // clang-format on
1676
1677 RndDef definitions[num_definitions];
1678
1679 // We are going to generate num_definitions, the first
1680 // num_struct_definitions will be structs, the rest tables. For each
1681 // generate random fields, some of which may be struct/table types
1682 // referring to previously generated structs/tables.
1683 // Simultanenously, we generate instances_per_definition JSON data
1684 // definitions, which will have identical structure to the schema
1685 // being generated. We generate multiple instances such that when creating
1686 // hierarchy, we get some variety by picking one randomly.
1687 for (int definition = 0; definition < num_definitions; definition++) {
1688 std::string definition_name = "D" + flatbuffers::NumToString(definition);
1689
1690 bool is_struct = definition < num_struct_definitions;
1691
1692 AddToSchemaAndInstances(
1693 ((is_struct ? "struct " : "table ") + definition_name + " {\n").c_str(),
1694 "{\n");
1695
1696 for (int field = 0; field < fields_per_definition; field++) {
1697 const bool is_last_field = field == fields_per_definition - 1;
1698
1699 // Deprecate 1 in deprecation_rate fields. Only table fields can be
1700 // deprecated.
1701 // Don't deprecate the last field to avoid dangling commas in JSON.
1702 const bool deprecated =
1703 !is_struct && !is_last_field && (lcg_rand() % deprecation_rate == 0);
1704
1705 std::string field_name = "f" + flatbuffers::NumToString(field);
1706 AddToSchemaAndInstances((" " + field_name + ":").c_str(),
1707 deprecated ? "" : (field_name + ": ").c_str());
1708 // Pick random type:
1709 auto base_type = static_cast<flatbuffers::BaseType>(
1710 lcg_rand() % (flatbuffers::BASE_TYPE_UNION + 1));
1711 switch (base_type) {
1712 case flatbuffers::BASE_TYPE_STRING:
1713 if (is_struct) {
1714 Dummy(); // No strings in structs.
1715 } else {
1716 AddToSchemaAndInstances("string", deprecated ? "" : "\"hi\"");
1717 }
1718 break;
1719 case flatbuffers::BASE_TYPE_VECTOR:
1720 if (is_struct) {
1721 Dummy(); // No vectors in structs.
1722 } else {
1723 AddToSchemaAndInstances("[ubyte]",
1724 deprecated ? "" : "[\n0,\n1,\n255\n]");
1725 }
1726 break;
1727 case flatbuffers::BASE_TYPE_NONE:
1728 case flatbuffers::BASE_TYPE_UTYPE:
1729 case flatbuffers::BASE_TYPE_STRUCT:
1730 case flatbuffers::BASE_TYPE_UNION:
1731 if (definition) {
1732 // Pick a random previous definition and random data instance of
1733 // that definition.
1734 int defref = lcg_rand() % definition;
1735 int instance = lcg_rand() % instances_per_definition;
1736 AddToSchemaAndInstances(
1737 ("D" + flatbuffers::NumToString(defref)).c_str(),
1738 deprecated ? ""
1739 : definitions[defref].instances[instance].c_str());
1740 } else {
1741 // If this is the first definition, we have no definition we can
1742 // refer to.
1743 Dummy();
1744 }
1745 break;
1746 case flatbuffers::BASE_TYPE_BOOL:
1747 AddToSchemaAndInstances(
1748 "bool", deprecated ? "" : (lcg_rand() % 2 ? "true" : "false"));
1749 break;
1750 case flatbuffers::BASE_TYPE_ARRAY:
1751 if (!is_struct) {
1752 AddToSchemaAndInstances(
1753 "ubyte",
1754 deprecated ? "" : "255"); // No fixed-length arrays in tables.
1755 } else {
1756 AddToSchemaAndInstances("[int:3]", deprecated ? "" : "[\n,\n,\n]");
1757 }
1758 break;
1759 default:
1760 // All the scalar types.
1761 schema += flatbuffers::kTypeNames[base_type];
1762
1763 if (!deprecated) {
1764 // We want each instance to use its own random value.
1765 for (int inst = 0; inst < instances_per_definition; inst++)
1766 definitions[definition].instances[inst] +=
1767 flatbuffers::IsFloat(base_type)
1768 ? flatbuffers::NumToString<double>(lcg_rand() % 128)
1769 .c_str()
1770 : flatbuffers::NumToString<int>(lcg_rand() % 128).c_str();
1771 }
1772 }
1773 AddToSchemaAndInstances(deprecated ? "(deprecated);\n" : ";\n",
1774 deprecated ? ""
1775 : is_last_field ? "\n"
1776 : ",\n");
1777 }
1778 AddToSchemaAndInstances("}\n\n", "}");
1779 }
1780
1781 schema += "root_type D" + flatbuffers::NumToString(num_definitions - 1);
1782 schema += ";\n";
1783
1784 flatbuffers::Parser parser;
1785
1786 // Will not compare against the original if we don't write defaults
1787 parser.builder_.ForceDefaults(true);
1788
1789 // Parse the schema, parse the generated data, then generate text back
1790 // from the binary and compare against the original.
1791 TEST_EQ(parser.Parse(schema.c_str()), true);
1792
1793 const std::string &json =
1794 definitions[num_definitions - 1].instances[0] + "\n";
1795
1796 TEST_EQ(parser.Parse(json.c_str()), true);
1797
1798 std::string jsongen;
1799 parser.opts.indent_step = 0;
1800 auto result =
1801 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
1802 TEST_EQ(result, true);
1803
1804 if (jsongen != json) {
1805 // These strings are larger than a megabyte, so we show the bytes around
1806 // the first bytes that are different rather than the whole string.
1807 size_t len = std::min(json.length(), jsongen.length());
1808 for (size_t i = 0; i < len; i++) {
1809 if (json[i] != jsongen[i]) {
1810 i -= std::min(static_cast<size_t>(10), i); // show some context;
1811 size_t end = std::min(len, i + 20);
1812 for (; i < end; i++)
1813 TEST_OUTPUT_LINE("at %d: found \"%c\", expected \"%c\"\n",
1814 static_cast<int>(i), jsongen[i], json[i]);
1815 break;
1816 }
1817 }
1818 TEST_NOTNULL(nullptr); //-V501 (this comment supresses CWE-570 warning)
1819 }
1820
1821 // clang-format off
1822 #ifdef FLATBUFFERS_TEST_VERBOSE
1823 TEST_OUTPUT_LINE("%dk schema tested with %dk of json\n",
1824 static_cast<int>(schema.length() / 1024),
1825 static_cast<int>(json.length() / 1024));
1826 #endif
1827 // clang-format on
1828 }
1829
1830 // Test that parser errors are actually generated.
TestError_(const char * src,const char * error_substr,bool strict_json,const char * file,int line,const char * func)1831 void TestError_(const char *src, const char *error_substr, bool strict_json,
1832 const char *file, int line, const char *func) {
1833 flatbuffers::IDLOptions opts;
1834 opts.strict_json = strict_json;
1835 flatbuffers::Parser parser(opts);
1836 if (parser.Parse(src)) {
1837 TestFail("true", "false",
1838 ("parser.Parse(\"" + std::string(src) + "\")").c_str(), file, line,
1839 func);
1840 } else if (!strstr(parser.error_.c_str(), error_substr)) {
1841 TestFail(error_substr, parser.error_.c_str(),
1842 ("parser.Parse(\"" + std::string(src) + "\")").c_str(), file, line,
1843 func);
1844 }
1845 }
1846
TestError_(const char * src,const char * error_substr,const char * file,int line,const char * func)1847 void TestError_(const char *src, const char *error_substr, const char *file,
1848 int line, const char *func) {
1849 TestError_(src, error_substr, false, file, line, func);
1850 }
1851
1852 #ifdef _WIN32
1853 # define TestError(src, ...) \
1854 TestError_(src, __VA_ARGS__, __FILE__, __LINE__, __FUNCTION__)
1855 #else
1856 # define TestError(src, ...) \
1857 TestError_(src, __VA_ARGS__, __FILE__, __LINE__, __PRETTY_FUNCTION__)
1858 #endif
1859
1860 // Test that parsing errors occur as we'd expect.
1861 // Also useful for coverage, making sure these paths are run.
ErrorTest()1862 void ErrorTest() {
1863 // In order they appear in idl_parser.cpp
1864 TestError("table X { Y:byte; } root_type X; { Y: 999 }", "does not fit");
1865 TestError("\"\0", "illegal");
1866 TestError("\"\\q", "escape code");
1867 TestError("table ///", "documentation");
1868 TestError("@", "illegal");
1869 TestError("table 1", "expecting");
1870 TestError("table X { Y:[[int]]; }", "nested vector");
1871 TestError("table X { Y:1; }", "illegal type");
1872 TestError("table X { Y:int; Y:int; }", "field already");
1873 TestError("table Y {} table X { Y:int; }", "same as table");
1874 TestError("struct X { Y:string; }", "only scalar");
1875 TestError("struct X { a:uint = 42; }", "default values");
1876 TestError("enum Y:byte { Z = 1 } table X { y:Y; }", "not part of enum");
1877 TestError("struct X { Y:int (deprecated); }", "deprecate");
1878 TestError("union Z { X } table X { Y:Z; } root_type X; { Y: {}, A:1 }",
1879 "missing type field");
1880 TestError("union Z { X } table X { Y:Z; } root_type X; { Y_type: 99, Y: {",
1881 "type id");
1882 TestError("table X { Y:int; } root_type X; { Z:", "unknown field");
1883 TestError("table X { Y:int; } root_type X; { Y:", "string constant", true);
1884 TestError("table X { Y:int; } root_type X; { \"Y\":1, }", "string constant",
1885 true);
1886 TestError(
1887 "struct X { Y:int; Z:int; } table W { V:X; } root_type W; "
1888 "{ V:{ Y:1 } }",
1889 "wrong number");
1890 TestError("enum E:byte { A } table X { Y:E; } root_type X; { Y:U }",
1891 "unknown enum value");
1892 TestError("table X { Y:byte; } root_type X; { Y:; }", "starting");
1893 TestError("enum X:byte { Y } enum X {", "enum already");
1894 TestError("enum X:float {}", "underlying");
1895 TestError("enum X:byte { Y, Y }", "value already");
1896 TestError("enum X:byte { Y=2, Z=2 }", "unique");
1897 TestError("table X { Y:int; } table X {", "datatype already");
1898 TestError("table X { } union X { }", "datatype already");
1899 TestError("union X { } table X { }", "datatype already");
1900 TestError("namespace A; table X { } namespace A; union X { }",
1901 "datatype already");
1902 TestError("namespace A; union X { } namespace A; table X { }",
1903 "datatype already");
1904 TestError("struct X (force_align: 7) { Y:int; }", "force_align");
1905 TestError("struct X {}", "size 0");
1906 TestError("{}", "no root");
1907 TestError("table X { Y:byte; } root_type X; { Y:1 } { Y:1 }", "end of file");
1908 TestError("table X { Y:byte; } root_type X; { Y:1 } table Y{ Z:int }",
1909 "end of file");
1910 TestError("root_type X;", "unknown root");
1911 TestError("struct X { Y:int; } root_type X;", "a table");
1912 TestError("union X { Y }", "referenced");
1913 TestError("union Z { X } struct X { Y:int; }", "only tables");
1914 TestError("table X { Y:[int]; YLength:int; }", "clash");
1915 TestError("table X { Y:byte; } root_type X; { Y:1, Y:2 }", "more than once");
1916 // float to integer conversion is forbidden
1917 TestError("table X { Y:int; } root_type X; { Y:1.0 }", "float");
1918 TestError("table X { Y:bool; } root_type X; { Y:1.0 }", "float");
1919 TestError("enum X:bool { Y = true }", "must be integral");
1920 // Array of non-scalar
1921 TestError("table X { x:int; } struct Y { y:[X:2]; }",
1922 "may contain only scalar or struct fields");
1923 // Non-snake case field names
1924 TestError("table X { Y: int; } root_type Y: {Y:1.0}", "snake_case");
1925 // Complex defaults
1926 TestError("table X { y: string = 1; }", "expecting: string");
1927 TestError("table X { y: string = []; }", " Cannot assign token");
1928 TestError("table X { y: [int] = [1]; }", "Expected `]`");
1929 TestError("table X { y: [int] = [; }", "Expected `]`");
1930 TestError("table X { y: [int] = \"\"; }", "type mismatch");
1931 // An identifier can't start from sign (+|-)
1932 TestError("table X { -Y: int; } root_type Y: {Y:1.0}", "identifier");
1933 TestError("table X { +Y: int; } root_type Y: {Y:1.0}", "identifier");
1934 }
1935
1936 template<typename T>
TestValue(const char * json,const char * type_name,const char * decls=nullptr)1937 T TestValue(const char *json, const char *type_name,
1938 const char *decls = nullptr) {
1939 flatbuffers::Parser parser;
1940 parser.builder_.ForceDefaults(true); // return defaults
1941 auto check_default = json ? false : true;
1942 if (check_default) { parser.opts.output_default_scalars_in_json = true; }
1943 // Simple schema.
1944 std::string schema = std::string(decls ? decls : "") + "\n" +
1945 "table X { y:" + std::string(type_name) +
1946 "; } root_type X;";
1947 auto schema_done = parser.Parse(schema.c_str());
1948 TEST_EQ_STR(parser.error_.c_str(), "");
1949 TEST_EQ(schema_done, true);
1950
1951 auto done = parser.Parse(check_default ? "{}" : json);
1952 TEST_EQ_STR(parser.error_.c_str(), "");
1953 TEST_EQ(done, true);
1954
1955 // Check with print.
1956 std::string print_back;
1957 parser.opts.indent_step = -1;
1958 TEST_EQ(GenerateText(parser, parser.builder_.GetBufferPointer(), &print_back),
1959 true);
1960 // restore value from its default
1961 if (check_default) { TEST_EQ(parser.Parse(print_back.c_str()), true); }
1962
1963 auto root = flatbuffers::GetRoot<flatbuffers::Table>(
1964 parser.builder_.GetBufferPointer());
1965 return root->GetField<T>(flatbuffers::FieldIndexToOffset(0), 0);
1966 }
1967
FloatCompare(float a,float b)1968 bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; }
1969
1970 // Additional parser testing not covered elsewhere.
ValueTest()1971 void ValueTest() {
1972 // Test scientific notation numbers.
1973 TEST_EQ(
1974 FloatCompare(TestValue<float>("{ y:0.0314159e+2 }", "float"), 3.14159f),
1975 true);
1976 // number in string
1977 TEST_EQ(FloatCompare(TestValue<float>("{ y:\"0.0314159e+2\" }", "float"),
1978 3.14159f),
1979 true);
1980
1981 // Test conversion functions.
1982 TEST_EQ(FloatCompare(TestValue<float>("{ y:cos(rad(180)) }", "float"), -1),
1983 true);
1984
1985 // int embedded to string
1986 TEST_EQ(TestValue<int>("{ y:\"-876\" }", "int=-123"), -876);
1987 TEST_EQ(TestValue<int>("{ y:\"876\" }", "int=-123"), 876);
1988
1989 // Test negative hex constant.
1990 TEST_EQ(TestValue<int>("{ y:-0x8ea0 }", "int=-0x8ea0"), -36512);
1991 TEST_EQ(TestValue<int>(nullptr, "int=-0x8ea0"), -36512);
1992
1993 // positive hex constant
1994 TEST_EQ(TestValue<int>("{ y:0x1abcdef }", "int=0x1"), 0x1abcdef);
1995 // with optional '+' sign
1996 TEST_EQ(TestValue<int>("{ y:+0x1abcdef }", "int=+0x1"), 0x1abcdef);
1997 // hex in string
1998 TEST_EQ(TestValue<int>("{ y:\"0x1abcdef\" }", "int=+0x1"), 0x1abcdef);
1999
2000 // Make sure we do unsigned 64bit correctly.
2001 TEST_EQ(TestValue<uint64_t>("{ y:12335089644688340133 }", "ulong"),
2002 12335089644688340133ULL);
2003
2004 // bool in string
2005 TEST_EQ(TestValue<bool>("{ y:\"false\" }", "bool=true"), false);
2006 TEST_EQ(TestValue<bool>("{ y:\"true\" }", "bool=\"true\""), true);
2007 TEST_EQ(TestValue<bool>("{ y:'false' }", "bool=true"), false);
2008 TEST_EQ(TestValue<bool>("{ y:'true' }", "bool=\"true\""), true);
2009
2010 // check comments before and after json object
2011 TEST_EQ(TestValue<int>("/*before*/ { y:1 } /*after*/", "int"), 1);
2012 TEST_EQ(TestValue<int>("//before \n { y:1 } //after", "int"), 1);
2013 }
2014
NestedListTest()2015 void NestedListTest() {
2016 flatbuffers::Parser parser1;
2017 TEST_EQ(parser1.Parse("struct Test { a:short; b:byte; } table T { F:[Test]; }"
2018 "root_type T;"
2019 "{ F:[ [10,20], [30,40]] }"),
2020 true);
2021 }
2022
EnumStringsTest()2023 void EnumStringsTest() {
2024 flatbuffers::Parser parser1;
2025 TEST_EQ(parser1.Parse("enum E:byte { A, B, C } table T { F:[E]; }"
2026 "root_type T;"
2027 "{ F:[ A, B, \"C\", \"A B C\" ] }"),
2028 true);
2029 flatbuffers::Parser parser2;
2030 TEST_EQ(parser2.Parse("enum E:byte { A, B, C } table T { F:[int]; }"
2031 "root_type T;"
2032 "{ F:[ \"E.C\", \"E.A E.B E.C\" ] }"),
2033 true);
2034 // unsigned bit_flags
2035 flatbuffers::Parser parser3;
2036 TEST_EQ(
2037 parser3.Parse("enum E:uint16 (bit_flags) { F0, F07=7, F08, F14=14, F15 }"
2038 " table T { F: E = \"F15 F08\"; }"
2039 "root_type T;"),
2040 true);
2041 }
2042
EnumNamesTest()2043 void EnumNamesTest() {
2044 TEST_EQ_STR("Red", EnumNameColor(Color_Red));
2045 TEST_EQ_STR("Green", EnumNameColor(Color_Green));
2046 TEST_EQ_STR("Blue", EnumNameColor(Color_Blue));
2047 // Check that Color to string don't crash while decode a mixture of Colors.
2048 // 1) Example::Color enum is enum with unfixed underlying type.
2049 // 2) Valid enum range: [0; 2^(ceil(log2(Color_ANY))) - 1].
2050 // Consequence: A value is out of this range will lead to UB (since C++17).
2051 // For details see C++17 standard or explanation on the SO:
2052 // stackoverflow.com/questions/18195312/what-happens-if-you-static-cast-invalid-value-to-enum-class
2053 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(0)));
2054 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY - 1)));
2055 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY + 1)));
2056 }
2057
EnumOutOfRangeTest()2058 void EnumOutOfRangeTest() {
2059 TestError("enum X:byte { Y = 128 }", "enum value does not fit");
2060 TestError("enum X:byte { Y = -129 }", "enum value does not fit");
2061 TestError("enum X:byte { Y = 126, Z0, Z1 }", "enum value does not fit");
2062 TestError("enum X:ubyte { Y = -1 }", "enum value does not fit");
2063 TestError("enum X:ubyte { Y = 256 }", "enum value does not fit");
2064 TestError("enum X:ubyte { Y = 255, Z }", "enum value does not fit");
2065 TestError("table Y{} union X { Y = -1 }", "enum value does not fit");
2066 TestError("table Y{} union X { Y = 256 }", "enum value does not fit");
2067 TestError("table Y{} union X { Y = 255, Z:Y }", "enum value does not fit");
2068 TestError("enum X:int { Y = -2147483649 }", "enum value does not fit");
2069 TestError("enum X:int { Y = 2147483648 }", "enum value does not fit");
2070 TestError("enum X:uint { Y = -1 }", "enum value does not fit");
2071 TestError("enum X:uint { Y = 4294967297 }", "enum value does not fit");
2072 TestError("enum X:long { Y = 9223372036854775808 }", "does not fit");
2073 TestError("enum X:long { Y = 9223372036854775807, Z }",
2074 "enum value does not fit");
2075 TestError("enum X:ulong { Y = -1 }", "does not fit");
2076 TestError("enum X:ubyte (bit_flags) { Y=8 }", "bit flag out");
2077 TestError("enum X:byte (bit_flags) { Y=7 }", "must be unsigned"); // -128
2078 // bit_flgs out of range
2079 TestError("enum X:ubyte (bit_flags) { Y0,Y1,Y2,Y3,Y4,Y5,Y6,Y7,Y8 }",
2080 "out of range");
2081 }
2082
EnumValueTest()2083 void EnumValueTest() {
2084 // json: "{ Y:0 }", schema: table X { y: "E"}
2085 // 0 in enum (V=0) E then Y=0 is valid.
2086 TEST_EQ(TestValue<int>("{ y:0 }", "E", "enum E:int { V }"), 0);
2087 TEST_EQ(TestValue<int>("{ y:V }", "E", "enum E:int { V }"), 0);
2088 // A default value of Y is 0.
2089 TEST_EQ(TestValue<int>("{ }", "E", "enum E:int { V }"), 0);
2090 TEST_EQ(TestValue<int>("{ y:5 }", "E=V", "enum E:int { V=5 }"), 5);
2091 // Generate json with defaults and check.
2092 TEST_EQ(TestValue<int>(nullptr, "E=V", "enum E:int { V=5 }"), 5);
2093 // 5 in enum
2094 TEST_EQ(TestValue<int>("{ y:5 }", "E", "enum E:int { Z, V=5 }"), 5);
2095 TEST_EQ(TestValue<int>("{ y:5 }", "E=V", "enum E:int { Z, V=5 }"), 5);
2096 // Generate json with defaults and check.
2097 TEST_EQ(TestValue<int>(nullptr, "E", "enum E:int { Z, V=5 }"), 0);
2098 TEST_EQ(TestValue<int>(nullptr, "E=V", "enum E:int { Z, V=5 }"), 5);
2099 // u84 test
2100 TEST_EQ(TestValue<uint64_t>(nullptr, "E=V",
2101 "enum E:ulong { V = 13835058055282163712 }"),
2102 13835058055282163712ULL);
2103 TEST_EQ(TestValue<uint64_t>(nullptr, "E=V",
2104 "enum E:ulong { V = 18446744073709551615 }"),
2105 18446744073709551615ULL);
2106 // Assign non-enum value to enum field. Is it right?
2107 TEST_EQ(TestValue<int>("{ y:7 }", "E", "enum E:int { V = 0 }"), 7);
2108 // Check that non-ascending values are valid.
2109 TEST_EQ(TestValue<int>("{ y:5 }", "E=V", "enum E:int { Z=10, V=5 }"), 5);
2110 }
2111
IntegerOutOfRangeTest()2112 void IntegerOutOfRangeTest() {
2113 TestError("table T { F:byte; } root_type T; { F:128 }",
2114 "constant does not fit");
2115 TestError("table T { F:byte; } root_type T; { F:-129 }",
2116 "constant does not fit");
2117 TestError("table T { F:ubyte; } root_type T; { F:256 }",
2118 "constant does not fit");
2119 TestError("table T { F:ubyte; } root_type T; { F:-1 }",
2120 "constant does not fit");
2121 TestError("table T { F:short; } root_type T; { F:32768 }",
2122 "constant does not fit");
2123 TestError("table T { F:short; } root_type T; { F:-32769 }",
2124 "constant does not fit");
2125 TestError("table T { F:ushort; } root_type T; { F:65536 }",
2126 "constant does not fit");
2127 TestError("table T { F:ushort; } root_type T; { F:-1 }",
2128 "constant does not fit");
2129 TestError("table T { F:int; } root_type T; { F:2147483648 }",
2130 "constant does not fit");
2131 TestError("table T { F:int; } root_type T; { F:-2147483649 }",
2132 "constant does not fit");
2133 TestError("table T { F:uint; } root_type T; { F:4294967296 }",
2134 "constant does not fit");
2135 TestError("table T { F:uint; } root_type T; { F:-1 }",
2136 "constant does not fit");
2137 // Check fixed width aliases
2138 TestError("table X { Y:uint8; } root_type X; { Y: -1 }", "does not fit");
2139 TestError("table X { Y:uint8; } root_type X; { Y: 256 }", "does not fit");
2140 TestError("table X { Y:uint16; } root_type X; { Y: -1 }", "does not fit");
2141 TestError("table X { Y:uint16; } root_type X; { Y: 65536 }", "does not fit");
2142 TestError("table X { Y:uint32; } root_type X; { Y: -1 }", "");
2143 TestError("table X { Y:uint32; } root_type X; { Y: 4294967296 }",
2144 "does not fit");
2145 TestError("table X { Y:uint64; } root_type X; { Y: -1 }", "");
2146 TestError("table X { Y:uint64; } root_type X; { Y: -9223372036854775809 }",
2147 "does not fit");
2148 TestError("table X { Y:uint64; } root_type X; { Y: 18446744073709551616 }",
2149 "does not fit");
2150
2151 TestError("table X { Y:int8; } root_type X; { Y: -129 }", "does not fit");
2152 TestError("table X { Y:int8; } root_type X; { Y: 128 }", "does not fit");
2153 TestError("table X { Y:int16; } root_type X; { Y: -32769 }", "does not fit");
2154 TestError("table X { Y:int16; } root_type X; { Y: 32768 }", "does not fit");
2155 TestError("table X { Y:int32; } root_type X; { Y: -2147483649 }", "");
2156 TestError("table X { Y:int32; } root_type X; { Y: 2147483648 }",
2157 "does not fit");
2158 TestError("table X { Y:int64; } root_type X; { Y: -9223372036854775809 }",
2159 "does not fit");
2160 TestError("table X { Y:int64; } root_type X; { Y: 9223372036854775808 }",
2161 "does not fit");
2162 // check out-of-int64 as int8
2163 TestError("table X { Y:int8; } root_type X; { Y: -9223372036854775809 }",
2164 "does not fit");
2165 TestError("table X { Y:int8; } root_type X; { Y: 9223372036854775808 }",
2166 "does not fit");
2167
2168 // Check default values
2169 TestError("table X { Y:int64=-9223372036854775809; } root_type X; {}",
2170 "does not fit");
2171 TestError("table X { Y:int64= 9223372036854775808; } root_type X; {}",
2172 "does not fit");
2173 TestError("table X { Y:uint64; } root_type X; { Y: -1 }", "");
2174 TestError("table X { Y:uint64=-9223372036854775809; } root_type X; {}",
2175 "does not fit");
2176 TestError("table X { Y:uint64= 18446744073709551616; } root_type X; {}",
2177 "does not fit");
2178 }
2179
IntegerBoundaryTest()2180 void IntegerBoundaryTest() {
2181 // Check numerical compatibility with non-C++ languages.
2182 // By the C++ standard, std::numerical_limits<int64_t>::min() ==
2183 // -9223372036854775807 (-2^63+1) or less* The Flatbuffers grammar and most of
2184 // the languages (C#, Java, Rust) expect that minimum values are: -128,
2185 // -32768,.., -9223372036854775808. Since C++20,
2186 // static_cast<int64>(0x8000000000000000ULL) is well-defined two's complement
2187 // cast. Therefore -9223372036854775808 should be valid negative value.
2188 TEST_EQ(flatbuffers::numeric_limits<int8_t>::min(), -128);
2189 TEST_EQ(flatbuffers::numeric_limits<int8_t>::max(), 127);
2190 TEST_EQ(flatbuffers::numeric_limits<int16_t>::min(), -32768);
2191 TEST_EQ(flatbuffers::numeric_limits<int16_t>::max(), 32767);
2192 TEST_EQ(flatbuffers::numeric_limits<int32_t>::min() + 1, -2147483647);
2193 TEST_EQ(flatbuffers::numeric_limits<int32_t>::max(), 2147483647ULL);
2194 TEST_EQ(flatbuffers::numeric_limits<int64_t>::min() + 1LL,
2195 -9223372036854775807LL);
2196 TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(), 9223372036854775807ULL);
2197 TEST_EQ(flatbuffers::numeric_limits<uint8_t>::max(), 255);
2198 TEST_EQ(flatbuffers::numeric_limits<uint16_t>::max(), 65535);
2199 TEST_EQ(flatbuffers::numeric_limits<uint32_t>::max(), 4294967295ULL);
2200 TEST_EQ(flatbuffers::numeric_limits<uint64_t>::max(),
2201 18446744073709551615ULL);
2202
2203 TEST_EQ(TestValue<int8_t>("{ y:127 }", "byte"), 127);
2204 TEST_EQ(TestValue<int8_t>("{ y:-128 }", "byte"), -128);
2205 TEST_EQ(TestValue<uint8_t>("{ y:255 }", "ubyte"), 255);
2206 TEST_EQ(TestValue<uint8_t>("{ y:0 }", "ubyte"), 0);
2207 TEST_EQ(TestValue<int16_t>("{ y:32767 }", "short"), 32767);
2208 TEST_EQ(TestValue<int16_t>("{ y:-32768 }", "short"), -32768);
2209 TEST_EQ(TestValue<uint16_t>("{ y:65535 }", "ushort"), 65535);
2210 TEST_EQ(TestValue<uint16_t>("{ y:0 }", "ushort"), 0);
2211 TEST_EQ(TestValue<int32_t>("{ y:2147483647 }", "int"), 2147483647);
2212 TEST_EQ(TestValue<int32_t>("{ y:-2147483648 }", "int") + 1, -2147483647);
2213 TEST_EQ(TestValue<uint32_t>("{ y:4294967295 }", "uint"), 4294967295);
2214 TEST_EQ(TestValue<uint32_t>("{ y:0 }", "uint"), 0);
2215 TEST_EQ(TestValue<int64_t>("{ y:9223372036854775807 }", "long"),
2216 9223372036854775807LL);
2217 TEST_EQ(TestValue<int64_t>("{ y:-9223372036854775808 }", "long") + 1LL,
2218 -9223372036854775807LL);
2219 TEST_EQ(TestValue<uint64_t>("{ y:18446744073709551615 }", "ulong"),
2220 18446744073709551615ULL);
2221 TEST_EQ(TestValue<uint64_t>("{ y:0 }", "ulong"), 0);
2222 TEST_EQ(TestValue<uint64_t>("{ y: 18446744073709551615 }", "uint64"),
2223 18446744073709551615ULL);
2224 // check that the default works
2225 TEST_EQ(TestValue<uint64_t>(nullptr, "uint64 = 18446744073709551615"),
2226 18446744073709551615ULL);
2227 }
2228
ValidFloatTest()2229 void ValidFloatTest() {
2230 // check rounding to infinity
2231 TEST_EQ(TestValue<float>("{ y:+3.4029e+38 }", "float"), +infinity_f);
2232 TEST_EQ(TestValue<float>("{ y:-3.4029e+38 }", "float"), -infinity_f);
2233 TEST_EQ(TestValue<double>("{ y:+1.7977e+308 }", "double"), +infinity_d);
2234 TEST_EQ(TestValue<double>("{ y:-1.7977e+308 }", "double"), -infinity_d);
2235
2236 TEST_EQ(
2237 FloatCompare(TestValue<float>("{ y:0.0314159e+2 }", "float"), 3.14159f),
2238 true);
2239 // float in string
2240 TEST_EQ(FloatCompare(TestValue<float>("{ y:\" 0.0314159e+2 \" }", "float"),
2241 3.14159f),
2242 true);
2243
2244 TEST_EQ(TestValue<float>("{ y:1 }", "float"), 1.0f);
2245 TEST_EQ(TestValue<float>("{ y:1.0 }", "float"), 1.0f);
2246 TEST_EQ(TestValue<float>("{ y:1. }", "float"), 1.0f);
2247 TEST_EQ(TestValue<float>("{ y:+1. }", "float"), 1.0f);
2248 TEST_EQ(TestValue<float>("{ y:-1. }", "float"), -1.0f);
2249 TEST_EQ(TestValue<float>("{ y:1.e0 }", "float"), 1.0f);
2250 TEST_EQ(TestValue<float>("{ y:1.e+0 }", "float"), 1.0f);
2251 TEST_EQ(TestValue<float>("{ y:1.e-0 }", "float"), 1.0f);
2252 TEST_EQ(TestValue<float>("{ y:0.125 }", "float"), 0.125f);
2253 TEST_EQ(TestValue<float>("{ y:.125 }", "float"), 0.125f);
2254 TEST_EQ(TestValue<float>("{ y:-.125 }", "float"), -0.125f);
2255 TEST_EQ(TestValue<float>("{ y:+.125 }", "float"), +0.125f);
2256 TEST_EQ(TestValue<float>("{ y:5 }", "float"), 5.0f);
2257 TEST_EQ(TestValue<float>("{ y:\"5\" }", "float"), 5.0f);
2258
2259 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
2260 // Old MSVC versions may have problem with this check.
2261 // https://www.exploringbinary.com/visual-c-plus-plus-strtod-still-broken/
2262 TEST_EQ(TestValue<double>("{ y:6.9294956446009195e15 }", "double"),
2263 6929495644600920.0);
2264 // check nan's
2265 TEST_EQ(std::isnan(TestValue<double>("{ y:nan }", "double")), true);
2266 TEST_EQ(std::isnan(TestValue<float>("{ y:nan }", "float")), true);
2267 TEST_EQ(std::isnan(TestValue<float>("{ y:\"nan\" }", "float")), true);
2268 TEST_EQ(std::isnan(TestValue<float>("{ y:\"+nan\" }", "float")), true);
2269 TEST_EQ(std::isnan(TestValue<float>("{ y:\"-nan\" }", "float")), true);
2270 TEST_EQ(std::isnan(TestValue<float>("{ y:+nan }", "float")), true);
2271 TEST_EQ(std::isnan(TestValue<float>("{ y:-nan }", "float")), true);
2272 TEST_EQ(std::isnan(TestValue<float>(nullptr, "float=nan")), true);
2273 TEST_EQ(std::isnan(TestValue<float>(nullptr, "float=-nan")), true);
2274 // check inf
2275 TEST_EQ(TestValue<float>("{ y:inf }", "float"), infinity_f);
2276 TEST_EQ(TestValue<float>("{ y:\"inf\" }", "float"), infinity_f);
2277 TEST_EQ(TestValue<float>("{ y:\"-inf\" }", "float"), -infinity_f);
2278 TEST_EQ(TestValue<float>("{ y:\"+inf\" }", "float"), infinity_f);
2279 TEST_EQ(TestValue<float>("{ y:+inf }", "float"), infinity_f);
2280 TEST_EQ(TestValue<float>("{ y:-inf }", "float"), -infinity_f);
2281 TEST_EQ(TestValue<float>(nullptr, "float=inf"), infinity_f);
2282 TEST_EQ(TestValue<float>(nullptr, "float=-inf"), -infinity_f);
2283 TestValue<double>(
2284 "{ y: [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, "
2285 "3.0e2] }",
2286 "[double]");
2287 TestValue<float>(
2288 "{ y: [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, "
2289 "3.0e2] }",
2290 "[float]");
2291
2292 // Test binary format of float point.
2293 // https://en.cppreference.com/w/cpp/language/floating_literal
2294 // 0x11.12p-1 = (1*16^1 + 2*16^0 + 3*16^-1 + 4*16^-2) * 2^-1 =
2295 TEST_EQ(TestValue<double>("{ y:0x12.34p-1 }", "double"), 9.1015625);
2296 // hex fraction 1.2 (decimal 1.125) scaled by 2^3, that is 9.0
2297 TEST_EQ(TestValue<float>("{ y:-0x0.2p0 }", "float"), -0.125f);
2298 TEST_EQ(TestValue<float>("{ y:-0x.2p1 }", "float"), -0.25f);
2299 TEST_EQ(TestValue<float>("{ y:0x1.2p3 }", "float"), 9.0f);
2300 TEST_EQ(TestValue<float>("{ y:0x10.1p0 }", "float"), 16.0625f);
2301 TEST_EQ(TestValue<double>("{ y:0x1.2p3 }", "double"), 9.0);
2302 TEST_EQ(TestValue<double>("{ y:0x10.1p0 }", "double"), 16.0625);
2303 TEST_EQ(TestValue<double>("{ y:0xC.68p+2 }", "double"), 49.625);
2304 TestValue<double>("{ y: [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[double]");
2305 TestValue<float>("{ y: [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[float]");
2306
2307 #else // FLATBUFFERS_HAS_NEW_STRTOD
2308 TEST_OUTPUT_LINE("FLATBUFFERS_HAS_NEW_STRTOD tests skipped");
2309 #endif // !FLATBUFFERS_HAS_NEW_STRTOD
2310 }
2311
InvalidFloatTest()2312 void InvalidFloatTest() {
2313 auto invalid_msg = "invalid number";
2314 auto comma_msg = "expecting: ,";
2315 TestError("table T { F:float; } root_type T; { F:1,0 }", "");
2316 TestError("table T { F:float; } root_type T; { F:. }", "");
2317 TestError("table T { F:float; } root_type T; { F:- }", invalid_msg);
2318 TestError("table T { F:float; } root_type T; { F:+ }", invalid_msg);
2319 TestError("table T { F:float; } root_type T; { F:-. }", invalid_msg);
2320 TestError("table T { F:float; } root_type T; { F:+. }", invalid_msg);
2321 TestError("table T { F:float; } root_type T; { F:.e }", "");
2322 TestError("table T { F:float; } root_type T; { F:-e }", invalid_msg);
2323 TestError("table T { F:float; } root_type T; { F:+e }", invalid_msg);
2324 TestError("table T { F:float; } root_type T; { F:-.e }", invalid_msg);
2325 TestError("table T { F:float; } root_type T; { F:+.e }", invalid_msg);
2326 TestError("table T { F:float; } root_type T; { F:-e1 }", invalid_msg);
2327 TestError("table T { F:float; } root_type T; { F:+e1 }", invalid_msg);
2328 TestError("table T { F:float; } root_type T; { F:1.0e+ }", invalid_msg);
2329 TestError("table T { F:float; } root_type T; { F:1.0e- }", invalid_msg);
2330 // exponent pP is mandatory for hex-float
2331 TestError("table T { F:float; } root_type T; { F:0x0 }", invalid_msg);
2332 TestError("table T { F:float; } root_type T; { F:-0x. }", invalid_msg);
2333 TestError("table T { F:float; } root_type T; { F:0x. }", invalid_msg);
2334 TestError("table T { F:float; } root_type T; { F:0Xe }", invalid_msg);
2335 TestError("table T { F:float; } root_type T; { F:\"0Xe\" }", invalid_msg);
2336 TestError("table T { F:float; } root_type T; { F:\"nan(1)\" }", invalid_msg);
2337 // eE not exponent in hex-float!
2338 TestError("table T { F:float; } root_type T; { F:0x0.0e+ }", invalid_msg);
2339 TestError("table T { F:float; } root_type T; { F:0x0.0e- }", invalid_msg);
2340 TestError("table T { F:float; } root_type T; { F:0x0.0p }", invalid_msg);
2341 TestError("table T { F:float; } root_type T; { F:0x0.0p+ }", invalid_msg);
2342 TestError("table T { F:float; } root_type T; { F:0x0.0p- }", invalid_msg);
2343 TestError("table T { F:float; } root_type T; { F:0x0.0pa1 }", invalid_msg);
2344 TestError("table T { F:float; } root_type T; { F:0x0.0e+ }", invalid_msg);
2345 TestError("table T { F:float; } root_type T; { F:0x0.0e- }", invalid_msg);
2346 TestError("table T { F:float; } root_type T; { F:0x0.0e+0 }", invalid_msg);
2347 TestError("table T { F:float; } root_type T; { F:0x0.0e-0 }", invalid_msg);
2348 TestError("table T { F:float; } root_type T; { F:0x0.0ep+ }", invalid_msg);
2349 TestError("table T { F:float; } root_type T; { F:0x0.0ep- }", invalid_msg);
2350 TestError("table T { F:float; } root_type T; { F:1.2.3 }", invalid_msg);
2351 TestError("table T { F:float; } root_type T; { F:1.2.e3 }", invalid_msg);
2352 TestError("table T { F:float; } root_type T; { F:1.2e.3 }", invalid_msg);
2353 TestError("table T { F:float; } root_type T; { F:1.2e0.3 }", invalid_msg);
2354 TestError("table T { F:float; } root_type T; { F:1.2e3. }", invalid_msg);
2355 TestError("table T { F:float; } root_type T; { F:1.2e3.0 }", invalid_msg);
2356 TestError("table T { F:float; } root_type T; { F:+-1.0 }", invalid_msg);
2357 TestError("table T { F:float; } root_type T; { F:1.0e+-1 }", invalid_msg);
2358 TestError("table T { F:float; } root_type T; { F:\"1.0e+-1\" }", invalid_msg);
2359 TestError("table T { F:float; } root_type T; { F:1.e0e }", comma_msg);
2360 TestError("table T { F:float; } root_type T; { F:0x1.p0e }", comma_msg);
2361 TestError("table T { F:float; } root_type T; { F:\" 0x10 \" }", invalid_msg);
2362 // floats in string
2363 TestError("table T { F:float; } root_type T; { F:\"1,2.\" }", invalid_msg);
2364 TestError("table T { F:float; } root_type T; { F:\"1.2e3.\" }", invalid_msg);
2365 TestError("table T { F:float; } root_type T; { F:\"0x1.p0e\" }", invalid_msg);
2366 TestError("table T { F:float; } root_type T; { F:\"0x1.0\" }", invalid_msg);
2367 TestError("table T { F:float; } root_type T; { F:\" 0x1.0\" }", invalid_msg);
2368 TestError("table T { F:float; } root_type T; { F:\"+ 0\" }", invalid_msg);
2369 // disable escapes for "number-in-string"
2370 TestError("table T { F:float; } root_type T; { F:\"\\f1.2e3.\" }", "invalid");
2371 TestError("table T { F:float; } root_type T; { F:\"\\t1.2e3.\" }", "invalid");
2372 TestError("table T { F:float; } root_type T; { F:\"\\n1.2e3.\" }", "invalid");
2373 TestError("table T { F:float; } root_type T; { F:\"\\r1.2e3.\" }", "invalid");
2374 TestError("table T { F:float; } root_type T; { F:\"4\\x005\" }", "invalid");
2375 TestError("table T { F:float; } root_type T; { F:\"\'12\'\" }", invalid_msg);
2376 // null is not a number constant!
2377 TestError("table T { F:float; } root_type T; { F:\"null\" }", invalid_msg);
2378 TestError("table T { F:float; } root_type T; { F:null }", invalid_msg);
2379 }
2380
GenerateTableTextTest()2381 void GenerateTableTextTest() {
2382 std::string schemafile;
2383 std::string jsonfile;
2384 bool ok =
2385 flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
2386 false, &schemafile) &&
2387 flatbuffers::LoadFile((test_data_path + "monsterdata_test.json").c_str(),
2388 false, &jsonfile);
2389 TEST_EQ(ok, true);
2390 auto include_test_path =
2391 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
2392 const char *include_directories[] = { test_data_path.c_str(),
2393 include_test_path.c_str(), nullptr };
2394 flatbuffers::IDLOptions opt;
2395 opt.indent_step = -1;
2396 flatbuffers::Parser parser(opt);
2397 ok = parser.Parse(schemafile.c_str(), include_directories) &&
2398 parser.Parse(jsonfile.c_str(), include_directories);
2399 TEST_EQ(ok, true);
2400 // Test root table
2401 const Monster *monster = GetMonster(parser.builder_.GetBufferPointer());
2402 const auto abilities = monster->testarrayofsortedstruct();
2403 TEST_EQ(abilities->size(), 3);
2404 TEST_EQ(abilities->Get(0)->id(), 0);
2405 TEST_EQ(abilities->Get(0)->distance(), 45);
2406 TEST_EQ(abilities->Get(1)->id(), 1);
2407 TEST_EQ(abilities->Get(1)->distance(), 21);
2408 TEST_EQ(abilities->Get(2)->id(), 5);
2409 TEST_EQ(abilities->Get(2)->distance(), 12);
2410
2411 std::string jsongen;
2412 auto result = GenerateTextFromTable(parser, monster, "MyGame.Example.Monster",
2413 &jsongen);
2414 TEST_EQ(result, true);
2415 // Test sub table
2416 const Vec3 *pos = monster->pos();
2417 jsongen.clear();
2418 result = GenerateTextFromTable(parser, pos, "MyGame.Example.Vec3", &jsongen);
2419 TEST_EQ(result, true);
2420 TEST_EQ_STR(
2421 jsongen.c_str(),
2422 "{x: 1.0,y: 2.0,z: 3.0,test1: 3.0,test2: \"Green\",test3: {a: 5,b: 6}}");
2423 const Test &test3 = pos->test3();
2424 jsongen.clear();
2425 result =
2426 GenerateTextFromTable(parser, &test3, "MyGame.Example.Test", &jsongen);
2427 TEST_EQ(result, true);
2428 TEST_EQ_STR(jsongen.c_str(), "{a: 5,b: 6}");
2429 const Test *test4 = monster->test4()->Get(0);
2430 jsongen.clear();
2431 result =
2432 GenerateTextFromTable(parser, test4, "MyGame.Example.Test", &jsongen);
2433 TEST_EQ(result, true);
2434 TEST_EQ_STR(jsongen.c_str(), "{a: 10,b: 20}");
2435 }
2436
2437 template<typename T>
NumericUtilsTestInteger(const char * lower,const char * upper)2438 void NumericUtilsTestInteger(const char *lower, const char *upper) {
2439 T x;
2440 TEST_EQ(flatbuffers::StringToNumber("1q", &x), false);
2441 TEST_EQ(x, 0);
2442 TEST_EQ(flatbuffers::StringToNumber(upper, &x), false);
2443 TEST_EQ(x, flatbuffers::numeric_limits<T>::max());
2444 TEST_EQ(flatbuffers::StringToNumber(lower, &x), false);
2445 auto expval = flatbuffers::is_unsigned<T>::value
2446 ? flatbuffers::numeric_limits<T>::max()
2447 : flatbuffers::numeric_limits<T>::lowest();
2448 TEST_EQ(x, expval);
2449 }
2450
2451 template<typename T>
NumericUtilsTestFloat(const char * lower,const char * upper)2452 void NumericUtilsTestFloat(const char *lower, const char *upper) {
2453 T f;
2454 TEST_EQ(flatbuffers::StringToNumber("", &f), false);
2455 TEST_EQ(flatbuffers::StringToNumber("1q", &f), false);
2456 TEST_EQ(f, 0);
2457 TEST_EQ(flatbuffers::StringToNumber(upper, &f), true);
2458 TEST_EQ(f, +flatbuffers::numeric_limits<T>::infinity());
2459 TEST_EQ(flatbuffers::StringToNumber(lower, &f), true);
2460 TEST_EQ(f, -flatbuffers::numeric_limits<T>::infinity());
2461 }
2462
NumericUtilsTest()2463 void NumericUtilsTest() {
2464 NumericUtilsTestInteger<uint64_t>("-1", "18446744073709551616");
2465 NumericUtilsTestInteger<uint8_t>("-1", "256");
2466 NumericUtilsTestInteger<int64_t>("-9223372036854775809",
2467 "9223372036854775808");
2468 NumericUtilsTestInteger<int8_t>("-129", "128");
2469 NumericUtilsTestFloat<float>("-3.4029e+38", "+3.4029e+38");
2470 NumericUtilsTestFloat<float>("-1.7977e+308", "+1.7977e+308");
2471 }
2472
IsAsciiUtilsTest()2473 void IsAsciiUtilsTest() {
2474 char c = -128;
2475 for (int cnt = 0; cnt < 256; cnt++) {
2476 auto alpha = (('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z'));
2477 auto dec = (('0' <= c) && (c <= '9'));
2478 auto hex = (('a' <= c) && (c <= 'f')) || (('A' <= c) && (c <= 'F'));
2479 TEST_EQ(flatbuffers::is_alpha(c), alpha);
2480 TEST_EQ(flatbuffers::is_alnum(c), alpha || dec);
2481 TEST_EQ(flatbuffers::is_digit(c), dec);
2482 TEST_EQ(flatbuffers::is_xdigit(c), dec || hex);
2483 c += 1;
2484 }
2485 }
2486
UnicodeTest()2487 void UnicodeTest() {
2488 flatbuffers::Parser parser;
2489 // Without setting allow_non_utf8 = true, we treat \x sequences as byte
2490 // sequences which are then validated as UTF-8.
2491 TEST_EQ(parser.Parse("table T { F:string; }"
2492 "root_type T;"
2493 "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2494 "\\u5225\\u30B5\\u30A4\\u30C8\\xE2\\x82\\xAC\\u0080\\uD8"
2495 "3D\\uDE0E\" }"),
2496 true);
2497 std::string jsongen;
2498 parser.opts.indent_step = -1;
2499 auto result =
2500 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2501 TEST_EQ(result, true);
2502 TEST_EQ_STR(jsongen.c_str(),
2503 "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2504 "\\u5225\\u30B5\\u30A4\\u30C8\\u20AC\\u0080\\uD83D\\uDE0E\"}");
2505 }
2506
UnicodeTestAllowNonUTF8()2507 void UnicodeTestAllowNonUTF8() {
2508 flatbuffers::Parser parser;
2509 parser.opts.allow_non_utf8 = true;
2510 TEST_EQ(
2511 parser.Parse(
2512 "table T { F:string; }"
2513 "root_type T;"
2514 "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2515 "\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\\u0080\\uD83D\\uDE0E\" }"),
2516 true);
2517 std::string jsongen;
2518 parser.opts.indent_step = -1;
2519 auto result =
2520 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2521 TEST_EQ(result, true);
2522 TEST_EQ_STR(
2523 jsongen.c_str(),
2524 "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2525 "\\u5225\\u30B5\\u30A4\\u30C8\\u0001\\x80\\u0080\\uD83D\\uDE0E\"}");
2526 }
2527
UnicodeTestGenerateTextFailsOnNonUTF8()2528 void UnicodeTestGenerateTextFailsOnNonUTF8() {
2529 flatbuffers::Parser parser;
2530 // Allow non-UTF-8 initially to model what happens when we load a binary
2531 // flatbuffer from disk which contains non-UTF-8 strings.
2532 parser.opts.allow_non_utf8 = true;
2533 TEST_EQ(
2534 parser.Parse(
2535 "table T { F:string; }"
2536 "root_type T;"
2537 "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2538 "\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\\u0080\\uD83D\\uDE0E\" }"),
2539 true);
2540 std::string jsongen;
2541 parser.opts.indent_step = -1;
2542 // Now, disallow non-UTF-8 (the default behavior) so GenerateText indicates
2543 // failure.
2544 parser.opts.allow_non_utf8 = false;
2545 auto result =
2546 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2547 TEST_EQ(result, false);
2548 }
2549
UnicodeSurrogatesTest()2550 void UnicodeSurrogatesTest() {
2551 flatbuffers::Parser parser;
2552
2553 TEST_EQ(parser.Parse("table T { F:string (id: 0); }"
2554 "root_type T;"
2555 "{ F:\"\\uD83D\\uDCA9\"}"),
2556 true);
2557 auto root = flatbuffers::GetRoot<flatbuffers::Table>(
2558 parser.builder_.GetBufferPointer());
2559 auto string = root->GetPointer<flatbuffers::String *>(
2560 flatbuffers::FieldIndexToOffset(0));
2561 TEST_EQ_STR(string->c_str(), "\xF0\x9F\x92\xA9");
2562 }
2563
UnicodeInvalidSurrogatesTest()2564 void UnicodeInvalidSurrogatesTest() {
2565 TestError(
2566 "table T { F:string; }"
2567 "root_type T;"
2568 "{ F:\"\\uD800\"}",
2569 "unpaired high surrogate");
2570 TestError(
2571 "table T { F:string; }"
2572 "root_type T;"
2573 "{ F:\"\\uD800abcd\"}",
2574 "unpaired high surrogate");
2575 TestError(
2576 "table T { F:string; }"
2577 "root_type T;"
2578 "{ F:\"\\uD800\\n\"}",
2579 "unpaired high surrogate");
2580 TestError(
2581 "table T { F:string; }"
2582 "root_type T;"
2583 "{ F:\"\\uD800\\uD800\"}",
2584 "multiple high surrogates");
2585 TestError(
2586 "table T { F:string; }"
2587 "root_type T;"
2588 "{ F:\"\\uDC00\"}",
2589 "unpaired low surrogate");
2590 }
2591
InvalidUTF8Test()2592 void InvalidUTF8Test() {
2593 // "1 byte" pattern, under min length of 2 bytes
2594 TestError(
2595 "table T { F:string; }"
2596 "root_type T;"
2597 "{ F:\"\x80\"}",
2598 "illegal UTF-8 sequence");
2599 // 2 byte pattern, string too short
2600 TestError(
2601 "table T { F:string; }"
2602 "root_type T;"
2603 "{ F:\"\xDF\"}",
2604 "illegal UTF-8 sequence");
2605 // 3 byte pattern, string too short
2606 TestError(
2607 "table T { F:string; }"
2608 "root_type T;"
2609 "{ F:\"\xEF\xBF\"}",
2610 "illegal UTF-8 sequence");
2611 // 4 byte pattern, string too short
2612 TestError(
2613 "table T { F:string; }"
2614 "root_type T;"
2615 "{ F:\"\xF7\xBF\xBF\"}",
2616 "illegal UTF-8 sequence");
2617 // "5 byte" pattern, string too short
2618 TestError(
2619 "table T { F:string; }"
2620 "root_type T;"
2621 "{ F:\"\xFB\xBF\xBF\xBF\"}",
2622 "illegal UTF-8 sequence");
2623 // "6 byte" pattern, string too short
2624 TestError(
2625 "table T { F:string; }"
2626 "root_type T;"
2627 "{ F:\"\xFD\xBF\xBF\xBF\xBF\"}",
2628 "illegal UTF-8 sequence");
2629 // "7 byte" pattern, string too short
2630 TestError(
2631 "table T { F:string; }"
2632 "root_type T;"
2633 "{ F:\"\xFE\xBF\xBF\xBF\xBF\xBF\"}",
2634 "illegal UTF-8 sequence");
2635 // "5 byte" pattern, over max length of 4 bytes
2636 TestError(
2637 "table T { F:string; }"
2638 "root_type T;"
2639 "{ F:\"\xFB\xBF\xBF\xBF\xBF\"}",
2640 "illegal UTF-8 sequence");
2641 // "6 byte" pattern, over max length of 4 bytes
2642 TestError(
2643 "table T { F:string; }"
2644 "root_type T;"
2645 "{ F:\"\xFD\xBF\xBF\xBF\xBF\xBF\"}",
2646 "illegal UTF-8 sequence");
2647 // "7 byte" pattern, over max length of 4 bytes
2648 TestError(
2649 "table T { F:string; }"
2650 "root_type T;"
2651 "{ F:\"\xFE\xBF\xBF\xBF\xBF\xBF\xBF\"}",
2652 "illegal UTF-8 sequence");
2653
2654 // Three invalid encodings for U+000A (\n, aka NEWLINE)
2655 TestError(
2656 "table T { F:string; }"
2657 "root_type T;"
2658 "{ F:\"\xC0\x8A\"}",
2659 "illegal UTF-8 sequence");
2660 TestError(
2661 "table T { F:string; }"
2662 "root_type T;"
2663 "{ F:\"\xE0\x80\x8A\"}",
2664 "illegal UTF-8 sequence");
2665 TestError(
2666 "table T { F:string; }"
2667 "root_type T;"
2668 "{ F:\"\xF0\x80\x80\x8A\"}",
2669 "illegal UTF-8 sequence");
2670
2671 // Two invalid encodings for U+00A9 (COPYRIGHT SYMBOL)
2672 TestError(
2673 "table T { F:string; }"
2674 "root_type T;"
2675 "{ F:\"\xE0\x81\xA9\"}",
2676 "illegal UTF-8 sequence");
2677 TestError(
2678 "table T { F:string; }"
2679 "root_type T;"
2680 "{ F:\"\xF0\x80\x81\xA9\"}",
2681 "illegal UTF-8 sequence");
2682
2683 // Invalid encoding for U+20AC (EURO SYMBOL)
2684 TestError(
2685 "table T { F:string; }"
2686 "root_type T;"
2687 "{ F:\"\xF0\x82\x82\xAC\"}",
2688 "illegal UTF-8 sequence");
2689
2690 // UTF-16 surrogate values between U+D800 and U+DFFF cannot be encoded in
2691 // UTF-8
2692 TestError(
2693 "table T { F:string; }"
2694 "root_type T;"
2695 // U+10400 "encoded" as U+D801 U+DC00
2696 "{ F:\"\xED\xA0\x81\xED\xB0\x80\"}",
2697 "illegal UTF-8 sequence");
2698
2699 // Check independence of identifier from locale.
2700 std::string locale_ident;
2701 locale_ident += "table T { F";
2702 locale_ident += static_cast<char>(-32); // unsigned 0xE0
2703 locale_ident += " :string; }";
2704 locale_ident += "root_type T;";
2705 locale_ident += "{}";
2706 TestError(locale_ident.c_str(), "");
2707 }
2708
UnknownFieldsTest()2709 void UnknownFieldsTest() {
2710 flatbuffers::IDLOptions opts;
2711 opts.skip_unexpected_fields_in_json = true;
2712 flatbuffers::Parser parser(opts);
2713
2714 TEST_EQ(parser.Parse("table T { str:string; i:int;}"
2715 "root_type T;"
2716 "{ str:\"test\","
2717 "unknown_string:\"test\","
2718 "\"unknown_string\":\"test\","
2719 "unknown_int:10,"
2720 "unknown_float:1.0,"
2721 "unknown_array: [ 1, 2, 3, 4],"
2722 "unknown_object: { i: 10 },"
2723 "\"unknown_object\": { \"i\": 10 },"
2724 "i:10}"),
2725 true);
2726
2727 std::string jsongen;
2728 parser.opts.indent_step = -1;
2729 auto result =
2730 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2731 TEST_EQ(result, true);
2732 TEST_EQ_STR(jsongen.c_str(), "{str: \"test\",i: 10}");
2733 }
2734
ParseUnionTest()2735 void ParseUnionTest() {
2736 // Unions must be parseable with the type field following the object.
2737 flatbuffers::Parser parser;
2738 TEST_EQ(parser.Parse("table T { A:int; }"
2739 "union U { T }"
2740 "table V { X:U; }"
2741 "root_type V;"
2742 "{ X:{ A:1 }, X_type: T }"),
2743 true);
2744 // Unions must be parsable with prefixed namespace.
2745 flatbuffers::Parser parser2;
2746 TEST_EQ(parser2.Parse("namespace N; table A {} namespace; union U { N.A }"
2747 "table B { e:U; } root_type B;"
2748 "{ e_type: N_A, e: {} }"),
2749 true);
2750 }
2751
ValidSameNameDifferentNamespaceTest()2752 void ValidSameNameDifferentNamespaceTest() {
2753 // Duplicate table names in different namespaces must be parsable
2754 TEST_ASSERT(flatbuffers::Parser().Parse(
2755 "namespace A; table X {} namespace B; table X {}"));
2756 // Duplicate union names in different namespaces must be parsable
2757 TEST_ASSERT(flatbuffers::Parser().Parse(
2758 "namespace A; union X {} namespace B; union X {}"));
2759 // Clashing table and union names in different namespaces must be parsable
2760 TEST_ASSERT(flatbuffers::Parser().Parse(
2761 "namespace A; table X {} namespace B; union X {}"));
2762 TEST_ASSERT(flatbuffers::Parser().Parse(
2763 "namespace A; union X {} namespace B; table X {}"));
2764 }
2765
MultiFileNameClashTest()2766 void MultiFileNameClashTest() {
2767 const auto name_clash_path =
2768 flatbuffers::ConCatPathFileName(test_data_path, "name_clash_test");
2769 const char *include_directories[] = { name_clash_path.c_str() };
2770
2771 // Load valid 2 file Flatbuffer schema
2772 const auto valid_path =
2773 flatbuffers::ConCatPathFileName(name_clash_path, "valid_test1.fbs");
2774 std::string valid_schema;
2775 TEST_ASSERT(flatbuffers::LoadFile(valid_path.c_str(), false, &valid_schema));
2776 // Clashing table and union names in different namespaces must be parsable
2777 TEST_ASSERT(
2778 flatbuffers::Parser().Parse(valid_schema.c_str(), include_directories));
2779
2780 flatbuffers::Parser p;
2781 TEST_ASSERT(p.Parse(valid_schema.c_str(), include_directories));
2782
2783 // Load invalid 2 file Flatbuffer schema
2784 const auto invalid_path =
2785 flatbuffers::ConCatPathFileName(name_clash_path, "invalid_test1.fbs");
2786 std::string invalid_schema;
2787 TEST_ASSERT(
2788 flatbuffers::LoadFile(invalid_path.c_str(), false, &invalid_schema));
2789 // Clashing table and union names in same namespace must fail to parse
2790 TEST_EQ(
2791 flatbuffers::Parser().Parse(invalid_schema.c_str(), include_directories),
2792 false);
2793 }
2794
InvalidNestedFlatbufferTest()2795 void InvalidNestedFlatbufferTest() {
2796 // First, load and parse FlatBuffer schema (.fbs)
2797 std::string schemafile;
2798 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
2799 false, &schemafile),
2800 true);
2801 auto include_test_path =
2802 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
2803 const char *include_directories[] = { test_data_path.c_str(),
2804 include_test_path.c_str(), nullptr };
2805 flatbuffers::Parser parser1;
2806 TEST_EQ(parser1.Parse(schemafile.c_str(), include_directories), true);
2807
2808 // "color" inside nested flatbuffer contains invalid enum value
2809 TEST_EQ(parser1.Parse("{ name: \"Bender\", testnestedflatbuffer: { name: "
2810 "\"Leela\", color: \"nonexistent\"}}"),
2811 false);
2812 }
2813
EvolutionTest()2814 void EvolutionTest() {
2815 // VS10 does not support typed enums, exclude from tests
2816 #if !defined(_MSC_VER) || _MSC_VER >= 1700
2817 const int NUM_VERSIONS = 2;
2818 std::string schemas[NUM_VERSIONS];
2819 std::string jsonfiles[NUM_VERSIONS];
2820 std::vector<uint8_t> binaries[NUM_VERSIONS];
2821
2822 flatbuffers::IDLOptions idl_opts;
2823 idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
2824 flatbuffers::Parser parser(idl_opts);
2825
2826 // Load all the schema versions and their associated data.
2827 for (int i = 0; i < NUM_VERSIONS; ++i) {
2828 std::string schema = test_data_path + "evolution_test/evolution_v" +
2829 flatbuffers::NumToString(i + 1) + ".fbs";
2830 TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i]));
2831 std::string json = test_data_path + "evolution_test/evolution_v" +
2832 flatbuffers::NumToString(i + 1) + ".json";
2833 TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i]));
2834
2835 TEST_ASSERT(parser.Parse(schemas[i].c_str()));
2836 TEST_ASSERT(parser.Parse(jsonfiles[i].c_str()));
2837
2838 auto bufLen = parser.builder_.GetSize();
2839 auto buf = parser.builder_.GetBufferPointer();
2840 binaries[i].reserve(bufLen);
2841 std::copy(buf, buf + bufLen, std::back_inserter(binaries[i]));
2842 }
2843
2844 // Assert that all the verifiers for the different schema versions properly
2845 // verify any version data.
2846 for (int i = 0; i < NUM_VERSIONS; ++i) {
2847 flatbuffers::Verifier verifier(&binaries[i].front(), binaries[i].size());
2848 TEST_ASSERT(Evolution::V1::VerifyRootBuffer(verifier));
2849 TEST_ASSERT(Evolution::V2::VerifyRootBuffer(verifier));
2850 }
2851
2852 // Test backwards compatibility by reading old data with an evolved schema.
2853 auto root_v1_viewed_from_v2 = Evolution::V2::GetRoot(&binaries[0].front());
2854 // field 'k' is new in version 2, so it should be null.
2855 TEST_ASSERT(nullptr == root_v1_viewed_from_v2->k());
2856 // field 'l' is new in version 2 with a default of 56.
2857 TEST_EQ(root_v1_viewed_from_v2->l(), 56);
2858 // field 'c' of 'TableA' is new in version 2, so it should be null.
2859 TEST_ASSERT(nullptr == root_v1_viewed_from_v2->e()->c());
2860 // 'TableC' was added to field 'c' union in version 2, so it should be null.
2861 TEST_ASSERT(nullptr == root_v1_viewed_from_v2->c_as_TableC());
2862 // The field 'c' union should be of type 'TableB' regardless of schema version
2863 TEST_ASSERT(root_v1_viewed_from_v2->c_type() == Evolution::V2::Union::TableB);
2864 // The field 'f' was renamed to 'ff' in version 2, it should still be
2865 // readable.
2866 TEST_EQ(root_v1_viewed_from_v2->ff()->a(), 16);
2867
2868 // Test forwards compatibility by reading new data with an old schema.
2869 auto root_v2_viewed_from_v1 = Evolution::V1::GetRoot(&binaries[1].front());
2870 // The field 'c' union in version 2 is a new table (index = 3) and should
2871 // still be accessible, but not interpretable.
2872 TEST_EQ(static_cast<uint8_t>(root_v2_viewed_from_v1->c_type()), 3);
2873 TEST_NOTNULL(root_v2_viewed_from_v1->c());
2874 // The field 'd' enum in verison 2 has new members and should still be
2875 // accessible, but not interpretable.
2876 TEST_EQ(static_cast<int8_t>(root_v2_viewed_from_v1->d()), 3);
2877 // The field 'a' in version 2 is deprecated and should return the default
2878 // value (0) instead of the value stored in the in the buffer (42).
2879 TEST_EQ(root_v2_viewed_from_v1->a(), 0);
2880 // The field 'ff' was originally named 'f' in version 1, it should still be
2881 // readable.
2882 TEST_EQ(root_v2_viewed_from_v1->f()->a(), 35);
2883 #endif
2884 }
2885
UnionDeprecationTest()2886 void UnionDeprecationTest() {
2887 const int NUM_VERSIONS = 2;
2888 std::string schemas[NUM_VERSIONS];
2889 std::string jsonfiles[NUM_VERSIONS];
2890 std::vector<uint8_t> binaries[NUM_VERSIONS];
2891
2892 flatbuffers::IDLOptions idl_opts;
2893 idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
2894 flatbuffers::Parser parser(idl_opts);
2895
2896 // Load all the schema versions and their associated data.
2897 for (int i = 0; i < NUM_VERSIONS; ++i) {
2898 std::string schema = test_data_path + "evolution_test/evolution_v" +
2899 flatbuffers::NumToString(i + 1) + ".fbs";
2900 TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i]));
2901 std::string json = test_data_path + "evolution_test/evolution_v" +
2902 flatbuffers::NumToString(i + 1) + ".json";
2903 TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i]));
2904
2905 TEST_ASSERT(parser.Parse(schemas[i].c_str()));
2906 TEST_ASSERT(parser.Parse(jsonfiles[i].c_str()));
2907
2908 auto bufLen = parser.builder_.GetSize();
2909 auto buf = parser.builder_.GetBufferPointer();
2910 binaries[i].reserve(bufLen);
2911 std::copy(buf, buf + bufLen, std::back_inserter(binaries[i]));
2912 }
2913
2914 auto v2 = parser.LookupStruct("Evolution.V2.Root");
2915 TEST_NOTNULL(v2);
2916 auto j_type_field = v2->fields.Lookup("j_type");
2917 TEST_NOTNULL(j_type_field);
2918 TEST_ASSERT(j_type_field->deprecated);
2919 }
2920
UnionVectorTest()2921 void UnionVectorTest() {
2922 // load FlatBuffer fbs schema and json.
2923 std::string schemafile, jsonfile;
2924 TEST_EQ(flatbuffers::LoadFile(
2925 (test_data_path + "union_vector/union_vector.fbs").c_str(), false,
2926 &schemafile),
2927 true);
2928 TEST_EQ(flatbuffers::LoadFile(
2929 (test_data_path + "union_vector/union_vector.json").c_str(),
2930 false, &jsonfile),
2931 true);
2932
2933 // parse schema.
2934 flatbuffers::IDLOptions idl_opts;
2935 idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
2936 flatbuffers::Parser parser(idl_opts);
2937 TEST_EQ(parser.Parse(schemafile.c_str()), true);
2938
2939 flatbuffers::FlatBufferBuilder fbb;
2940
2941 // union types.
2942 std::vector<uint8_t> types;
2943 types.push_back(static_cast<uint8_t>(Character_Belle));
2944 types.push_back(static_cast<uint8_t>(Character_MuLan));
2945 types.push_back(static_cast<uint8_t>(Character_BookFan));
2946 types.push_back(static_cast<uint8_t>(Character_Other));
2947 types.push_back(static_cast<uint8_t>(Character_Unused));
2948
2949 // union values.
2950 std::vector<flatbuffers::Offset<void>> characters;
2951 characters.push_back(fbb.CreateStruct(BookReader(/*books_read=*/7)).Union());
2952 characters.push_back(CreateAttacker(fbb, /*sword_attack_damage=*/5).Union());
2953 characters.push_back(fbb.CreateStruct(BookReader(/*books_read=*/2)).Union());
2954 characters.push_back(fbb.CreateString("Other").Union());
2955 characters.push_back(fbb.CreateString("Unused").Union());
2956
2957 // create Movie.
2958 const auto movie_offset =
2959 CreateMovie(fbb, Character_Rapunzel,
2960 fbb.CreateStruct(Rapunzel(/*hair_length=*/6)).Union(),
2961 fbb.CreateVector(types), fbb.CreateVector(characters));
2962 FinishMovieBuffer(fbb, movie_offset);
2963
2964 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
2965 TEST_EQ(VerifyMovieBuffer(verifier), true);
2966
2967 auto flat_movie = GetMovie(fbb.GetBufferPointer());
2968
2969 auto TestMovie = [](const Movie *movie) {
2970 TEST_EQ(movie->main_character_type() == Character_Rapunzel, true);
2971
2972 auto cts = movie->characters_type();
2973 TEST_EQ(movie->characters_type()->size(), 5);
2974 TEST_EQ(cts->GetEnum<Character>(0) == Character_Belle, true);
2975 TEST_EQ(cts->GetEnum<Character>(1) == Character_MuLan, true);
2976 TEST_EQ(cts->GetEnum<Character>(2) == Character_BookFan, true);
2977 TEST_EQ(cts->GetEnum<Character>(3) == Character_Other, true);
2978 TEST_EQ(cts->GetEnum<Character>(4) == Character_Unused, true);
2979
2980 auto rapunzel = movie->main_character_as_Rapunzel();
2981 TEST_NOTNULL(rapunzel);
2982 TEST_EQ(rapunzel->hair_length(), 6);
2983
2984 auto cs = movie->characters();
2985 TEST_EQ(cs->size(), 5);
2986 auto belle = cs->GetAs<BookReader>(0);
2987 TEST_EQ(belle->books_read(), 7);
2988 auto mu_lan = cs->GetAs<Attacker>(1);
2989 TEST_EQ(mu_lan->sword_attack_damage(), 5);
2990 auto book_fan = cs->GetAs<BookReader>(2);
2991 TEST_EQ(book_fan->books_read(), 2);
2992 auto other = cs->GetAsString(3);
2993 TEST_EQ_STR(other->c_str(), "Other");
2994 auto unused = cs->GetAsString(4);
2995 TEST_EQ_STR(unused->c_str(), "Unused");
2996 };
2997
2998 TestMovie(flat_movie);
2999
3000 // Also test the JSON we loaded above.
3001 TEST_EQ(parser.Parse(jsonfile.c_str()), true);
3002 auto jbuf = parser.builder_.GetBufferPointer();
3003 flatbuffers::Verifier jverifier(jbuf, parser.builder_.GetSize());
3004 TEST_EQ(VerifyMovieBuffer(jverifier), true);
3005 TestMovie(GetMovie(jbuf));
3006
3007 auto movie_object = flat_movie->UnPack();
3008 TEST_EQ(movie_object->main_character.AsRapunzel()->hair_length(), 6);
3009 TEST_EQ(movie_object->characters[0].AsBelle()->books_read(), 7);
3010 TEST_EQ(movie_object->characters[1].AsMuLan()->sword_attack_damage, 5);
3011 TEST_EQ(movie_object->characters[2].AsBookFan()->books_read(), 2);
3012 TEST_EQ_STR(movie_object->characters[3].AsOther()->c_str(), "Other");
3013 TEST_EQ_STR(movie_object->characters[4].AsUnused()->c_str(), "Unused");
3014
3015 fbb.Clear();
3016 fbb.Finish(Movie::Pack(fbb, movie_object));
3017
3018 delete movie_object;
3019
3020 auto repacked_movie = GetMovie(fbb.GetBufferPointer());
3021
3022 TestMovie(repacked_movie);
3023
3024 // Generate text using mini-reflection.
3025 auto s =
3026 flatbuffers::FlatBufferToString(fbb.GetBufferPointer(), MovieTypeTable());
3027 TEST_EQ_STR(
3028 s.c_str(),
3029 "{ main_character_type: Rapunzel, main_character: { hair_length: 6 }, "
3030 "characters_type: [ Belle, MuLan, BookFan, Other, Unused ], "
3031 "characters: [ { books_read: 7 }, { sword_attack_damage: 5 }, "
3032 "{ books_read: 2 }, \"Other\", \"Unused\" ] }");
3033
3034 flatbuffers::ToStringVisitor visitor("\n", true, " ");
3035 IterateFlatBuffer(fbb.GetBufferPointer(), MovieTypeTable(), &visitor);
3036 TEST_EQ_STR(visitor.s.c_str(),
3037 "{\n"
3038 " \"main_character_type\": \"Rapunzel\",\n"
3039 " \"main_character\": {\n"
3040 " \"hair_length\": 6\n"
3041 " },\n"
3042 " \"characters_type\": [\n"
3043 " \"Belle\",\n"
3044 " \"MuLan\",\n"
3045 " \"BookFan\",\n"
3046 " \"Other\",\n"
3047 " \"Unused\"\n"
3048 " ],\n"
3049 " \"characters\": [\n"
3050 " {\n"
3051 " \"books_read\": 7\n"
3052 " },\n"
3053 " {\n"
3054 " \"sword_attack_damage\": 5\n"
3055 " },\n"
3056 " {\n"
3057 " \"books_read\": 2\n"
3058 " },\n"
3059 " \"Other\",\n"
3060 " \"Unused\"\n"
3061 " ]\n"
3062 "}");
3063
3064 // Generate text using parsed schema.
3065 std::string jsongen;
3066 auto result = GenerateText(parser, fbb.GetBufferPointer(), &jsongen);
3067 TEST_EQ(result, true);
3068 TEST_EQ_STR(jsongen.c_str(),
3069 "{\n"
3070 " main_character_type: \"Rapunzel\",\n"
3071 " main_character: {\n"
3072 " hair_length: 6\n"
3073 " },\n"
3074 " characters_type: [\n"
3075 " \"Belle\",\n"
3076 " \"MuLan\",\n"
3077 " \"BookFan\",\n"
3078 " \"Other\",\n"
3079 " \"Unused\"\n"
3080 " ],\n"
3081 " characters: [\n"
3082 " {\n"
3083 " books_read: 7\n"
3084 " },\n"
3085 " {\n"
3086 " sword_attack_damage: 5\n"
3087 " },\n"
3088 " {\n"
3089 " books_read: 2\n"
3090 " },\n"
3091 " \"Other\",\n"
3092 " \"Unused\"\n"
3093 " ]\n"
3094 "}\n");
3095
3096 // Simple test with reflection.
3097 parser.Serialize();
3098 auto schema = reflection::GetSchema(parser.builder_.GetBufferPointer());
3099 auto ok = flatbuffers::Verify(*schema, *schema->root_table(),
3100 fbb.GetBufferPointer(), fbb.GetSize());
3101 TEST_EQ(ok, true);
3102
3103 flatbuffers::Parser parser2(idl_opts);
3104 TEST_EQ(parser2.Parse("struct Bool { b:bool; }"
3105 "union Any { Bool }"
3106 "table Root { a:Any; }"
3107 "root_type Root;"),
3108 true);
3109 TEST_EQ(parser2.Parse("{a_type:Bool,a:{b:true}}"), true);
3110 }
3111
StructUnionTest()3112 void StructUnionTest() {
3113 GadgetUnion gadget;
3114 gadget.Set(FallingTub(100));
3115
3116 HandFanT fan;
3117 fan.length = 10;
3118 gadget.Set(fan);
3119 }
3120
WarningsAsErrorsTest()3121 void WarningsAsErrorsTest() {
3122 {
3123 flatbuffers::IDLOptions opts;
3124 // opts.warnings_as_errors should default to false
3125 flatbuffers::Parser parser(opts);
3126 TEST_EQ(parser.Parse("table T { THIS_NAME_CAUSES_A_WARNING:string;}\n"
3127 "root_type T;"),
3128 true);
3129 }
3130 {
3131 flatbuffers::IDLOptions opts;
3132 opts.warnings_as_errors = true;
3133 flatbuffers::Parser parser(opts);
3134 TEST_EQ(parser.Parse("table T { THIS_NAME_CAUSES_A_WARNING:string;}\n"
3135 "root_type T;"),
3136 false);
3137 }
3138 }
3139
ConformTest()3140 void ConformTest() {
3141 flatbuffers::Parser parser;
3142 TEST_EQ(parser.Parse("table T { A:int; } enum E:byte { A }"), true);
3143
3144 auto test_conform = [](flatbuffers::Parser &parser1, const char *test,
3145 const char *expected_err) {
3146 flatbuffers::Parser parser2;
3147 TEST_EQ(parser2.Parse(test), true);
3148 auto err = parser2.ConformTo(parser1);
3149 TEST_NOTNULL(strstr(err.c_str(), expected_err));
3150 };
3151
3152 test_conform(parser, "table T { A:byte; }", "types differ for field");
3153 test_conform(parser, "table T { B:int; A:int; }", "offsets differ for field");
3154 test_conform(parser, "table T { A:int = 1; }", "defaults differ for field");
3155 test_conform(parser, "table T { B:float; }",
3156 "field renamed to different type");
3157 test_conform(parser, "enum E:byte { B, A }", "values differ for enum");
3158 }
3159
ParseProtoBufAsciiTest()3160 void ParseProtoBufAsciiTest() {
3161 // We can put the parser in a mode where it will accept JSON that looks more
3162 // like Protobuf ASCII, for users that have data in that format.
3163 // This uses no "" for field names (which we already support by default,
3164 // omits `,`, `:` before `{` and a couple of other features.
3165 flatbuffers::Parser parser;
3166 parser.opts.protobuf_ascii_alike = true;
3167 TEST_EQ(
3168 parser.Parse("table S { B:int; } table T { A:[int]; C:S; } root_type T;"),
3169 true);
3170 TEST_EQ(parser.Parse("{ A [1 2] C { B:2 }}"), true);
3171 // Similarly, in text output, it should omit these.
3172 std::string text;
3173 auto ok = flatbuffers::GenerateText(
3174 parser, parser.builder_.GetBufferPointer(), &text);
3175 TEST_EQ(ok, true);
3176 TEST_EQ_STR(text.c_str(),
3177 "{\n A [\n 1\n 2\n ]\n C {\n B: 2\n }\n}\n");
3178 }
3179
FlexBuffersTest()3180 void FlexBuffersTest() {
3181 flexbuffers::Builder slb(512,
3182 flexbuffers::BUILDER_FLAG_SHARE_KEYS_AND_STRINGS);
3183
3184 // Write the equivalent of:
3185 // { vec: [ -100, "Fred", 4.0, false ], bar: [ 1, 2, 3 ], bar3: [ 1, 2, 3 ],
3186 // foo: 100, bool: true, mymap: { foo: "Fred" } }
3187
3188 // It's possible to do this without std::function support as well.
3189 slb.Map([&]() {
3190 slb.Vector("vec", [&]() {
3191 slb += -100; // Equivalent to slb.Add(-100) or slb.Int(-100);
3192 slb += "Fred";
3193 slb.IndirectFloat(4.0f);
3194 auto i_f = slb.LastValue();
3195 uint8_t blob[] = { 77 };
3196 slb.Blob(blob, 1);
3197 slb += false;
3198 slb.ReuseValue(i_f);
3199 });
3200 int ints[] = { 1, 2, 3 };
3201 slb.Vector("bar", ints, 3);
3202 slb.FixedTypedVector("bar3", ints, 3);
3203 bool bools[] = { true, false, true, false };
3204 slb.Vector("bools", bools, 4);
3205 slb.Bool("bool", true);
3206 slb.Double("foo", 100);
3207 slb.Map("mymap", [&]() {
3208 slb.String("foo", "Fred"); // Testing key and string reuse.
3209 });
3210 });
3211 slb.Finish();
3212
3213 // clang-format off
3214 #ifdef FLATBUFFERS_TEST_VERBOSE
3215 for (size_t i = 0; i < slb.GetBuffer().size(); i++)
3216 printf("%d ", slb.GetBuffer().data()[i]);
3217 printf("\n");
3218 #endif
3219 // clang-format on
3220
3221 std::vector<uint8_t> reuse_tracker;
3222 TEST_EQ(flexbuffers::VerifyBuffer(slb.GetBuffer().data(),
3223 slb.GetBuffer().size(), &reuse_tracker),
3224 true);
3225
3226 auto map = flexbuffers::GetRoot(slb.GetBuffer()).AsMap();
3227 TEST_EQ(map.size(), 7);
3228 auto vec = map["vec"].AsVector();
3229 TEST_EQ(vec.size(), 6);
3230 TEST_EQ(vec[0].AsInt64(), -100);
3231 TEST_EQ_STR(vec[1].AsString().c_str(), "Fred");
3232 TEST_EQ(vec[1].AsInt64(), 0); // Number parsing failed.
3233 TEST_EQ(vec[2].AsDouble(), 4.0);
3234 TEST_EQ(vec[2].AsString().IsTheEmptyString(), true); // Wrong Type.
3235 TEST_EQ_STR(vec[2].AsString().c_str(), ""); // This still works though.
3236 TEST_EQ_STR(vec[2].ToString().c_str(), "4.0"); // Or have it converted.
3237 // Few tests for templated version of As.
3238 TEST_EQ(vec[0].As<int64_t>(), -100);
3239 TEST_EQ_STR(vec[1].As<std::string>().c_str(), "Fred");
3240 TEST_EQ(vec[1].As<int64_t>(), 0); // Number parsing failed.
3241 TEST_EQ(vec[2].As<double>(), 4.0);
3242 // Test that the blob can be accessed.
3243 TEST_EQ(vec[3].IsBlob(), true);
3244 auto blob = vec[3].AsBlob();
3245 TEST_EQ(blob.size(), 1);
3246 TEST_EQ(blob.data()[0], 77);
3247 TEST_EQ(vec[4].IsBool(), true); // Check if type is a bool
3248 TEST_EQ(vec[4].AsBool(), false); // Check if value is false
3249 TEST_EQ(vec[5].AsDouble(), 4.0); // This is shared with vec[2] !
3250 auto tvec = map["bar"].AsTypedVector();
3251 TEST_EQ(tvec.size(), 3);
3252 TEST_EQ(tvec[2].AsInt8(), 3);
3253 auto tvec3 = map["bar3"].AsFixedTypedVector();
3254 TEST_EQ(tvec3.size(), 3);
3255 TEST_EQ(tvec3[2].AsInt8(), 3);
3256 TEST_EQ(map["bool"].AsBool(), true);
3257 auto tvecb = map["bools"].AsTypedVector();
3258 TEST_EQ(tvecb.ElementType(), flexbuffers::FBT_BOOL);
3259 TEST_EQ(map["foo"].AsUInt8(), 100);
3260 TEST_EQ(map["unknown"].IsNull(), true);
3261 auto mymap = map["mymap"].AsMap();
3262 // These should be equal by pointer equality, since key and value are shared.
3263 TEST_EQ(mymap.Keys()[0].AsKey(), map.Keys()[4].AsKey());
3264 TEST_EQ(mymap.Values()[0].AsString().c_str(), vec[1].AsString().c_str());
3265 // We can mutate values in the buffer.
3266 TEST_EQ(vec[0].MutateInt(-99), true);
3267 TEST_EQ(vec[0].AsInt64(), -99);
3268 TEST_EQ(vec[1].MutateString("John"), true); // Size must match.
3269 TEST_EQ_STR(vec[1].AsString().c_str(), "John");
3270 TEST_EQ(vec[1].MutateString("Alfred"), false); // Too long.
3271 TEST_EQ(vec[2].MutateFloat(2.0f), true);
3272 TEST_EQ(vec[2].AsFloat(), 2.0f);
3273 TEST_EQ(vec[2].MutateFloat(3.14159), false); // Double does not fit in float.
3274 TEST_EQ(vec[4].AsBool(), false); // Is false before change
3275 TEST_EQ(vec[4].MutateBool(true), true); // Can change a bool
3276 TEST_EQ(vec[4].AsBool(), true); // Changed bool is now true
3277
3278 // Parse from JSON:
3279 flatbuffers::Parser parser;
3280 slb.Clear();
3281 auto jsontest = "{ a: [ 123, 456.0 ], b: \"hello\", c: true, d: false }";
3282 TEST_EQ(parser.ParseFlexBuffer(jsontest, nullptr, &slb), true);
3283 TEST_EQ(flexbuffers::VerifyBuffer(slb.GetBuffer().data(),
3284 slb.GetBuffer().size(), &reuse_tracker),
3285 true);
3286 auto jroot = flexbuffers::GetRoot(slb.GetBuffer());
3287 auto jmap = jroot.AsMap();
3288 auto jvec = jmap["a"].AsVector();
3289 TEST_EQ(jvec[0].AsInt64(), 123);
3290 TEST_EQ(jvec[1].AsDouble(), 456.0);
3291 TEST_EQ_STR(jmap["b"].AsString().c_str(), "hello");
3292 TEST_EQ(jmap["c"].IsBool(), true); // Parsed correctly to a bool
3293 TEST_EQ(jmap["c"].AsBool(), true); // Parsed correctly to true
3294 TEST_EQ(jmap["d"].IsBool(), true); // Parsed correctly to a bool
3295 TEST_EQ(jmap["d"].AsBool(), false); // Parsed correctly to false
3296 // And from FlexBuffer back to JSON:
3297 auto jsonback = jroot.ToString();
3298 TEST_EQ_STR(jsontest, jsonback.c_str());
3299
3300 slb.Clear();
3301 slb.Vector([&]() {
3302 for (int i = 0; i < 130; ++i) slb.Add(static_cast<uint8_t>(255));
3303 slb.Vector([&]() {
3304 for (int i = 0; i < 130; ++i) slb.Add(static_cast<uint8_t>(255));
3305 slb.Vector([] {});
3306 });
3307 });
3308 slb.Finish();
3309 TEST_EQ(slb.GetSize(), 664);
3310 }
3311
FlexBuffersReuseBugTest()3312 void FlexBuffersReuseBugTest() {
3313 flexbuffers::Builder slb;
3314 slb.Map([&]() {
3315 slb.Vector("vec", [&]() {});
3316 slb.Bool("bool", true);
3317 });
3318 slb.Finish();
3319 std::vector<uint8_t> reuse_tracker;
3320 // This would fail before, since the reuse_tracker would use the address of
3321 // the vector reference to check for reuse, but in this case we have an empty
3322 // vector, and since the size field is before the pointer, its address is the
3323 // same as thing after it, the key "bool".
3324 // We fix this by using the address of the size field for tracking reuse.
3325 TEST_EQ(flexbuffers::VerifyBuffer(slb.GetBuffer().data(),
3326 slb.GetBuffer().size(), &reuse_tracker),
3327 true);
3328 }
3329
FlexBuffersFloatingPointTest()3330 void FlexBuffersFloatingPointTest() {
3331 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
3332 flexbuffers::Builder slb(512,
3333 flexbuffers::BUILDER_FLAG_SHARE_KEYS_AND_STRINGS);
3334 // Parse floating-point values from JSON:
3335 flatbuffers::Parser parser;
3336 slb.Clear();
3337 auto jsontest =
3338 "{ a: [1.0, nan, inf, infinity, -inf, +inf, -infinity, 8.0] }";
3339 TEST_EQ(parser.ParseFlexBuffer(jsontest, nullptr, &slb), true);
3340 auto jroot = flexbuffers::GetRoot(slb.GetBuffer());
3341 TEST_EQ(flexbuffers::VerifyBuffer(slb.GetBuffer().data(),
3342 slb.GetBuffer().size(), nullptr),
3343 true);
3344 auto jmap = jroot.AsMap();
3345 auto jvec = jmap["a"].AsVector();
3346 TEST_EQ(8, jvec.size());
3347 TEST_EQ(1.0, jvec[0].AsDouble());
3348 TEST_ASSERT(is_quiet_nan(jvec[1].AsDouble()));
3349 TEST_EQ(infinity_d, jvec[2].AsDouble());
3350 TEST_EQ(infinity_d, jvec[3].AsDouble());
3351 TEST_EQ(-infinity_d, jvec[4].AsDouble());
3352 TEST_EQ(+infinity_d, jvec[5].AsDouble());
3353 TEST_EQ(-infinity_d, jvec[6].AsDouble());
3354 TEST_EQ(8.0, jvec[7].AsDouble());
3355 #endif
3356 }
3357
FlexBuffersDeprecatedTest()3358 void FlexBuffersDeprecatedTest() {
3359 // FlexBuffers as originally designed had a flaw involving the
3360 // FBT_VECTOR_STRING datatype, and this test documents/tests the fix for it.
3361 // Discussion: https://github.com/google/flatbuffers/issues/5627
3362 flexbuffers::Builder slb;
3363 // FBT_VECTOR_* are "typed vectors" where all elements are of the same type.
3364 // Problem is, when storing FBT_STRING elements, it relies on that type to
3365 // get the bit-width for the size field of the string, which in this case
3366 // isn't present, and instead defaults to 8-bit. This means that any strings
3367 // stored inside such a vector, when accessed thru the old API that returns
3368 // a String reference, will appear to be truncated if the string stored is
3369 // actually >=256 bytes.
3370 std::string test_data(300, 'A');
3371 auto start = slb.StartVector();
3372 // This one will have a 16-bit size field.
3373 slb.String(test_data);
3374 // This one will have an 8-bit size field.
3375 slb.String("hello");
3376 // We're asking this to be serialized as a typed vector (true), but not
3377 // fixed size (false). The type will be FBT_VECTOR_STRING with a bit-width
3378 // of whatever the offsets in the vector need, the bit-widths of the strings
3379 // are not stored(!) <- the actual design flaw.
3380 // Note that even in the fixed code, we continue to serialize the elements of
3381 // FBT_VECTOR_STRING as FBT_STRING, since there may be old code out there
3382 // reading new data that we want to continue to function.
3383 // Thus, FBT_VECTOR_STRING, while deprecated, will always be represented the
3384 // same way, the fix lies on the reading side.
3385 slb.EndVector(start, true, false);
3386 slb.Finish();
3387 // Verify because why not.
3388 TEST_EQ(flexbuffers::VerifyBuffer(slb.GetBuffer().data(),
3389 slb.GetBuffer().size(), nullptr),
3390 true);
3391 // So now lets read this data back.
3392 // For existing data, since we have no way of knowing what the actual
3393 // bit-width of the size field of the string is, we are going to ignore this
3394 // field, and instead treat these strings as FBT_KEY (null-terminated), so we
3395 // can deal with strings of arbitrary length. This of course truncates strings
3396 // with embedded nulls, but we think that that is preferrable over truncating
3397 // strings >= 256 bytes.
3398 auto vec = flexbuffers::GetRoot(slb.GetBuffer()).AsTypedVector();
3399 // Even though this was serialized as FBT_VECTOR_STRING, it is read as
3400 // FBT_VECTOR_KEY:
3401 TEST_EQ(vec.ElementType(), flexbuffers::FBT_KEY);
3402 // Access the long string. Previously, this would return a string of size 1,
3403 // since it would read the high-byte of the 16-bit length.
3404 // This should now correctly test the full 300 bytes, using AsKey():
3405 TEST_EQ_STR(vec[0].AsKey(), test_data.c_str());
3406 // Old code that called AsString will continue to work, as the String
3407 // accessor objects now use a cached size that can come from a key as well.
3408 TEST_EQ_STR(vec[0].AsString().c_str(), test_data.c_str());
3409 // Short strings work as before:
3410 TEST_EQ_STR(vec[1].AsKey(), "hello");
3411 TEST_EQ_STR(vec[1].AsString().c_str(), "hello");
3412 // So, while existing code and data mostly "just work" with the fixes applied
3413 // to AsTypedVector and AsString, what do you do going forward?
3414 // Code accessing existing data doesn't necessarily need to change, though
3415 // you could consider using AsKey instead of AsString for a) documenting
3416 // that you are accessing keys, or b) a speedup if you don't actually use
3417 // the string size.
3418 // For new data, or data that doesn't need to be backwards compatible,
3419 // instead serialize as FBT_VECTOR (call EndVector with typed = false, then
3420 // read elements with AsString), or, for maximum compactness, use
3421 // FBT_VECTOR_KEY (call slb.Key above instead, read with AsKey or AsString).
3422 }
3423
TypeAliasesTest()3424 void TypeAliasesTest() {
3425 flatbuffers::FlatBufferBuilder builder;
3426
3427 builder.Finish(CreateTypeAliases(
3428 builder, flatbuffers::numeric_limits<int8_t>::min(),
3429 flatbuffers::numeric_limits<uint8_t>::max(),
3430 flatbuffers::numeric_limits<int16_t>::min(),
3431 flatbuffers::numeric_limits<uint16_t>::max(),
3432 flatbuffers::numeric_limits<int32_t>::min(),
3433 flatbuffers::numeric_limits<uint32_t>::max(),
3434 flatbuffers::numeric_limits<int64_t>::min(),
3435 flatbuffers::numeric_limits<uint64_t>::max(), 2.3f, 2.3));
3436
3437 auto p = builder.GetBufferPointer();
3438 auto ta = flatbuffers::GetRoot<TypeAliases>(p);
3439
3440 TEST_EQ(ta->i8(), flatbuffers::numeric_limits<int8_t>::min());
3441 TEST_EQ(ta->u8(), flatbuffers::numeric_limits<uint8_t>::max());
3442 TEST_EQ(ta->i16(), flatbuffers::numeric_limits<int16_t>::min());
3443 TEST_EQ(ta->u16(), flatbuffers::numeric_limits<uint16_t>::max());
3444 TEST_EQ(ta->i32(), flatbuffers::numeric_limits<int32_t>::min());
3445 TEST_EQ(ta->u32(), flatbuffers::numeric_limits<uint32_t>::max());
3446 TEST_EQ(ta->i64(), flatbuffers::numeric_limits<int64_t>::min());
3447 TEST_EQ(ta->u64(), flatbuffers::numeric_limits<uint64_t>::max());
3448 TEST_EQ(ta->f32(), 2.3f);
3449 TEST_EQ(ta->f64(), 2.3);
3450 using namespace flatbuffers; // is_same
3451 static_assert(is_same<decltype(ta->i8()), int8_t>::value, "invalid type");
3452 static_assert(is_same<decltype(ta->i16()), int16_t>::value, "invalid type");
3453 static_assert(is_same<decltype(ta->i32()), int32_t>::value, "invalid type");
3454 static_assert(is_same<decltype(ta->i64()), int64_t>::value, "invalid type");
3455 static_assert(is_same<decltype(ta->u8()), uint8_t>::value, "invalid type");
3456 static_assert(is_same<decltype(ta->u16()), uint16_t>::value, "invalid type");
3457 static_assert(is_same<decltype(ta->u32()), uint32_t>::value, "invalid type");
3458 static_assert(is_same<decltype(ta->u64()), uint64_t>::value, "invalid type");
3459 static_assert(is_same<decltype(ta->f32()), float>::value, "invalid type");
3460 static_assert(is_same<decltype(ta->f64()), double>::value, "invalid type");
3461 }
3462
EndianSwapTest()3463 void EndianSwapTest() {
3464 TEST_EQ(flatbuffers::EndianSwap(static_cast<int16_t>(0x1234)), 0x3412);
3465 TEST_EQ(flatbuffers::EndianSwap(static_cast<int32_t>(0x12345678)),
3466 0x78563412);
3467 TEST_EQ(flatbuffers::EndianSwap(static_cast<int64_t>(0x1234567890ABCDEF)),
3468 0xEFCDAB9078563412);
3469 TEST_EQ(flatbuffers::EndianSwap(flatbuffers::EndianSwap(3.14f)), 3.14f);
3470 }
3471
UninitializedVectorTest()3472 void UninitializedVectorTest() {
3473 flatbuffers::FlatBufferBuilder builder;
3474
3475 Test *buf = nullptr;
3476 auto vector_offset =
3477 builder.CreateUninitializedVectorOfStructs<Test>(2, &buf);
3478 TEST_NOTNULL(buf);
3479 buf[0] = Test(10, 20);
3480 buf[1] = Test(30, 40);
3481
3482 auto required_name = builder.CreateString("myMonster");
3483 auto monster_builder = MonsterBuilder(builder);
3484 monster_builder.add_name(
3485 required_name); // required field mandated for monster.
3486 monster_builder.add_test4(vector_offset);
3487 builder.Finish(monster_builder.Finish());
3488
3489 auto p = builder.GetBufferPointer();
3490 auto uvt = flatbuffers::GetRoot<Monster>(p);
3491 TEST_NOTNULL(uvt);
3492 auto vec = uvt->test4();
3493 TEST_NOTNULL(vec);
3494 auto test_0 = vec->Get(0);
3495 auto test_1 = vec->Get(1);
3496 TEST_EQ(test_0->a(), 10);
3497 TEST_EQ(test_0->b(), 20);
3498 TEST_EQ(test_1->a(), 30);
3499 TEST_EQ(test_1->b(), 40);
3500 }
3501
EqualOperatorTest()3502 void EqualOperatorTest() {
3503 MonsterT a;
3504 MonsterT b;
3505 TEST_EQ(b == a, true);
3506 TEST_EQ(b != a, false);
3507
3508 b.mana = 33;
3509 TEST_EQ(b == a, false);
3510 TEST_EQ(b != a, true);
3511 b.mana = 150;
3512 TEST_EQ(b == a, true);
3513 TEST_EQ(b != a, false);
3514
3515 b.inventory.push_back(3);
3516 TEST_EQ(b == a, false);
3517 TEST_EQ(b != a, true);
3518 b.inventory.clear();
3519 TEST_EQ(b == a, true);
3520 TEST_EQ(b != a, false);
3521
3522 a.enemy.reset(new MonsterT());
3523 TEST_EQ(b != a, true);
3524 a.enemy->mana = 33;
3525 TEST_EQ(b == a, false);
3526 TEST_EQ(b != a, true);
3527
3528 b.enemy.reset(new MonsterT());
3529 TEST_EQ(b == a, false);
3530 TEST_EQ(b != a, true);
3531 b.enemy->mana = 33;
3532 TEST_EQ(b == a, true);
3533 TEST_EQ(b != a, false);
3534
3535 a.enemy.reset(nullptr);
3536 TEST_EQ(b == a, false);
3537 TEST_EQ(b != a, true);
3538 b.enemy->mana = 150;
3539 TEST_EQ(b == a, false);
3540 TEST_EQ(b != a, true);
3541 a.enemy.reset(new MonsterT());
3542 TEST_EQ(b == a, true);
3543 TEST_EQ(b != a, false);
3544
3545 b.enemy.reset(nullptr);
3546
3547 b.test.type = Any_Monster;
3548 TEST_EQ(b == a, false);
3549 TEST_EQ(b != a, true);
3550
3551 // Test that vector of tables are compared by value and not by reference.
3552 {
3553 // Two tables are equal by default.
3554 MonsterT a, b;
3555 TEST_EQ(a == b, true);
3556
3557 // Adding only a table to one of the monster vectors should make it not
3558 // equal (due to size mistmatch).
3559 a.testarrayoftables.push_back(
3560 flatbuffers::unique_ptr<MonsterT>(new MonsterT));
3561 TEST_EQ(a == b, false);
3562
3563 // Adding an equalivant table to the other monster vector should make it
3564 // equal again.
3565 b.testarrayoftables.push_back(
3566 flatbuffers::unique_ptr<MonsterT>(new MonsterT));
3567 TEST_EQ(a == b, true);
3568
3569 // Create two new monsters that are different.
3570 auto c = flatbuffers::unique_ptr<MonsterT>(new MonsterT);
3571 auto d = flatbuffers::unique_ptr<MonsterT>(new MonsterT);
3572 c->hp = 1;
3573 d->hp = 2;
3574 TEST_EQ(c == d, false);
3575
3576 // Adding them to the original monsters should also make them different.
3577 a.testarrayoftables.push_back(std::move(c));
3578 b.testarrayoftables.push_back(std::move(d));
3579 TEST_EQ(a == b, false);
3580
3581 // Remove the mismatching monsters to get back to equality
3582 a.testarrayoftables.pop_back();
3583 b.testarrayoftables.pop_back();
3584 TEST_EQ(a == b, true);
3585
3586 // Check that nullptr are OK.
3587 a.testarrayoftables.push_back(nullptr);
3588 b.testarrayoftables.push_back(
3589 flatbuffers::unique_ptr<MonsterT>(new MonsterT));
3590 TEST_EQ(a == b, false);
3591 }
3592 }
3593
3594 // For testing any binaries, e.g. from fuzzing.
LoadVerifyBinaryTest()3595 void LoadVerifyBinaryTest() {
3596 std::string binary;
3597 if (flatbuffers::LoadFile(
3598 (test_data_path + "fuzzer/your-filename-here").c_str(), true,
3599 &binary)) {
3600 flatbuffers::Verifier verifier(
3601 reinterpret_cast<const uint8_t *>(binary.data()), binary.size());
3602 TEST_EQ(VerifyMonsterBuffer(verifier), true);
3603 }
3604 }
3605
CreateSharedStringTest()3606 void CreateSharedStringTest() {
3607 flatbuffers::FlatBufferBuilder builder;
3608 const auto one1 = builder.CreateSharedString("one");
3609 const auto two = builder.CreateSharedString("two");
3610 const auto one2 = builder.CreateSharedString("one");
3611 TEST_EQ(one1.o, one2.o);
3612 const auto onetwo = builder.CreateSharedString("onetwo");
3613 TEST_EQ(onetwo.o != one1.o, true);
3614 TEST_EQ(onetwo.o != two.o, true);
3615
3616 // Support for embedded nulls
3617 const char chars_b[] = { 'a', '\0', 'b' };
3618 const char chars_c[] = { 'a', '\0', 'c' };
3619 const auto null_b1 = builder.CreateSharedString(chars_b, sizeof(chars_b));
3620 const auto null_c = builder.CreateSharedString(chars_c, sizeof(chars_c));
3621 const auto null_b2 = builder.CreateSharedString(chars_b, sizeof(chars_b));
3622 TEST_EQ(null_b1.o != null_c.o, true); // Issue#5058 repro
3623 TEST_EQ(null_b1.o, null_b2.o);
3624
3625 // Put the strings into an array for round trip verification.
3626 std::array<flatbuffers::Offset<flatbuffers::String>, 7> array = {
3627 one1, two, one2, onetwo, null_b1, null_c, null_b2
3628 };
3629 const auto vector_offset =
3630 builder.CreateVector<flatbuffers::Offset<flatbuffers::String>>(array);
3631 MonsterBuilder monster_builder(builder);
3632 monster_builder.add_name(two);
3633 monster_builder.add_testarrayofstring(vector_offset);
3634 builder.Finish(monster_builder.Finish());
3635
3636 // Read the Monster back.
3637 const auto *monster =
3638 flatbuffers::GetRoot<Monster>(builder.GetBufferPointer());
3639 TEST_EQ_STR(monster->name()->c_str(), "two");
3640 const auto *testarrayofstring = monster->testarrayofstring();
3641 TEST_EQ(testarrayofstring->size(), flatbuffers::uoffset_t(7));
3642 const auto &a = *testarrayofstring;
3643 TEST_EQ_STR(a[0]->c_str(), "one");
3644 TEST_EQ_STR(a[1]->c_str(), "two");
3645 TEST_EQ_STR(a[2]->c_str(), "one");
3646 TEST_EQ_STR(a[3]->c_str(), "onetwo");
3647 TEST_EQ(a[4]->str(), (std::string(chars_b, sizeof(chars_b))));
3648 TEST_EQ(a[5]->str(), (std::string(chars_c, sizeof(chars_c))));
3649 TEST_EQ(a[6]->str(), (std::string(chars_b, sizeof(chars_b))));
3650
3651 // Make sure String::operator< works, too, since it is related to
3652 // StringOffsetCompare.
3653 TEST_EQ((*a[0]) < (*a[1]), true);
3654 TEST_EQ((*a[1]) < (*a[0]), false);
3655 TEST_EQ((*a[1]) < (*a[2]), false);
3656 TEST_EQ((*a[2]) < (*a[1]), true);
3657 TEST_EQ((*a[4]) < (*a[3]), true);
3658 TEST_EQ((*a[5]) < (*a[4]), false);
3659 TEST_EQ((*a[5]) < (*a[4]), false);
3660 TEST_EQ((*a[6]) < (*a[5]), true);
3661 }
3662
3663 #if !defined(FLATBUFFERS_USE_STD_SPAN) && !defined(FLATBUFFERS_SPAN_MINIMAL)
FlatbuffersSpanTest()3664 void FlatbuffersSpanTest() {
3665 // Compile-time checking of non-const [] to const [] conversions.
3666 using flatbuffers::internal::is_span_convertable;
3667 (void)is_span_convertable<int, 1, int, 1>::type(123);
3668 (void)is_span_convertable<const int, 1, int, 1>::type(123);
3669 (void)is_span_convertable<const int64_t, 1, int64_t, 1>::type(123);
3670 (void)is_span_convertable<const uint64_t, 1, uint64_t, 1>::type(123);
3671 (void)is_span_convertable<const int, 1, const int, 1>::type(123);
3672 (void)is_span_convertable<const int64_t, 1, const int64_t, 1>::type(123);
3673 (void)is_span_convertable<const uint64_t, 1, const uint64_t, 1>::type(123);
3674
3675 using flatbuffers::span;
3676 span<char, 0> c1;
3677 TEST_EQ(c1.size(), 0);
3678 span<char, flatbuffers::dynamic_extent> c2;
3679 TEST_EQ(c2.size(), 0);
3680 span<char> c3;
3681 TEST_EQ(c3.size(), 0);
3682 TEST_ASSERT(c1.empty() && c2.empty() && c3.empty());
3683
3684 int i_data7[7] = { 0, 1, 2, 3, 4, 5, 6 };
3685 span<int, 7> i1(&i_data7[0], 7);
3686 span<int> i2(i1); // make dynamic from static
3687 TEST_EQ(i1.size(), 7);
3688 TEST_EQ(i1.empty(), false);
3689 TEST_EQ(i1.size(), i2.size());
3690 TEST_EQ(i1.data(), i_data7);
3691 TEST_EQ(i1[2], 2);
3692 // Make const span from a non-const one.
3693 span<const int, 7> i3(i1);
3694 // Construct from a C-array.
3695 span<int, 7> i4(i_data7);
3696 span<const int, 7> i5(i_data7);
3697 span<int> i6(i_data7);
3698 span<const int> i7(i_data7);
3699 TEST_EQ(i7.size(), 7);
3700 // Check construction from a const array.
3701 const int i_cdata5[5] = { 4, 3, 2, 1, 0 };
3702 span<const int, 5> i8(i_cdata5);
3703 span<const int> i9(i_cdata5);
3704 TEST_EQ(i9.size(), 5);
3705 // Construction from a (ptr, size) pair.
3706 span<int, 7> i10(i_data7, 7);
3707 span<int> i11(i_data7, 7);
3708 TEST_EQ(i11.size(), 7);
3709 span<const int, 5> i12(i_cdata5, 5);
3710 span<const int> i13(i_cdata5, 5);
3711 TEST_EQ(i13.size(), 5);
3712 // Construction from std::array.
3713 std::array<int, 6> i_arr6 = { { 0, 1, 2, 3, 4, 5 } };
3714 span<int, 6> i14(i_arr6);
3715 span<const int, 6> i15(i_arr6);
3716 span<int> i16(i_arr6);
3717 span<const int> i17(i_arr6);
3718 TEST_EQ(i17.size(), 6);
3719 const std::array<int, 8> i_carr8 = { { 0, 1, 2, 3, 4, 5, 6, 7 } };
3720 span<const int, 8> i18(i_carr8);
3721 span<const int> i19(i_carr8);
3722 TEST_EQ(i18.size(), 8);
3723 TEST_EQ(i19.size(), 8);
3724 TEST_EQ(i19[7], 7);
3725 // Check compatibility with flatbuffers::Array.
3726 int fbs_int3_underlaying[3] = { 0 };
3727 int fbs_int3_data[3] = { 1, 2, 3 };
3728 auto &fbs_int3 = flatbuffers::CastToArray(fbs_int3_underlaying);
3729 fbs_int3.CopyFromSpan(fbs_int3_data);
3730 TEST_EQ(fbs_int3.Get(1), 2);
3731 const int fbs_cint3_data[3] = { 2, 3, 4 };
3732 fbs_int3.CopyFromSpan(fbs_cint3_data);
3733 TEST_EQ(fbs_int3.Get(1), 3);
3734 // Check with Array<Enum, N>
3735 enum class Dummy : uint16_t { Zero = 0, One, Two };
3736 Dummy fbs_dummy3_underlaying[3] = {};
3737 Dummy fbs_dummy3_data[3] = { Dummy::One, Dummy::Two, Dummy::Two };
3738 auto &fbs_dummy3 = flatbuffers::CastToArray(fbs_dummy3_underlaying);
3739 fbs_dummy3.CopyFromSpan(fbs_dummy3_data);
3740 TEST_EQ(fbs_dummy3.Get(1), Dummy::Two);
3741 }
3742 #else
FlatbuffersSpanTest()3743 void FlatbuffersSpanTest() {}
3744 #endif
3745
3746 // VS10 does not support typed enums, exclude from tests
3747 #if !defined(_MSC_VER) || _MSC_VER >= 1700
FixedLengthArrayTest()3748 void FixedLengthArrayTest() {
3749 // Generate an ArrayTable containing one ArrayStruct.
3750 flatbuffers::FlatBufferBuilder fbb;
3751 MyGame::Example::NestedStruct nStruct0(MyGame::Example::TestEnum::B);
3752 TEST_NOTNULL(nStruct0.mutable_a());
3753 nStruct0.mutable_a()->Mutate(0, 1);
3754 nStruct0.mutable_a()->Mutate(1, 2);
3755 TEST_NOTNULL(nStruct0.mutable_c());
3756 nStruct0.mutable_c()->Mutate(0, MyGame::Example::TestEnum::C);
3757 nStruct0.mutable_c()->Mutate(1, MyGame::Example::TestEnum::A);
3758 TEST_NOTNULL(nStruct0.mutable_d());
3759 nStruct0.mutable_d()->Mutate(0, flatbuffers::numeric_limits<int64_t>::max());
3760 nStruct0.mutable_d()->Mutate(1, flatbuffers::numeric_limits<int64_t>::min());
3761 MyGame::Example::NestedStruct nStruct1(MyGame::Example::TestEnum::C);
3762 TEST_NOTNULL(nStruct1.mutable_a());
3763 nStruct1.mutable_a()->Mutate(0, 3);
3764 nStruct1.mutable_a()->Mutate(1, 4);
3765 TEST_NOTNULL(nStruct1.mutable_c());
3766 nStruct1.mutable_c()->Mutate(0, MyGame::Example::TestEnum::C);
3767 nStruct1.mutable_c()->Mutate(1, MyGame::Example::TestEnum::A);
3768 TEST_NOTNULL(nStruct1.mutable_d());
3769 nStruct1.mutable_d()->Mutate(0, flatbuffers::numeric_limits<int64_t>::min());
3770 nStruct1.mutable_d()->Mutate(1, flatbuffers::numeric_limits<int64_t>::max());
3771 MyGame::Example::ArrayStruct aStruct(2, 12, 1);
3772 TEST_NOTNULL(aStruct.b());
3773 TEST_NOTNULL(aStruct.mutable_b());
3774 TEST_NOTNULL(aStruct.mutable_d());
3775 TEST_NOTNULL(aStruct.mutable_f());
3776 for (int i = 0; i < aStruct.b()->size(); i++)
3777 aStruct.mutable_b()->Mutate(i, i + 1);
3778 aStruct.mutable_d()->Mutate(0, nStruct0);
3779 aStruct.mutable_d()->Mutate(1, nStruct1);
3780 auto aTable = MyGame::Example::CreateArrayTable(fbb, &aStruct);
3781 MyGame::Example::FinishArrayTableBuffer(fbb, aTable);
3782 // Verify correctness of the ArrayTable.
3783 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
3784 TEST_ASSERT(MyGame::Example::VerifyArrayTableBuffer(verifier));
3785 // Do test.
3786 auto p = MyGame::Example::GetMutableArrayTable(fbb.GetBufferPointer());
3787 auto mArStruct = p->mutable_a();
3788 TEST_NOTNULL(mArStruct);
3789 TEST_NOTNULL(mArStruct->b());
3790 TEST_NOTNULL(mArStruct->d());
3791 TEST_NOTNULL(mArStruct->f());
3792 TEST_NOTNULL(mArStruct->mutable_b());
3793 TEST_NOTNULL(mArStruct->mutable_d());
3794 TEST_NOTNULL(mArStruct->mutable_f());
3795 TEST_EQ(mArStruct->a(), 2);
3796 TEST_EQ(mArStruct->b()->size(), 15);
3797 mArStruct->mutable_b()->Mutate(14, -14);
3798 TEST_EQ(mArStruct->b()->Get(14), -14);
3799 TEST_EQ(mArStruct->c(), 12);
3800 TEST_NOTNULL(mArStruct->d()->Get(0));
3801 TEST_NOTNULL(mArStruct->d()->Get(0)->a());
3802 TEST_EQ(mArStruct->d()->Get(0)->a()->Get(0), 1);
3803 TEST_EQ(mArStruct->d()->Get(0)->a()->Get(1), 2);
3804 TEST_NOTNULL(mArStruct->d()->Get(1));
3805 TEST_NOTNULL(mArStruct->d()->Get(1)->a());
3806 TEST_EQ(mArStruct->d()->Get(1)->a()->Get(0), 3);
3807 TEST_EQ(mArStruct->d()->Get(1)->a()->Get(1), 4);
3808 TEST_NOTNULL(mArStruct->mutable_d()->GetMutablePointer(1));
3809 TEST_NOTNULL(mArStruct->mutable_d()->GetMutablePointer(1)->mutable_a());
3810 mArStruct->mutable_d()->GetMutablePointer(1)->mutable_a()->Mutate(1, 5);
3811 TEST_EQ(5, mArStruct->d()->Get(1)->a()->Get(1));
3812 TEST_EQ(MyGame::Example::TestEnum::B, mArStruct->d()->Get(0)->b());
3813 TEST_NOTNULL(mArStruct->d()->Get(0)->c());
3814 TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(0)->c()->Get(0));
3815 TEST_EQ(MyGame::Example::TestEnum::A, mArStruct->d()->Get(0)->c()->Get(1));
3816 TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(),
3817 mArStruct->d()->Get(0)->d()->Get(0));
3818 TEST_EQ(flatbuffers::numeric_limits<int64_t>::min(),
3819 mArStruct->d()->Get(0)->d()->Get(1));
3820 TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(1)->b());
3821 TEST_NOTNULL(mArStruct->d()->Get(1)->c());
3822 TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(1)->c()->Get(0));
3823 TEST_EQ(MyGame::Example::TestEnum::A, mArStruct->d()->Get(1)->c()->Get(1));
3824 TEST_EQ(flatbuffers::numeric_limits<int64_t>::min(),
3825 mArStruct->d()->Get(1)->d()->Get(0));
3826 TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(),
3827 mArStruct->d()->Get(1)->d()->Get(1));
3828 for (int i = 0; i < mArStruct->b()->size() - 1; i++)
3829 TEST_EQ(mArStruct->b()->Get(i), i + 1);
3830 // Check alignment
3831 TEST_EQ(0, reinterpret_cast<uintptr_t>(mArStruct->d()) % 8);
3832 TEST_EQ(0, reinterpret_cast<uintptr_t>(mArStruct->f()) % 8);
3833
3834 // Check if default constructor set all memory zero
3835 const size_t arr_size = sizeof(MyGame::Example::ArrayStruct);
3836 char non_zero_memory[arr_size];
3837 // set memory chunk of size ArrayStruct to 1's
3838 std::memset(static_cast<void *>(non_zero_memory), 1, arr_size);
3839 // after placement-new it should be all 0's
3840 # if defined(_MSC_VER) && defined(_DEBUG)
3841 # undef new
3842 # endif
3843 MyGame::Example::ArrayStruct *ap =
3844 new (non_zero_memory) MyGame::Example::ArrayStruct;
3845 # if defined(_MSC_VER) && defined(_DEBUG)
3846 # define new DEBUG_NEW
3847 # endif
3848 (void)ap;
3849 for (size_t i = 0; i < arr_size; ++i) { TEST_EQ(non_zero_memory[i], 0); }
3850 }
3851 #else
FixedLengthArrayTest()3852 void FixedLengthArrayTest() {}
3853 #endif // !defined(_MSC_VER) || _MSC_VER >= 1700
3854
3855 #if !defined(FLATBUFFERS_SPAN_MINIMAL) && \
3856 (!defined(_MSC_VER) || _MSC_VER >= 1700)
FixedLengthArrayConstructorTest()3857 void FixedLengthArrayConstructorTest() {
3858 const int32_t nested_a[2] = { 1, 2 };
3859 MyGame::Example::TestEnum nested_c[2] = { MyGame::Example::TestEnum::A,
3860 MyGame::Example::TestEnum::B };
3861 const int64_t int64_2[2] = { -2, -1 };
3862
3863 std::array<MyGame::Example::NestedStruct, 2> init_d = {
3864 { MyGame::Example::NestedStruct(nested_a, MyGame::Example::TestEnum::B,
3865 nested_c, int64_2),
3866 MyGame::Example::NestedStruct(nested_a, MyGame::Example::TestEnum::A,
3867 nested_c,
3868 std::array<int64_t, 2>{ { 12, 13 } }) }
3869 };
3870
3871 MyGame::Example::ArrayStruct arr_struct(
3872 8.125,
3873 std::array<int32_t, 0xF>{
3874 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } },
3875 -17, init_d, 10, int64_2);
3876 TEST_EQ(arr_struct.a(), 8.125);
3877 TEST_EQ(arr_struct.b()->Get(2), 3);
3878 TEST_EQ(arr_struct.c(), -17);
3879
3880 TEST_NOTNULL(arr_struct.d());
3881 const auto &arr_d_0 = *arr_struct.d()->Get(0);
3882 TEST_EQ(arr_d_0.a()->Get(0), 1);
3883 TEST_EQ(arr_d_0.a()->Get(1), 2);
3884 TEST_EQ(arr_d_0.b(), MyGame::Example::TestEnum::B);
3885 TEST_EQ(arr_d_0.c()->Get(0), MyGame::Example::TestEnum::A);
3886 TEST_EQ(arr_d_0.c()->Get(1), MyGame::Example::TestEnum::B);
3887 TEST_EQ(arr_d_0.d()->Get(0), -2);
3888 TEST_EQ(arr_d_0.d()->Get(1), -1);
3889 const auto &arr_d_1 = *arr_struct.d()->Get(1);
3890 TEST_EQ(arr_d_1.a()->Get(0), 1);
3891 TEST_EQ(arr_d_1.a()->Get(1), 2);
3892 TEST_EQ(arr_d_1.b(), MyGame::Example::TestEnum::A);
3893 TEST_EQ(arr_d_1.c()->Get(0), MyGame::Example::TestEnum::A);
3894 TEST_EQ(arr_d_1.c()->Get(1), MyGame::Example::TestEnum::B);
3895 TEST_EQ(arr_d_1.d()->Get(0), 12);
3896 TEST_EQ(arr_d_1.d()->Get(1), 13);
3897
3898 TEST_EQ(arr_struct.e(), 10);
3899 TEST_EQ(arr_struct.f()->Get(0), -2);
3900 TEST_EQ(arr_struct.f()->Get(1), -1);
3901 }
3902 #else
FixedLengthArrayConstructorTest()3903 void FixedLengthArrayConstructorTest() {}
3904 #endif
3905
NativeTypeTest()3906 void NativeTypeTest() {
3907 const int N = 3;
3908
3909 Geometry::ApplicationDataT src_data;
3910 src_data.vectors.reserve(N);
3911 src_data.vectors_alt.reserve(N);
3912
3913 for (int i = 0; i < N; ++i) {
3914 src_data.vectors.push_back(
3915 Native::Vector3D(10 * i + 0.1f, 10 * i + 0.2f, 10 * i + 0.3f));
3916 src_data.vectors_alt.push_back(
3917 Native::Vector3D(20 * i + 0.1f, 20 * i + 0.2f, 20 * i + 0.3f));
3918 }
3919
3920 flatbuffers::FlatBufferBuilder fbb;
3921 fbb.Finish(Geometry::ApplicationData::Pack(fbb, &src_data));
3922
3923 auto dstDataT = Geometry::UnPackApplicationData(fbb.GetBufferPointer());
3924
3925 for (int i = 0; i < N; ++i) {
3926 const Native::Vector3D &v = dstDataT->vectors[i];
3927 TEST_EQ(v.x, 10 * i + 0.1f);
3928 TEST_EQ(v.y, 10 * i + 0.2f);
3929 TEST_EQ(v.z, 10 * i + 0.3f);
3930
3931 const Native::Vector3D &v2 = dstDataT->vectors_alt[i];
3932 TEST_EQ(v2.x, 20 * i + 0.1f);
3933 TEST_EQ(v2.y, 20 * i + 0.2f);
3934 TEST_EQ(v2.z, 20 * i + 0.3f);
3935 }
3936 }
3937
3938 // VS10 does not support typed enums, exclude from tests
3939 #if !defined(_MSC_VER) || _MSC_VER >= 1700
FixedLengthArrayJsonTest(bool binary)3940 void FixedLengthArrayJsonTest(bool binary) {
3941 // load FlatBuffer schema (.fbs) and JSON from disk
3942 std::string schemafile;
3943 std::string jsonfile;
3944 TEST_EQ(
3945 flatbuffers::LoadFile(
3946 (test_data_path + "arrays_test." + (binary ? "bfbs" : "fbs")).c_str(),
3947 binary, &schemafile),
3948 true);
3949 TEST_EQ(flatbuffers::LoadFile((test_data_path + "arrays_test.golden").c_str(),
3950 false, &jsonfile),
3951 true);
3952
3953 // parse schema first, so we can use it to parse the data after
3954 flatbuffers::Parser parserOrg, parserGen;
3955 if (binary) {
3956 flatbuffers::Verifier verifier(
3957 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
3958 schemafile.size());
3959 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
3960 TEST_EQ(parserOrg.Deserialize(
3961 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
3962 schemafile.size()),
3963 true);
3964 TEST_EQ(parserGen.Deserialize(
3965 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
3966 schemafile.size()),
3967 true);
3968 } else {
3969 TEST_EQ(parserOrg.Parse(schemafile.c_str()), true);
3970 TEST_EQ(parserGen.Parse(schemafile.c_str()), true);
3971 }
3972 TEST_EQ(parserOrg.Parse(jsonfile.c_str()), true);
3973
3974 // First, verify it, just in case:
3975 flatbuffers::Verifier verifierOrg(parserOrg.builder_.GetBufferPointer(),
3976 parserOrg.builder_.GetSize());
3977 TEST_EQ(VerifyArrayTableBuffer(verifierOrg), true);
3978
3979 // Export to JSON
3980 std::string jsonGen;
3981 TEST_EQ(
3982 GenerateText(parserOrg, parserOrg.builder_.GetBufferPointer(), &jsonGen),
3983 true);
3984
3985 // Import from JSON
3986 TEST_EQ(parserGen.Parse(jsonGen.c_str()), true);
3987
3988 // Verify buffer from generated JSON
3989 flatbuffers::Verifier verifierGen(parserGen.builder_.GetBufferPointer(),
3990 parserGen.builder_.GetSize());
3991 TEST_EQ(VerifyArrayTableBuffer(verifierGen), true);
3992
3993 // Compare generated buffer to original
3994 TEST_EQ(parserOrg.builder_.GetSize(), parserGen.builder_.GetSize());
3995 TEST_EQ(std::memcmp(parserOrg.builder_.GetBufferPointer(),
3996 parserGen.builder_.GetBufferPointer(),
3997 parserOrg.builder_.GetSize()),
3998 0);
3999 }
4000
FixedLengthArraySpanTest()4001 void FixedLengthArraySpanTest() {
4002 // load FlatBuffer schema (.fbs) and JSON from disk
4003 std::string schemafile;
4004 std::string jsonfile;
4005 TEST_EQ(flatbuffers::LoadFile((test_data_path + "arrays_test.fbs").c_str(),
4006 false, &schemafile),
4007 true);
4008 TEST_EQ(flatbuffers::LoadFile((test_data_path + "arrays_test.golden").c_str(),
4009 false, &jsonfile),
4010 true);
4011
4012 // parse schema first, so we can use it to parse the data after
4013 flatbuffers::Parser parser;
4014 TEST_EQ(parser.Parse(schemafile.c_str()), true);
4015 TEST_EQ(parser.Parse(jsonfile.c_str()), true);
4016 auto &fbb = parser.builder_;
4017 auto verifier = flatbuffers::Verifier(fbb.GetBufferPointer(), fbb.GetSize());
4018 TEST_EQ(true, VerifyArrayTableBuffer(verifier));
4019
4020 auto p = MyGame::Example::GetMutableArrayTable(fbb.GetBufferPointer());
4021 TEST_NOTNULL(p);
4022 auto table_struct = p->mutable_a();
4023 TEST_NOTNULL(table_struct);
4024 TEST_EQ(2, table_struct->d()->size());
4025 TEST_NOTNULL(table_struct->d());
4026 TEST_NOTNULL(table_struct->mutable_d());
4027 // test array of structs
4028 auto const_d = flatbuffers::make_span(*table_struct->d());
4029 auto mutable_d = flatbuffers::make_span(*table_struct->mutable_d());
4030 TEST_EQ(2, const_d.size());
4031 TEST_EQ(2, mutable_d.size());
4032 TEST_ASSERT(const_d[0] == mutable_d[0]);
4033 TEST_ASSERT(const_d[1] == mutable_d[1]);
4034 mutable_d[0] = const_d[0]; // mutate
4035 // test scalars
4036 auto &const_nested = const_d[0];
4037 auto &mutable_nested = mutable_d[0];
4038 static_assert(sizeof(MyGame::Example::TestEnum) == sizeof(uint8_t),
4039 "TestEnum's underlaying type must by byte");
4040 TEST_NOTNULL(const_nested.d());
4041 TEST_NOTNULL(mutable_nested.d());
4042 {
4043 flatbuffers::span<const MyGame::Example::TestEnum, 2> const_d_c =
4044 flatbuffers::make_span(*const_nested.c());
4045 auto mutable_d_c = flatbuffers::make_span(*mutable_nested.mutable_c());
4046 TEST_EQ(2, const_d_c.size());
4047 TEST_EQ(2, mutable_d_c.size());
4048 TEST_EQ(MyGame::Example::TestEnum::C, const_d_c[0]);
4049 TEST_EQ(MyGame::Example::TestEnum::B, const_d_c[1]);
4050 TEST_ASSERT(mutable_d_c.end() == std::copy(const_d_c.begin(),
4051 const_d_c.end(),
4052 mutable_d_c.begin()));
4053 TEST_ASSERT(
4054 std::equal(const_d_c.begin(), const_d_c.end(), mutable_d_c.begin()));
4055 }
4056 // test little endian array of int32
4057 # if FLATBUFFERS_LITTLEENDIAN
4058 {
4059 flatbuffers::span<const int32_t, 2> const_d_a =
4060 flatbuffers::make_span(*const_nested.a());
4061 auto mutable_d_a = flatbuffers::make_span(*mutable_nested.mutable_a());
4062 TEST_EQ(2, const_d_a.size());
4063 TEST_EQ(2, mutable_d_a.size());
4064 TEST_EQ(-1, const_d_a[0]);
4065 TEST_EQ(2, const_d_a[1]);
4066 TEST_ASSERT(mutable_d_a.end() == std::copy(const_d_a.begin(),
4067 const_d_a.end(),
4068 mutable_d_a.begin()));
4069 TEST_ASSERT(
4070 std::equal(const_d_a.begin(), const_d_a.end(), mutable_d_a.begin()));
4071 }
4072 # endif
4073 }
4074 #else
FixedLengthArrayJsonTest(bool)4075 void FixedLengthArrayJsonTest(bool /*binary*/) {}
FixedLengthArraySpanTest()4076 void FixedLengthArraySpanTest() {}
4077 #endif
4078
TestEmbeddedBinarySchema()4079 void TestEmbeddedBinarySchema() {
4080 // load JSON from disk
4081 std::string jsonfile;
4082 TEST_EQ(flatbuffers::LoadFile(
4083 (test_data_path + "monsterdata_test.golden").c_str(), false,
4084 &jsonfile),
4085 true);
4086
4087 // parse schema first, so we can use it to parse the data after
4088 flatbuffers::Parser parserOrg, parserGen;
4089 flatbuffers::Verifier verifier(MyGame::Example::MonsterBinarySchema::data(),
4090 MyGame::Example::MonsterBinarySchema::size());
4091 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
4092 TEST_EQ(parserOrg.Deserialize(MyGame::Example::MonsterBinarySchema::data(),
4093 MyGame::Example::MonsterBinarySchema::size()),
4094 true);
4095 TEST_EQ(parserGen.Deserialize(MyGame::Example::MonsterBinarySchema::data(),
4096 MyGame::Example::MonsterBinarySchema::size()),
4097 true);
4098 TEST_EQ(parserOrg.Parse(jsonfile.c_str()), true);
4099
4100 // First, verify it, just in case:
4101 flatbuffers::Verifier verifierOrg(parserOrg.builder_.GetBufferPointer(),
4102 parserOrg.builder_.GetSize());
4103 TEST_EQ(VerifyMonsterBuffer(verifierOrg), true);
4104
4105 // Export to JSON
4106 std::string jsonGen;
4107 TEST_EQ(
4108 GenerateText(parserOrg, parserOrg.builder_.GetBufferPointer(), &jsonGen),
4109 true);
4110
4111 // Import from JSON
4112 TEST_EQ(parserGen.Parse(jsonGen.c_str()), true);
4113
4114 // Verify buffer from generated JSON
4115 flatbuffers::Verifier verifierGen(parserGen.builder_.GetBufferPointer(),
4116 parserGen.builder_.GetSize());
4117 TEST_EQ(VerifyMonsterBuffer(verifierGen), true);
4118
4119 // Compare generated buffer to original
4120 TEST_EQ(parserOrg.builder_.GetSize(), parserGen.builder_.GetSize());
4121 TEST_EQ(std::memcmp(parserOrg.builder_.GetBufferPointer(),
4122 parserGen.builder_.GetBufferPointer(),
4123 parserOrg.builder_.GetSize()),
4124 0);
4125 }
4126
StringVectorDefaultsTest()4127 void StringVectorDefaultsTest() {
4128 std::vector<std::string> schemas;
4129 schemas.push_back("table Monster { mana: string = \"\"; }");
4130 schemas.push_back("table Monster { mana: string = \"mystr\"; }");
4131 schemas.push_back("table Monster { mana: string = \" \"; }");
4132 schemas.push_back("table Monster { mana: string = \"null\"; }");
4133 schemas.push_back("table Monster { mana: [int] = []; }");
4134 schemas.push_back("table Monster { mana: [uint] = [ ]; }");
4135 schemas.push_back("table Monster { mana: [byte] = [\t\t\n]; }");
4136 schemas.push_back("enum E:int{}table Monster{mana:[E]=[];}");
4137 for (auto s = schemas.begin(); s < schemas.end(); s++) {
4138 flatbuffers::Parser parser;
4139 TEST_ASSERT(parser.Parse(s->c_str()));
4140 const auto *mana = parser.structs_.Lookup("Monster")->fields.Lookup("mana");
4141 TEST_EQ(mana->IsDefault(), true);
4142 }
4143 }
4144
OptionalScalarsTest()4145 void OptionalScalarsTest() {
4146 // Simple schemas and a "has optional scalar" sentinal.
4147 std::vector<std::string> schemas;
4148 schemas.push_back("table Monster { mana : int; }");
4149 schemas.push_back("table Monster { mana : int = 42; }");
4150 schemas.push_back("table Monster { mana : int = null; }");
4151 schemas.push_back("table Monster { mana : long; }");
4152 schemas.push_back("table Monster { mana : long = 42; }");
4153 schemas.push_back("table Monster { mana : long = null; }");
4154 schemas.push_back("table Monster { mana : float; }");
4155 schemas.push_back("table Monster { mana : float = 42; }");
4156 schemas.push_back("table Monster { mana : float = null; }");
4157 schemas.push_back("table Monster { mana : double; }");
4158 schemas.push_back("table Monster { mana : double = 42; }");
4159 schemas.push_back("table Monster { mana : double = null; }");
4160 schemas.push_back("table Monster { mana : bool; }");
4161 schemas.push_back("table Monster { mana : bool = 42; }");
4162 schemas.push_back("table Monster { mana : bool = null; }");
4163 schemas.push_back(
4164 "enum Enum: int {A=0, B=1} "
4165 "table Monster { mana : Enum; }");
4166 schemas.push_back(
4167 "enum Enum: int {A=0, B=1} "
4168 "table Monster { mana : Enum = B; }");
4169 schemas.push_back(
4170 "enum Enum: int {A=0, B=1} "
4171 "table Monster { mana : Enum = null; }");
4172
4173 // Check the FieldDef is correctly set.
4174 for (auto schema = schemas.begin(); schema < schemas.end(); schema++) {
4175 const bool has_null = schema->find("null") != std::string::npos;
4176 flatbuffers::Parser parser;
4177 TEST_ASSERT(parser.Parse(schema->c_str()));
4178 const auto *mana = parser.structs_.Lookup("Monster")->fields.Lookup("mana");
4179 TEST_EQ(mana->IsOptional(), has_null);
4180 }
4181
4182 // Test if nullable scalars are allowed for each language.
4183 for (unsigned lang = 1; lang < flatbuffers::IDLOptions::kMAX; lang <<= 1) {
4184 flatbuffers::IDLOptions opts;
4185 opts.lang_to_generate = lang;
4186 if (false == flatbuffers::Parser::SupportsOptionalScalars(opts)) {
4187 continue;
4188 }
4189 for (auto schema = schemas.begin(); schema < schemas.end(); schema++) {
4190 flatbuffers::Parser parser(opts);
4191 auto done = parser.Parse(schema->c_str());
4192 TEST_EQ_STR(parser.error_.c_str(), "");
4193 TEST_ASSERT(done);
4194 }
4195 }
4196
4197 // test C++ nullable
4198 flatbuffers::FlatBufferBuilder fbb;
4199 FinishScalarStuffBuffer(
4200 fbb, optional_scalars::CreateScalarStuff(fbb, 1, static_cast<int8_t>(2)));
4201 auto opts = optional_scalars::GetMutableScalarStuff(fbb.GetBufferPointer());
4202 TEST_ASSERT(!opts->maybe_bool());
4203 TEST_ASSERT(!opts->maybe_f32().has_value());
4204 TEST_ASSERT(opts->maybe_i8().has_value());
4205 TEST_EQ(opts->maybe_i8().value(), 2);
4206 TEST_ASSERT(opts->mutate_maybe_i8(3));
4207 TEST_ASSERT(opts->maybe_i8().has_value());
4208 TEST_EQ(opts->maybe_i8().value(), 3);
4209 TEST_ASSERT(!opts->mutate_maybe_i16(-10));
4210
4211 optional_scalars::ScalarStuffT obj;
4212 TEST_ASSERT(!obj.maybe_bool);
4213 TEST_ASSERT(!obj.maybe_f32.has_value());
4214 opts->UnPackTo(&obj);
4215 TEST_ASSERT(!obj.maybe_bool);
4216 TEST_ASSERT(!obj.maybe_f32.has_value());
4217 TEST_ASSERT(obj.maybe_i8.has_value() && obj.maybe_i8.value() == 3);
4218 TEST_ASSERT(obj.maybe_i8 && *obj.maybe_i8 == 3);
4219 obj.maybe_i32 = -1;
4220 obj.maybe_enum = optional_scalars::OptionalByte_Two;
4221
4222 fbb.Clear();
4223 FinishScalarStuffBuffer(fbb, optional_scalars::ScalarStuff::Pack(fbb, &obj));
4224 opts = optional_scalars::GetMutableScalarStuff(fbb.GetBufferPointer());
4225 TEST_ASSERT(opts->maybe_i8().has_value());
4226 TEST_EQ(opts->maybe_i8().value(), 3);
4227 TEST_ASSERT(opts->maybe_i32().has_value());
4228 TEST_EQ(opts->maybe_i32().value(), -1);
4229 TEST_EQ(opts->maybe_enum().value(), optional_scalars::OptionalByte_Two);
4230 TEST_ASSERT(opts->maybe_i32() == flatbuffers::Optional<int64_t>(-1));
4231 }
4232
ParseFlexbuffersFromJsonWithNullTest()4233 void ParseFlexbuffersFromJsonWithNullTest() {
4234 // Test nulls are handled appropriately through flexbuffers to exercise other
4235 // code paths of ParseSingleValue in the optional scalars change.
4236 // TODO(cneo): Json -> Flatbuffers test once some language can generate code
4237 // with optional scalars.
4238 {
4239 char json[] = "{\"opt_field\": 123 }";
4240 flatbuffers::Parser parser;
4241 flexbuffers::Builder flexbuild;
4242 parser.ParseFlexBuffer(json, nullptr, &flexbuild);
4243 auto root = flexbuffers::GetRoot(flexbuild.GetBuffer());
4244 TEST_EQ(root.AsMap()["opt_field"].AsInt64(), 123);
4245 }
4246 {
4247 char json[] = "{\"opt_field\": 123.4 }";
4248 flatbuffers::Parser parser;
4249 flexbuffers::Builder flexbuild;
4250 parser.ParseFlexBuffer(json, nullptr, &flexbuild);
4251 auto root = flexbuffers::GetRoot(flexbuild.GetBuffer());
4252 TEST_EQ(root.AsMap()["opt_field"].AsDouble(), 123.4);
4253 }
4254 {
4255 char json[] = "{\"opt_field\": null }";
4256 flatbuffers::Parser parser;
4257 flexbuffers::Builder flexbuild;
4258 parser.ParseFlexBuffer(json, nullptr, &flexbuild);
4259 auto root = flexbuffers::GetRoot(flexbuild.GetBuffer());
4260 TEST_ASSERT(!root.AsMap().IsTheEmptyMap());
4261 TEST_ASSERT(root.AsMap()["opt_field"].IsNull());
4262 TEST_EQ(root.ToString(), std::string("{ opt_field: null }"));
4263 }
4264 }
4265
FieldIdentifierTest()4266 void FieldIdentifierTest() {
4267 using flatbuffers::Parser;
4268 TEST_EQ(true, Parser().Parse("table T{ f: int (id:0); }"));
4269 // non-integer `id` should be rejected
4270 TEST_EQ(false, Parser().Parse("table T{ f: int (id:text); }"));
4271 TEST_EQ(false, Parser().Parse("table T{ f: int (id:\"text\"); }"));
4272 TEST_EQ(false, Parser().Parse("table T{ f: int (id:0text); }"));
4273 TEST_EQ(false, Parser().Parse("table T{ f: int (id:1.0); }"));
4274 TEST_EQ(false, Parser().Parse("table T{ f: int (id:-1); g: int (id:0); }"));
4275 TEST_EQ(false, Parser().Parse("table T{ f: int (id:129496726); }"));
4276 // A unuion filed occupys two ids: enumerator + pointer (offset).
4277 TEST_EQ(false,
4278 Parser().Parse("union X{} table T{ u: X(id:0); table F{x:int;\n}"));
4279 // Positive tests for unions
4280 TEST_EQ(true, Parser().Parse("union X{} table T{ u: X (id:1); }"));
4281 TEST_EQ(true, Parser().Parse("union X{} table T{ u: X; }"));
4282 // Test using 'inf' and 'nan' words both as identifiers and as default values.
4283 TEST_EQ(true, Parser().Parse("table T{ nan: string; }"));
4284 TEST_EQ(true, Parser().Parse("table T{ inf: string; }"));
4285 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
4286 TEST_EQ(true, Parser().Parse("table T{ inf: float = inf; }"));
4287 TEST_EQ(true, Parser().Parse("table T{ nan: float = inf; }"));
4288 #endif
4289 }
4290
NestedVerifierTest()4291 void NestedVerifierTest() {
4292 // Create a nested monster.
4293 flatbuffers::FlatBufferBuilder nested_builder;
4294 FinishMonsterBuffer(
4295 nested_builder,
4296 CreateMonster(nested_builder, nullptr, 0, 0,
4297 nested_builder.CreateString("NestedMonster")));
4298
4299 // Verify the nested monster
4300 flatbuffers::Verifier verifier(nested_builder.GetBufferPointer(),
4301 nested_builder.GetSize());
4302 TEST_EQ(true, VerifyMonsterBuffer(verifier));
4303
4304 {
4305 // Create the outer monster.
4306 flatbuffers::FlatBufferBuilder builder;
4307
4308 // Add the nested monster as a vector of bytes.
4309 auto nested_monster_bytes = builder.CreateVector(
4310 nested_builder.GetBufferPointer(), nested_builder.GetSize());
4311
4312 auto name = builder.CreateString("OuterMonster");
4313
4314 MonsterBuilder mon_builder(builder);
4315 mon_builder.add_name(name);
4316 mon_builder.add_testnestedflatbuffer(nested_monster_bytes);
4317 FinishMonsterBuffer(builder, mon_builder.Finish());
4318
4319 // Verify the root monster, which includes verifing the nested monster
4320 flatbuffers::Verifier verifier(builder.GetBufferPointer(),
4321 builder.GetSize());
4322 TEST_EQ(true, VerifyMonsterBuffer(verifier));
4323 }
4324
4325 {
4326 // Create the outer monster.
4327 flatbuffers::FlatBufferBuilder builder;
4328
4329 // Purposely invalidate the nested flatbuffer setting its length to 1, an
4330 // invalid length.
4331 uint8_t invalid_nested_buffer[1];
4332 auto nested_monster_bytes = builder.CreateVector(invalid_nested_buffer, 1);
4333
4334 auto name = builder.CreateString("OuterMonster");
4335
4336 MonsterBuilder mon_builder(builder);
4337 mon_builder.add_name(name);
4338 mon_builder.add_testnestedflatbuffer(nested_monster_bytes);
4339 FinishMonsterBuffer(builder, mon_builder.Finish());
4340
4341 // Verify the root monster fails, since the included nested monster fails.
4342 flatbuffers::Verifier verifier(builder.GetBufferPointer(),
4343 builder.GetSize());
4344 TEST_EQ(false, VerifyMonsterBuffer(verifier));
4345 }
4346
4347 {
4348 // Create the outer monster.
4349 flatbuffers::FlatBufferBuilder builder;
4350
4351 // Purposely invalidate the nested flatbuffer setting its length to 0, an
4352 // invalid length.
4353 uint8_t *invalid_nested_buffer = nullptr;
4354 auto nested_monster_bytes = builder.CreateVector(invalid_nested_buffer, 0);
4355
4356 auto name = builder.CreateString("OuterMonster");
4357
4358 MonsterBuilder mon_builder(builder);
4359 mon_builder.add_name(name);
4360 mon_builder.add_testnestedflatbuffer(nested_monster_bytes);
4361 FinishMonsterBuffer(builder, mon_builder.Finish());
4362
4363 // Verify the root monster fails, since the included nested monster fails.
4364 flatbuffers::Verifier verifier(builder.GetBufferPointer(),
4365 builder.GetSize());
4366 TEST_EQ(false, VerifyMonsterBuffer(verifier));
4367 }
4368 }
4369
ParseIncorrectMonsterJsonTest()4370 void ParseIncorrectMonsterJsonTest() {
4371 std::string schemafile;
4372 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.bfbs").c_str(),
4373 true, &schemafile),
4374 true);
4375 flatbuffers::Parser parser;
4376 flatbuffers::Verifier verifier(
4377 reinterpret_cast<const uint8_t *>(schemafile.c_str()), schemafile.size());
4378 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
4379 TEST_EQ(
4380 parser.Deserialize(reinterpret_cast<const uint8_t *>(schemafile.c_str()),
4381 schemafile.size()),
4382 true);
4383 TEST_EQ(parser.ParseJson("{name:\"monster\"}"), true);
4384 TEST_EQ(parser.ParseJson(""), false);
4385 TEST_EQ(parser.ParseJson("{name: 1}"), false);
4386 TEST_EQ(parser.ParseJson("{name:+1}"), false);
4387 TEST_EQ(parser.ParseJson("{name:-1}"), false);
4388 TEST_EQ(parser.ParseJson("{name:-f}"), false);
4389 TEST_EQ(parser.ParseJson("{name:+f}"), false);
4390 }
4391
4392 #if !defined(_MSC_VER) || _MSC_VER >= 1700
4393 template<class T, class Container>
TestIterators(const std::vector<T> & expected,const Container & tested)4394 void TestIterators(const std::vector<T> &expected, const Container &tested) {
4395 TEST_ASSERT(tested.rbegin().base() == tested.end());
4396 TEST_ASSERT(tested.crbegin().base() == tested.cend());
4397 TEST_ASSERT(tested.rend().base() == tested.begin());
4398 TEST_ASSERT(tested.crend().base() == tested.cbegin());
4399
4400 size_t k = 0;
4401 for (auto it = tested.begin(); it != tested.end(); ++it, ++k) {
4402 const auto &e = expected.at(k);
4403 TEST_EQ(*it, e);
4404 }
4405 TEST_EQ(k, expected.size());
4406
4407 k = expected.size();
4408 for (auto it = tested.rbegin(); it != tested.rend(); ++it, --k) {
4409 const auto &e = expected.at(k - 1);
4410 TEST_EQ(*it, e);
4411 }
4412 TEST_EQ(k, 0);
4413 }
4414
FlatbuffersIteratorsTest()4415 void FlatbuffersIteratorsTest() {
4416 {
4417 flatbuffers::FlatBufferBuilder fbb;
4418 const std::vector<unsigned char> inv_data = { 1, 2, 3 };
4419 {
4420 auto mon_name = fbb.CreateString("MyMonster"); // key, mandatory
4421 auto inv_vec = fbb.CreateVector(inv_data);
4422 auto empty_i64_vec =
4423 fbb.CreateVector(static_cast<const int64_t *>(nullptr), 0);
4424 MonsterBuilder mb(fbb);
4425 mb.add_name(mon_name);
4426 mb.add_inventory(inv_vec);
4427 mb.add_vector_of_longs(empty_i64_vec);
4428 FinishMonsterBuffer(fbb, mb.Finish());
4429 }
4430 const auto &mon = *flatbuffers::GetRoot<Monster>(fbb.GetBufferPointer());
4431
4432 TEST_EQ_STR("MyMonster", mon.name()->c_str());
4433 TEST_ASSERT(mon.inventory());
4434 TEST_ASSERT(mon.vector_of_longs());
4435 TestIterators(inv_data, *mon.inventory());
4436 TestIterators(std::vector<int64_t>(), *mon.vector_of_longs());
4437 }
4438
4439 {
4440 flatbuffers::FlatBufferBuilder fbb;
4441 MyGame::Example::ArrayStruct aStruct;
4442 MyGame::Example::FinishArrayTableBuffer(
4443 fbb, MyGame::Example::CreateArrayTable(fbb, &aStruct));
4444 const auto &array_table =
4445 *flatbuffers::GetRoot<ArrayTable>(fbb.GetBufferPointer());
4446 TEST_ASSERT(array_table.a());
4447 auto &int_15 = *array_table.a()->b();
4448 TestIterators(std::vector<int>(15, 0), int_15);
4449 }
4450 }
4451 #else
FlatbuffersIteratorsTest()4452 void FlatbuffersIteratorsTest() {}
4453 #endif
4454
PrivateAnnotationsLeaks()4455 void PrivateAnnotationsLeaks() {
4456 // Simple schemas and a "has optional scalar" sentinal.
4457 std::vector<std::string> schemas;
4458 std::vector<std::string> failure_schemas;
4459
4460 // (private) (table/struct)
4461 schemas.push_back(
4462 "table Monster (private) { mana: int; }"
4463 "struct ABC (private) { mana: int; }");
4464
4465 // (public) (table/struct)
4466 schemas.push_back(
4467 "table Monster { mana: int; }"
4468 "struct ABC { mana: int; }");
4469
4470 // (private) (union) containing (private) (table/struct)
4471 schemas.push_back(
4472 "table Monster (private) { mana: int; } "
4473 "struct ABC (private) { mana: int; } "
4474 "union Any (private) { Monster, ABC } ");
4475
4476 // (public) (union) containing (public) (table/struct)
4477 schemas.push_back(
4478 "table Monster { mana: int; }"
4479 "struct ABC { mana: int; }"
4480 "union Any { Monster, ABC }");
4481
4482 // (private) (table/struct/enum)
4483 schemas.push_back(
4484 "table Monster (private) { mana: int; }"
4485 "struct ABC (private) { mana: int; }"
4486 "enum Race:byte (private) { None = -1, Human = 0, }");
4487
4488 // (public) (table/struct/enum)
4489 schemas.push_back(
4490 "table Monster { mana: int; }"
4491 "struct ABC { mana: int; }"
4492 "enum Race:byte { None = -1, Human = 0, }");
4493
4494 // (private) (union) containing (private) (table/struct)
4495 schemas.push_back(
4496 "table Monster (private) { mana: int; }"
4497 "struct ABC (private) { mana: int; }"
4498 "enum Race:byte (private) { None = -1, Human = 0, }"
4499 "union Any (private) { Monster, ABC }");
4500
4501 // (public) (union) containing (public) (table/struct)
4502 schemas.push_back(
4503 "table Monster { mana: int; }"
4504 "struct ABC { mana: int; }"
4505 "enum Race:byte { None = -1, Human = 0, }"
4506 "union Any { Monster, ABC }");
4507
4508 // (private) (table), (public struct)
4509 schemas.push_back(
4510 "table Monster (private) { mana: int; }"
4511 "struct ABC { mana: int; }");
4512
4513 // (private) (table), (public) (struct/enum)
4514 schemas.push_back(
4515 "table Monster (private) { mana: int; }"
4516 "struct ABC { mana: int; }"
4517 "enum Race:byte { None = -1, Human = 0, }");
4518
4519 // (public) (struct) containing (public) (enum)
4520 schemas.push_back(
4521 "enum Race:byte { None = -1, Human = 0, }"
4522 "table Monster { mana: int; }"
4523 "struct ABC { mana: int; type: Race; }");
4524
4525 // (public) (union) containing (private) (table) & (public) (struct)
4526 failure_schemas.push_back(
4527 "table Monster (private) { mana: int; }"
4528 "struct ABC { mana: int; }"
4529 "union Any { Monster, ABC }");
4530
4531 // (public) (union) containing (private) (table/struct)
4532 failure_schemas.push_back(
4533 "table Monster (private) { mana: int; }"
4534 "struct ABC (private) { mana: int; }"
4535 "enum Race:byte { None = -1, Human = 0, }"
4536 "union Any { Monster, ABC }");
4537
4538 // (public) (table) containing (private) (struct)
4539 failure_schemas.push_back(
4540 "table Monster { mana: int; ab: ABC; }"
4541 "struct ABC (private) { mana: int; }");
4542
4543 // (public) (struct) containing (private) (enum)
4544 failure_schemas.push_back(
4545 "enum Race:byte (private) { None = -1, Human = 0, }"
4546 "table Monster { mana: int; }"
4547 "struct ABC { mana: int; type: Race; }");
4548
4549 flatbuffers::IDLOptions opts;
4550 opts.lang_to_generate = flatbuffers::IDLOptions::Language::kSwift;
4551 opts.no_leak_private_annotations = true;
4552
4553 for (auto schema = schemas.begin(); schema < schemas.end(); schema++) {
4554 flatbuffers::Parser parser(opts);
4555 TEST_ASSERT(parser.Parse(schema->c_str()));
4556 }
4557
4558 for (auto schema = failure_schemas.begin(); schema < failure_schemas.end();
4559 schema++) {
4560 flatbuffers::Parser parser(opts);
4561 TEST_EQ(false, parser.Parse(schema->c_str()));
4562 }
4563
4564 opts.no_leak_private_annotations = false;
4565
4566 for (auto schema = schemas.begin(); schema < schemas.end(); schema++) {
4567 flatbuffers::Parser parser(opts);
4568 TEST_ASSERT(parser.Parse(schema->c_str()));
4569 }
4570
4571 for (auto schema = failure_schemas.begin(); schema < failure_schemas.end();
4572 schema++) {
4573 flatbuffers::Parser parser(opts);
4574 TEST_ASSERT(parser.Parse(schema->c_str()));
4575 }
4576 }
4577
JsonUnsortedArrayTest()4578 void JsonUnsortedArrayTest()
4579 {
4580 flatbuffers::Parser parser;
4581 TEST_EQ(parser.Deserialize(MyGame::Example::MonsterBinarySchema::data(), MyGame::Example::MonsterBinarySchema::size()), true);
4582 auto jsonStr = R"(
4583 {
4584 "name": "lookupTest",
4585 "testarrayoftables": [
4586 { "name": "aaa" },
4587 { "name": "ccc" },
4588 { "name": "bbb" }
4589 ]
4590 }
4591 )";
4592 TEST_EQ(parser.ParseJson(jsonStr), true);
4593 auto monster = flatbuffers::GetRoot<MyGame::Example::Monster>(parser.builder_.GetBufferPointer());
4594
4595 TEST_NOTNULL(monster->testarrayoftables()->LookupByKey("aaa"));
4596 TEST_NOTNULL(monster->testarrayoftables()->LookupByKey("bbb"));
4597 TEST_NOTNULL(monster->testarrayoftables()->LookupByKey("ccc"));
4598 }
4599
VectorSpanTest()4600 void VectorSpanTest() {
4601 flatbuffers::FlatBufferBuilder builder;
4602
4603 auto mloc =
4604 CreateMonster(builder, nullptr, 0, 0, builder.CreateString("Monster"),
4605 builder.CreateVector<uint8_t>({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }));
4606
4607 FinishMonsterBuffer(builder, mloc);
4608
4609 auto monster = GetMonster(builder.GetBufferPointer());
4610 auto mutable_monster = GetMutableMonster(builder.GetBufferPointer());
4611
4612 { // using references
4613 TEST_NOTNULL(monster->inventory());
4614
4615 flatbuffers::span<const uint8_t> const_inventory =
4616 flatbuffers::make_span(*monster->inventory());
4617 TEST_EQ(const_inventory.size(), 10);
4618 TEST_EQ(const_inventory[0], 0);
4619 TEST_EQ(const_inventory[9], 9);
4620
4621 flatbuffers::span<uint8_t> mutable_inventory =
4622 flatbuffers::make_span(*mutable_monster->mutable_inventory());
4623 TEST_EQ(mutable_inventory.size(), 10);
4624 TEST_EQ(mutable_inventory[0], 0);
4625 TEST_EQ(mutable_inventory[9], 9);
4626
4627 mutable_inventory[0] = 42;
4628 TEST_EQ(mutable_inventory[0], 42);
4629
4630 mutable_inventory[0] = 0;
4631 TEST_EQ(mutable_inventory[0], 0);
4632 }
4633
4634 { // using pointers
4635 TEST_EQ(flatbuffers::VectorLength(monster->inventory()), 10);
4636
4637 flatbuffers::span<const uint8_t> const_inventory =
4638 flatbuffers::make_span(monster->inventory());
4639 TEST_EQ(const_inventory.size(), 10);
4640 TEST_EQ(const_inventory[0], 0);
4641 TEST_EQ(const_inventory[9], 9);
4642
4643 flatbuffers::span<uint8_t> mutable_inventory =
4644 flatbuffers::make_span(mutable_monster->mutable_inventory());
4645 TEST_EQ(mutable_inventory.size(), 10);
4646 TEST_EQ(mutable_inventory[0], 0);
4647 TEST_EQ(mutable_inventory[9], 9);
4648
4649 mutable_inventory[0] = 42;
4650 TEST_EQ(mutable_inventory[0], 42);
4651
4652 mutable_inventory[0] = 0;
4653 TEST_EQ(mutable_inventory[0], 0);
4654 }
4655
4656 {
4657 TEST_ASSERT(nullptr == monster->testnestedflatbuffer());
4658
4659 TEST_EQ(flatbuffers::VectorLength(monster->testnestedflatbuffer()), 0);
4660
4661 flatbuffers::span<const uint8_t> const_nested =
4662 flatbuffers::make_span(monster->testnestedflatbuffer());
4663 TEST_ASSERT(const_nested.empty());
4664
4665 flatbuffers::span<uint8_t> mutable_nested =
4666 flatbuffers::make_span(mutable_monster->mutable_testnestedflatbuffer());
4667 TEST_ASSERT(mutable_nested.empty());
4668 }
4669 }
4670
FlatBufferTests()4671 int FlatBufferTests() {
4672 // clang-format off
4673
4674 // Run our various test suites:
4675
4676 std::string rawbuf;
4677 auto flatbuf1 = CreateFlatBufferTest(rawbuf);
4678 auto flatbuf = std::move(flatbuf1); // Test move assignment.
4679
4680 TriviallyCopyableTest();
4681
4682 AccessFlatBufferTest(reinterpret_cast<const uint8_t *>(rawbuf.c_str()),
4683 rawbuf.length());
4684 AccessFlatBufferTest(flatbuf.data(), flatbuf.size());
4685
4686 MutateFlatBuffersTest(flatbuf.data(), flatbuf.size());
4687
4688 ObjectFlatBuffersTest(flatbuf.data());
4689
4690 MiniReflectFlatBuffersTest(flatbuf.data());
4691 MiniReflectFixedLengthArrayTest();
4692
4693 SizePrefixedTest();
4694
4695 #ifndef FLATBUFFERS_NO_FILE_TESTS
4696 #ifdef FLATBUFFERS_TEST_PATH_PREFIX
4697 test_data_path = FLATBUFFERS_STRING(FLATBUFFERS_TEST_PATH_PREFIX) +
4698 test_data_path;
4699 #endif
4700 ParseAndGenerateTextTest(false);
4701 ParseAndGenerateTextTest(true);
4702 FixedLengthArrayJsonTest(false);
4703 FixedLengthArrayJsonTest(true);
4704 ReflectionTest(flatbuf.data(), flatbuf.size());
4705 ParseProtoTest();
4706 ParseProtoTestWithSuffix();
4707 ParseProtoTestWithIncludes();
4708 EvolutionTest();
4709 UnionDeprecationTest();
4710 UnionVectorTest();
4711 LoadVerifyBinaryTest();
4712 GenerateTableTextTest();
4713 TestEmbeddedBinarySchema();
4714 JsonOptionalTest(false);
4715 JsonOptionalTest(true);
4716 #endif
4717 // clang-format on
4718
4719 UtilConvertCase();
4720
4721 FuzzTest1();
4722 FuzzTest2();
4723
4724 ErrorTest();
4725 ValueTest();
4726 EnumValueTest();
4727 NestedListTest();
4728 EnumStringsTest();
4729 EnumNamesTest();
4730 EnumOutOfRangeTest();
4731 IntegerOutOfRangeTest();
4732 IntegerBoundaryTest();
4733 UnicodeTest();
4734 UnicodeTestAllowNonUTF8();
4735 UnicodeTestGenerateTextFailsOnNonUTF8();
4736 UnicodeSurrogatesTest();
4737 UnicodeInvalidSurrogatesTest();
4738 InvalidUTF8Test();
4739 UnknownFieldsTest();
4740 ParseUnionTest();
4741 ValidSameNameDifferentNamespaceTest();
4742 MultiFileNameClashTest();
4743 InvalidNestedFlatbufferTest();
4744 ConformTest();
4745 ParseProtoBufAsciiTest();
4746 TypeAliasesTest();
4747 EndianSwapTest();
4748 CreateSharedStringTest();
4749 JsonDefaultTest();
4750 JsonEnumsTest();
4751 FlexBuffersTest();
4752 FlexBuffersReuseBugTest();
4753 FlexBuffersDeprecatedTest();
4754 UninitializedVectorTest();
4755 EqualOperatorTest();
4756 NumericUtilsTest();
4757 IsAsciiUtilsTest();
4758 ValidFloatTest();
4759 InvalidFloatTest();
4760 TestMonsterExtraFloats();
4761 FixedLengthArrayTest();
4762 NativeTypeTest();
4763 OptionalScalarsTest();
4764 ParseFlexbuffersFromJsonWithNullTest();
4765 FlatbuffersSpanTest();
4766 FixedLengthArrayConstructorTest();
4767 FieldIdentifierTest();
4768 StringVectorDefaultsTest();
4769 ParseIncorrectMonsterJsonTest();
4770 FlexBuffersFloatingPointTest();
4771 FlatbuffersIteratorsTest();
4772 FixedLengthArraySpanTest();
4773 StructUnionTest();
4774 WarningsAsErrorsTest();
4775 NestedVerifierTest();
4776 PrivateAnnotationsLeaks();
4777 JsonUnsortedArrayTest();
4778 VectorSpanTest();
4779 return 0;
4780 }
4781 } // namespace
4782
main(int argc,const char * argv[])4783 int main(int argc, const char *argv[]) {
4784 for (int argi = 1; argi < argc; argi++) {
4785 std::string arg = argv[argi];
4786 if (arg == "--test_path") {
4787 if (++argi >= argc) {
4788 fprintf(stderr, "error: missing path following: %s\n", arg.c_str());
4789 exit(1);
4790 }
4791 test_data_path = argv[argi];
4792 } else {
4793 fprintf(stderr, "error: Unknown argument: %s\n", arg.c_str());
4794 exit(1);
4795 }
4796 }
4797
4798 InitTestEngine();
4799
4800 std::string req_locale;
4801 if (flatbuffers::ReadEnvironmentVariable("FLATBUFFERS_TEST_LOCALE",
4802 &req_locale)) {
4803 TEST_OUTPUT_LINE("The environment variable FLATBUFFERS_TEST_LOCALE=%s",
4804 req_locale.c_str());
4805 req_locale = flatbuffers::RemoveStringQuotes(req_locale);
4806 std::string the_locale;
4807 TEST_ASSERT_FUNC(
4808 flatbuffers::SetGlobalTestLocale(req_locale.c_str(), &the_locale));
4809 TEST_OUTPUT_LINE("The global C-locale changed: %s", the_locale.c_str());
4810 }
4811
4812 FlatBufferTests();
4813 FlatBufferBuilderTest();
4814
4815 if (!testing_fails) {
4816 TEST_OUTPUT_LINE("ALL TESTS PASSED");
4817 } else {
4818 TEST_OUTPUT_LINE("%d FAILED TESTS", testing_fails);
4819 }
4820 return CloseTestEngine();
4821 }
4822