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 <cmath>
17
18 #include "flatbuffers/flatbuffers.h"
19 #include "flatbuffers/idl.h"
20 #include "flatbuffers/minireflect.h"
21 #include "flatbuffers/registry.h"
22 #include "flatbuffers/util.h"
23
24 // clang-format off
25 #ifdef FLATBUFFERS_CPP98_STL
26 #include "flatbuffers/stl_emulation.h"
27 namespace std {
28 using flatbuffers::unique_ptr;
29 }
30 #endif
31 // clang-format on
32
33 #include "monster_test_generated.h"
34 #include "namespace_test/namespace_test1_generated.h"
35 #include "namespace_test/namespace_test2_generated.h"
36 #include "union_vector/union_vector_generated.h"
37 #include "monster_extra_generated.h"
38 #if !defined(_MSC_VER) || _MSC_VER >= 1700
39 # include "arrays_test_generated.h"
40 # include "evolution_test/evolution_v1_generated.h"
41 # include "evolution_test/evolution_v2_generated.h"
42 #endif
43
44 #include "native_type_test_generated.h"
45 #include "test_assert.h"
46
47 #include "flatbuffers/flexbuffers.h"
48 #include "monster_test_bfbs_generated.h" // Generated using --bfbs-comments --bfbs-builtins --cpp --bfbs-gen-embed
49
50 // clang-format off
51 // Check that char* and uint8_t* are interoperable types.
52 // The reinterpret_cast<> between the pointers are used to simplify data loading.
53 static_assert(flatbuffers::is_same<uint8_t, char>::value ||
54 flatbuffers::is_same<uint8_t, unsigned char>::value,
55 "unexpected uint8_t type");
56
57 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
58 // Ensure IEEE-754 support if tests of floats with NaN/Inf will run.
59 static_assert(std::numeric_limits<float>::is_iec559 &&
60 std::numeric_limits<double>::is_iec559,
61 "IEC-559 (IEEE-754) standard required");
62 #endif
63 // clang-format on
64
65 // Shortcuts for the infinity.
66 static const auto infinityf = std::numeric_limits<float>::infinity();
67 static const auto infinityd = std::numeric_limits<double>::infinity();
68
69 using namespace MyGame::Example;
70
71 void FlatBufferBuilderTest();
72
73 // Include simple random number generator to ensure results will be the
74 // same cross platform.
75 // http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator
76 uint32_t lcg_seed = 48271;
lcg_rand()77 uint32_t lcg_rand() {
78 return lcg_seed =
79 (static_cast<uint64_t>(lcg_seed) * 279470273UL) % 4294967291UL;
80 }
lcg_reset()81 void lcg_reset() { lcg_seed = 48271; }
82
83 std::string test_data_path =
84 #ifdef BAZEL_TEST_DATA_PATH
85 "../com_github_google_flatbuffers/tests/";
86 #else
87 "tests/";
88 #endif
89
90 // example of how to build up a serialized buffer algorithmically:
CreateFlatBufferTest(std::string & buffer)91 flatbuffers::DetachedBuffer CreateFlatBufferTest(std::string &buffer) {
92 flatbuffers::FlatBufferBuilder builder;
93
94 auto vec = Vec3(1, 2, 3, 0, Color_Red, Test(10, 20));
95
96 auto name = builder.CreateString("MyMonster");
97
98 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
99 auto inventory = builder.CreateVector(inv_data, 10);
100
101 // Alternatively, create the vector first, and fill in data later:
102 // unsigned char *inv_buf = nullptr;
103 // auto inventory = builder.CreateUninitializedVector<unsigned char>(
104 // 10, &inv_buf);
105 // memcpy(inv_buf, inv_data, 10);
106
107 Test tests[] = { Test(10, 20), Test(30, 40) };
108 auto testv = builder.CreateVectorOfStructs(tests, 2);
109
110 // clang-format off
111 #ifndef FLATBUFFERS_CPP98_STL
112 // Create a vector of structures from a lambda.
113 auto testv2 = builder.CreateVectorOfStructs<Test>(
114 2, [&](size_t i, Test* s) -> void {
115 *s = tests[i];
116 });
117 #else
118 // Create a vector of structures using a plain old C++ function.
119 auto testv2 = builder.CreateVectorOfStructs<Test>(
120 2, [](size_t i, Test* s, void *state) -> void {
121 *s = (reinterpret_cast<Test*>(state))[i];
122 }, tests);
123 #endif // FLATBUFFERS_CPP98_STL
124 // clang-format on
125
126 // create monster with very few fields set:
127 // (same functionality as CreateMonster below, but sets fields manually)
128 flatbuffers::Offset<Monster> mlocs[3];
129 auto fred = builder.CreateString("Fred");
130 auto barney = builder.CreateString("Barney");
131 auto wilma = builder.CreateString("Wilma");
132 MonsterBuilder mb1(builder);
133 mb1.add_name(fred);
134 mlocs[0] = mb1.Finish();
135 MonsterBuilder mb2(builder);
136 mb2.add_name(barney);
137 mb2.add_hp(1000);
138 mlocs[1] = mb2.Finish();
139 MonsterBuilder mb3(builder);
140 mb3.add_name(wilma);
141 mlocs[2] = mb3.Finish();
142
143 // Create an array of strings. Also test string pooling, and lambdas.
144 auto vecofstrings =
145 builder.CreateVector<flatbuffers::Offset<flatbuffers::String>>(
146 4,
147 [](size_t i, flatbuffers::FlatBufferBuilder *b)
148 -> flatbuffers::Offset<flatbuffers::String> {
149 static const char *names[] = { "bob", "fred", "bob", "fred" };
150 return b->CreateSharedString(names[i]);
151 },
152 &builder);
153
154 // Creating vectors of strings in one convenient call.
155 std::vector<std::string> names2;
156 names2.push_back("jane");
157 names2.push_back("mary");
158 auto vecofstrings2 = builder.CreateVectorOfStrings(names2);
159
160 // Create an array of sorted tables, can be used with binary search when read:
161 auto vecoftables = builder.CreateVectorOfSortedTables(mlocs, 3);
162
163 // Create an array of sorted structs,
164 // can be used with binary search when read:
165 std::vector<Ability> abilities;
166 abilities.push_back(Ability(4, 40));
167 abilities.push_back(Ability(3, 30));
168 abilities.push_back(Ability(2, 20));
169 abilities.push_back(Ability(1, 10));
170 auto vecofstructs = builder.CreateVectorOfSortedStructs(&abilities);
171
172 // Create a nested FlatBuffer.
173 // Nested FlatBuffers are stored in a ubyte vector, which can be convenient
174 // since they can be memcpy'd around much easier than other FlatBuffer
175 // values. They have little overhead compared to storing the table directly.
176 // As a test, create a mostly empty Monster buffer:
177 flatbuffers::FlatBufferBuilder nested_builder;
178 auto nmloc = CreateMonster(nested_builder, nullptr, 0, 0,
179 nested_builder.CreateString("NestedMonster"));
180 FinishMonsterBuffer(nested_builder, nmloc);
181 // Now we can store the buffer in the parent. Note that by default, vectors
182 // are only aligned to their elements or size field, so in this case if the
183 // buffer contains 64-bit elements, they may not be correctly aligned. We fix
184 // that with:
185 builder.ForceVectorAlignment(nested_builder.GetSize(), sizeof(uint8_t),
186 nested_builder.GetBufferMinAlignment());
187 // If for whatever reason you don't have the nested_builder available, you
188 // can substitute flatbuffers::largest_scalar_t (64-bit) for the alignment, or
189 // the largest force_align value in your schema if you're using it.
190 auto nested_flatbuffer_vector = builder.CreateVector(
191 nested_builder.GetBufferPointer(), nested_builder.GetSize());
192
193 // Test a nested FlexBuffer:
194 flexbuffers::Builder flexbuild;
195 flexbuild.Int(1234);
196 flexbuild.Finish();
197 auto flex = builder.CreateVector(flexbuild.GetBuffer());
198
199 // Test vector of enums.
200 Color colors[] = { Color_Blue, Color_Green };
201 // We use this special creation function because we have an array of
202 // pre-C++11 (enum class) enums whose size likely is int, yet its declared
203 // type in the schema is byte.
204 auto vecofcolors = builder.CreateVectorScalarCast<uint8_t, Color>(colors, 2);
205
206 // shortcut for creating monster with all fields set:
207 auto mloc = CreateMonster(
208 builder, &vec, 150, 80, name, inventory, Color_Blue, Any_Monster,
209 mlocs[1].Union(), // Store a union.
210 testv, vecofstrings, vecoftables, 0, nested_flatbuffer_vector, 0, false,
211 0, 0, 0, 0, 0, 0, 0, 0, 0, 3.14159f, 3.0f, 0.0f, vecofstrings2,
212 vecofstructs, flex, testv2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
213 AnyUniqueAliases_NONE, 0, AnyAmbiguousAliases_NONE, 0, vecofcolors);
214
215 FinishMonsterBuffer(builder, mloc);
216
217 // clang-format off
218 #ifdef FLATBUFFERS_TEST_VERBOSE
219 // print byte data for debugging:
220 auto p = builder.GetBufferPointer();
221 for (flatbuffers::uoffset_t i = 0; i < builder.GetSize(); i++)
222 printf("%d ", p[i]);
223 #endif
224 // clang-format on
225
226 // return the buffer for the caller to use.
227 auto bufferpointer =
228 reinterpret_cast<const char *>(builder.GetBufferPointer());
229 buffer.assign(bufferpointer, bufferpointer + builder.GetSize());
230
231 return builder.Release();
232 }
233
234 // example of accessing a buffer loaded in memory:
AccessFlatBufferTest(const uint8_t * flatbuf,size_t length,bool pooled=true)235 void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length,
236 bool pooled = true) {
237 // First, verify the buffers integrity (optional)
238 flatbuffers::Verifier verifier(flatbuf, length);
239 TEST_EQ(VerifyMonsterBuffer(verifier), true);
240
241 // clang-format off
242 #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
243 std::vector<uint8_t> test_buff;
244 test_buff.resize(length * 2);
245 std::memcpy(&test_buff[0], flatbuf, length);
246 std::memcpy(&test_buff[length], flatbuf, length);
247
248 flatbuffers::Verifier verifier1(&test_buff[0], length);
249 TEST_EQ(VerifyMonsterBuffer(verifier1), true);
250 TEST_EQ(verifier1.GetComputedSize(), length);
251
252 flatbuffers::Verifier verifier2(&test_buff[length], length);
253 TEST_EQ(VerifyMonsterBuffer(verifier2), true);
254 TEST_EQ(verifier2.GetComputedSize(), length);
255 #endif
256 // clang-format on
257
258 TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0);
259 TEST_EQ(MonsterBufferHasIdentifier(flatbuf), true);
260 TEST_EQ(strcmp(MonsterExtension(), "mon"), 0);
261
262 // Access the buffer from the root.
263 auto monster = GetMonster(flatbuf);
264
265 TEST_EQ(monster->hp(), 80);
266 TEST_EQ(monster->mana(), 150); // default
267 TEST_EQ_STR(monster->name()->c_str(), "MyMonster");
268 // Can't access the following field, it is deprecated in the schema,
269 // which means accessors are not generated:
270 // monster.friendly()
271
272 auto pos = monster->pos();
273 TEST_NOTNULL(pos);
274 TEST_EQ(pos->z(), 3);
275 TEST_EQ(pos->test3().a(), 10);
276 TEST_EQ(pos->test3().b(), 20);
277
278 auto inventory = monster->inventory();
279 TEST_EQ(VectorLength(inventory), 10UL); // Works even if inventory is null.
280 TEST_NOTNULL(inventory);
281 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
282 // Check compatibilty of iterators with STL.
283 std::vector<unsigned char> inv_vec(inventory->begin(), inventory->end());
284 int n = 0;
285 for (auto it = inventory->begin(); it != inventory->end(); ++it, ++n) {
286 auto indx = it - inventory->begin();
287 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
288 TEST_EQ(*it, inv_data[indx]);
289 }
290 TEST_EQ(n, inv_vec.size());
291
292 n = 0;
293 for (auto it = inventory->cbegin(); it != inventory->cend(); ++it, ++n) {
294 auto indx = it - inventory->cbegin();
295 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
296 TEST_EQ(*it, inv_data[indx]);
297 }
298 TEST_EQ(n, inv_vec.size());
299
300 n = 0;
301 for (auto it = inventory->rbegin(); it != inventory->rend(); ++it, ++n) {
302 auto indx = inventory->rend() - it - 1;
303 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
304 TEST_EQ(*it, inv_data[indx]);
305 }
306 TEST_EQ(n, inv_vec.size());
307
308 n = 0;
309 for (auto it = inventory->crbegin(); it != inventory->crend(); ++it, ++n) {
310 auto indx = inventory->crend() - it - 1;
311 TEST_EQ(*it, inv_vec.at(indx)); // Use bounds-check.
312 TEST_EQ(*it, inv_data[indx]);
313 }
314 TEST_EQ(n, inv_vec.size());
315
316 TEST_EQ(monster->color(), Color_Blue);
317
318 // Example of accessing a union:
319 TEST_EQ(monster->test_type(), Any_Monster); // First make sure which it is.
320 auto monster2 = reinterpret_cast<const Monster *>(monster->test());
321 TEST_NOTNULL(monster2);
322 TEST_EQ_STR(monster2->name()->c_str(), "Fred");
323
324 // Example of accessing a vector of strings:
325 auto vecofstrings = monster->testarrayofstring();
326 TEST_EQ(vecofstrings->size(), 4U);
327 TEST_EQ_STR(vecofstrings->Get(0)->c_str(), "bob");
328 TEST_EQ_STR(vecofstrings->Get(1)->c_str(), "fred");
329 if (pooled) {
330 // These should have pointer equality because of string pooling.
331 TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str());
332 TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str());
333 }
334
335 auto vecofstrings2 = monster->testarrayofstring2();
336 if (vecofstrings2) {
337 TEST_EQ(vecofstrings2->size(), 2U);
338 TEST_EQ_STR(vecofstrings2->Get(0)->c_str(), "jane");
339 TEST_EQ_STR(vecofstrings2->Get(1)->c_str(), "mary");
340 }
341
342 // Example of accessing a vector of tables:
343 auto vecoftables = monster->testarrayoftables();
344 TEST_EQ(vecoftables->size(), 3U);
345 for (auto it = vecoftables->begin(); it != vecoftables->end(); ++it) {
346 TEST_EQ(strlen(it->name()->c_str()) >= 4, true);
347 }
348 TEST_EQ_STR(vecoftables->Get(0)->name()->c_str(), "Barney");
349 TEST_EQ(vecoftables->Get(0)->hp(), 1000);
350 TEST_EQ_STR(vecoftables->Get(1)->name()->c_str(), "Fred");
351 TEST_EQ_STR(vecoftables->Get(2)->name()->c_str(), "Wilma");
352 TEST_NOTNULL(vecoftables->LookupByKey("Barney"));
353 TEST_NOTNULL(vecoftables->LookupByKey("Fred"));
354 TEST_NOTNULL(vecoftables->LookupByKey("Wilma"));
355
356 // Test accessing a vector of sorted structs
357 auto vecofstructs = monster->testarrayofsortedstruct();
358 if (vecofstructs) { // not filled in monster_test.bfbs
359 for (flatbuffers::uoffset_t i = 0; i < vecofstructs->size() - 1; i++) {
360 auto left = vecofstructs->Get(i);
361 auto right = vecofstructs->Get(i + 1);
362 TEST_EQ(true, (left->KeyCompareLessThan(right)));
363 }
364 TEST_NOTNULL(vecofstructs->LookupByKey(3));
365 TEST_EQ(static_cast<const Ability *>(nullptr),
366 vecofstructs->LookupByKey(5));
367 }
368
369 // Test nested FlatBuffers if available:
370 auto nested_buffer = monster->testnestedflatbuffer();
371 if (nested_buffer) {
372 // nested_buffer is a vector of bytes you can memcpy. However, if you
373 // actually want to access the nested data, this is a convenient
374 // accessor that directly gives you the root table:
375 auto nested_monster = monster->testnestedflatbuffer_nested_root();
376 TEST_EQ_STR(nested_monster->name()->c_str(), "NestedMonster");
377 }
378
379 // Test flexbuffer if available:
380 auto flex = monster->flex();
381 // flex is a vector of bytes you can memcpy etc.
382 TEST_EQ(flex->size(), 4); // Encoded FlexBuffer bytes.
383 // However, if you actually want to access the nested data, this is a
384 // convenient accessor that directly gives you the root value:
385 TEST_EQ(monster->flex_flexbuffer_root().AsInt16(), 1234);
386
387 // Test vector of enums:
388 auto colors = monster->vector_of_enums();
389 if (colors) {
390 TEST_EQ(colors->size(), 2);
391 TEST_EQ(colors->Get(0), Color_Blue);
392 TEST_EQ(colors->Get(1), Color_Green);
393 }
394
395 // Since Flatbuffers uses explicit mechanisms to override the default
396 // compiler alignment, double check that the compiler indeed obeys them:
397 // (Test consists of a short and byte):
398 TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
399 TEST_EQ(sizeof(Test), 4UL);
400
401 const flatbuffers::Vector<const Test *> *tests_array[] = {
402 monster->test4(),
403 monster->test5(),
404 };
405 for (size_t i = 0; i < sizeof(tests_array) / sizeof(tests_array[0]); ++i) {
406 auto tests = tests_array[i];
407 TEST_NOTNULL(tests);
408 auto test_0 = tests->Get(0);
409 auto test_1 = tests->Get(1);
410 TEST_EQ(test_0->a(), 10);
411 TEST_EQ(test_0->b(), 20);
412 TEST_EQ(test_1->a(), 30);
413 TEST_EQ(test_1->b(), 40);
414 for (auto it = tests->begin(); it != tests->end(); ++it) {
415 TEST_EQ(it->a() == 10 || it->a() == 30, true); // Just testing iterators.
416 }
417 }
418
419 // Checking for presence of fields:
420 TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_HP), true);
421 TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_MANA), false);
422
423 // Obtaining a buffer from a root:
424 TEST_EQ(GetBufferStartFromRootPointer(monster), flatbuf);
425 }
426
427 // Change a FlatBuffer in-place, after it has been constructed.
MutateFlatBuffersTest(uint8_t * flatbuf,std::size_t length)428 void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
429 // Get non-const pointer to root.
430 auto monster = GetMutableMonster(flatbuf);
431
432 // Each of these tests mutates, then tests, then set back to the original,
433 // so we can test that the buffer in the end still passes our original test.
434 auto hp_ok = monster->mutate_hp(10);
435 TEST_EQ(hp_ok, true); // Field was present.
436 TEST_EQ(monster->hp(), 10);
437 // Mutate to default value
438 auto hp_ok_default = monster->mutate_hp(100);
439 TEST_EQ(hp_ok_default, true); // Field was present.
440 TEST_EQ(monster->hp(), 100);
441 // Test that mutate to default above keeps field valid for further mutations
442 auto hp_ok_2 = monster->mutate_hp(20);
443 TEST_EQ(hp_ok_2, true);
444 TEST_EQ(monster->hp(), 20);
445 monster->mutate_hp(80);
446
447 // Monster originally at 150 mana (default value)
448 auto mana_default_ok = monster->mutate_mana(150); // Mutate to default value.
449 TEST_EQ(mana_default_ok,
450 true); // Mutation should succeed, because default value.
451 TEST_EQ(monster->mana(), 150);
452 auto mana_ok = monster->mutate_mana(10);
453 TEST_EQ(mana_ok, false); // Field was NOT present, because default value.
454 TEST_EQ(monster->mana(), 150);
455
456 // Mutate structs.
457 auto pos = monster->mutable_pos();
458 auto test3 = pos->mutable_test3(); // Struct inside a struct.
459 test3.mutate_a(50); // Struct fields never fail.
460 TEST_EQ(test3.a(), 50);
461 test3.mutate_a(10);
462
463 // Mutate vectors.
464 auto inventory = monster->mutable_inventory();
465 inventory->Mutate(9, 100);
466 TEST_EQ(inventory->Get(9), 100);
467 inventory->Mutate(9, 9);
468
469 auto tables = monster->mutable_testarrayoftables();
470 auto first = tables->GetMutableObject(0);
471 TEST_EQ(first->hp(), 1000);
472 first->mutate_hp(0);
473 TEST_EQ(first->hp(), 0);
474 first->mutate_hp(1000);
475
476 // Run the verifier and the regular test to make sure we didn't trample on
477 // anything.
478 AccessFlatBufferTest(flatbuf, length);
479 }
480
481 // Unpack a FlatBuffer into objects.
ObjectFlatBuffersTest(uint8_t * flatbuf)482 void ObjectFlatBuffersTest(uint8_t *flatbuf) {
483 // Optional: we can specify resolver and rehasher functions to turn hashed
484 // strings into object pointers and back, to implement remote references
485 // and such.
486 auto resolver = flatbuffers::resolver_function_t(
487 [](void **pointer_adr, flatbuffers::hash_value_t hash) {
488 (void)pointer_adr;
489 (void)hash;
490 // Don't actually do anything, leave variable null.
491 });
492 auto rehasher = flatbuffers::rehasher_function_t(
493 [](void *pointer) -> flatbuffers::hash_value_t {
494 (void)pointer;
495 return 0;
496 });
497
498 // Turn a buffer into C++ objects.
499 auto monster1 = UnPackMonster(flatbuf, &resolver);
500
501 // Re-serialize the data.
502 flatbuffers::FlatBufferBuilder fbb1;
503 fbb1.Finish(CreateMonster(fbb1, monster1.get(), &rehasher),
504 MonsterIdentifier());
505
506 // Unpack again, and re-serialize again.
507 auto monster2 = UnPackMonster(fbb1.GetBufferPointer(), &resolver);
508 flatbuffers::FlatBufferBuilder fbb2;
509 fbb2.Finish(CreateMonster(fbb2, monster2.get(), &rehasher),
510 MonsterIdentifier());
511
512 // Now we've gone full round-trip, the two buffers should match.
513 auto len1 = fbb1.GetSize();
514 auto len2 = fbb2.GetSize();
515 TEST_EQ(len1, len2);
516 TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(), len1), 0);
517
518 // Test it with the original buffer test to make sure all data survived.
519 AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false);
520
521 // Test accessing fields, similar to AccessFlatBufferTest above.
522 TEST_EQ(monster2->hp, 80);
523 TEST_EQ(monster2->mana, 150); // default
524 TEST_EQ_STR(monster2->name.c_str(), "MyMonster");
525
526 auto &pos = monster2->pos;
527 TEST_NOTNULL(pos);
528 TEST_EQ(pos->z(), 3);
529 TEST_EQ(pos->test3().a(), 10);
530 TEST_EQ(pos->test3().b(), 20);
531
532 auto &inventory = monster2->inventory;
533 TEST_EQ(inventory.size(), 10UL);
534 unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
535 for (auto it = inventory.begin(); it != inventory.end(); ++it)
536 TEST_EQ(*it, inv_data[it - inventory.begin()]);
537
538 TEST_EQ(monster2->color, Color_Blue);
539
540 auto monster3 = monster2->test.AsMonster();
541 TEST_NOTNULL(monster3);
542 TEST_EQ_STR(monster3->name.c_str(), "Fred");
543
544 auto &vecofstrings = monster2->testarrayofstring;
545 TEST_EQ(vecofstrings.size(), 4U);
546 TEST_EQ_STR(vecofstrings[0].c_str(), "bob");
547 TEST_EQ_STR(vecofstrings[1].c_str(), "fred");
548
549 auto &vecofstrings2 = monster2->testarrayofstring2;
550 TEST_EQ(vecofstrings2.size(), 2U);
551 TEST_EQ_STR(vecofstrings2[0].c_str(), "jane");
552 TEST_EQ_STR(vecofstrings2[1].c_str(), "mary");
553
554 auto &vecoftables = monster2->testarrayoftables;
555 TEST_EQ(vecoftables.size(), 3U);
556 TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney");
557 TEST_EQ(vecoftables[0]->hp, 1000);
558 TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred");
559 TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma");
560
561 auto &tests = monster2->test4;
562 TEST_EQ(tests[0].a(), 10);
563 TEST_EQ(tests[0].b(), 20);
564 TEST_EQ(tests[1].a(), 30);
565 TEST_EQ(tests[1].b(), 40);
566 }
567
568 // Prefix a FlatBuffer with a size field.
SizePrefixedTest()569 void SizePrefixedTest() {
570 // Create size prefixed buffer.
571 flatbuffers::FlatBufferBuilder fbb;
572 FinishSizePrefixedMonsterBuffer(
573 fbb, CreateMonster(fbb, 0, 200, 300, fbb.CreateString("bob")));
574
575 // Verify it.
576 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
577 TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier), true);
578
579 // Access it.
580 auto m = GetSizePrefixedMonster(fbb.GetBufferPointer());
581 TEST_EQ(m->mana(), 200);
582 TEST_EQ(m->hp(), 300);
583 TEST_EQ_STR(m->name()->c_str(), "bob");
584 }
585
TriviallyCopyableTest()586 void TriviallyCopyableTest() {
587 // clang-format off
588 #if __GNUG__ && __GNUC__ < 5
589 TEST_EQ(__has_trivial_copy(Vec3), true);
590 #else
591 #if __cplusplus >= 201103L
592 TEST_EQ(std::is_trivially_copyable<Vec3>::value, true);
593 #endif
594 #endif
595 // clang-format on
596 }
597
598 // Check stringify of an default enum value to json
JsonDefaultTest()599 void JsonDefaultTest() {
600 // load FlatBuffer schema (.fbs) from disk
601 std::string schemafile;
602 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
603 false, &schemafile),
604 true);
605 // parse schema first, so we can use it to parse the data after
606 flatbuffers::Parser parser;
607 auto include_test_path =
608 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
609 const char *include_directories[] = { test_data_path.c_str(),
610 include_test_path.c_str(), nullptr };
611
612 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
613 // create incomplete monster and store to json
614 parser.opts.output_default_scalars_in_json = true;
615 parser.opts.output_enum_identifiers = true;
616 flatbuffers::FlatBufferBuilder builder;
617 auto name = builder.CreateString("default_enum");
618 MonsterBuilder color_monster(builder);
619 color_monster.add_name(name);
620 FinishMonsterBuffer(builder, color_monster.Finish());
621 std::string jsongen;
622 auto result = GenerateText(parser, builder.GetBufferPointer(), &jsongen);
623 TEST_EQ(result, true);
624 // default value of the "color" field is Blue
625 TEST_EQ(std::string::npos != jsongen.find("color: \"Blue\""), true);
626 // default value of the "testf" field is 3.14159
627 TEST_EQ(std::string::npos != jsongen.find("testf: 3.14159"), true);
628 }
629
JsonEnumsTest()630 void JsonEnumsTest() {
631 // load FlatBuffer schema (.fbs) from disk
632 std::string schemafile;
633 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
634 false, &schemafile),
635 true);
636 // parse schema first, so we can use it to parse the data after
637 flatbuffers::Parser parser;
638 auto include_test_path =
639 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
640 const char *include_directories[] = { test_data_path.c_str(),
641 include_test_path.c_str(), nullptr };
642 parser.opts.output_enum_identifiers = true;
643 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
644 flatbuffers::FlatBufferBuilder builder;
645 auto name = builder.CreateString("bitflag_enum");
646 MonsterBuilder color_monster(builder);
647 color_monster.add_name(name);
648 color_monster.add_color(Color(Color_Blue | Color_Red));
649 FinishMonsterBuffer(builder, color_monster.Finish());
650 std::string jsongen;
651 auto result = GenerateText(parser, builder.GetBufferPointer(), &jsongen);
652 TEST_EQ(result, true);
653 TEST_EQ(std::string::npos != jsongen.find("color: \"Red Blue\""), true);
654 // Test forward compatibility with 'output_enum_identifiers = true'.
655 // Current Color doesn't have '(1u << 2)' field, let's add it.
656 builder.Clear();
657 std::string future_json;
658 auto future_name = builder.CreateString("future bitflag_enum");
659 MonsterBuilder future_color(builder);
660 future_color.add_name(future_name);
661 future_color.add_color(
662 static_cast<Color>((1u << 2) | Color_Blue | Color_Red));
663 FinishMonsterBuffer(builder, future_color.Finish());
664 result = GenerateText(parser, builder.GetBufferPointer(), &future_json);
665 TEST_EQ(result, true);
666 TEST_EQ(std::string::npos != future_json.find("color: 13"), true);
667 }
668
669 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
670 // The IEEE-754 quiet_NaN is not simple binary constant.
671 // All binary NaN bit strings have all the bits of the biased exponent field E
672 // set to 1. A quiet NaN bit string should be encoded with the first bit d[1]
673 // of the trailing significand field T being 1 (d[0] is implicit bit).
674 // It is assumed that endianness of floating-point is same as integer.
is_quiet_nan_impl(T v)675 template<typename T, typename U, U qnan_base> bool is_quiet_nan_impl(T v) {
676 static_assert(sizeof(T) == sizeof(U), "unexpected");
677 U b = 0;
678 std::memcpy(&b, &v, sizeof(T));
679 return ((b & qnan_base) == qnan_base);
680 }
is_quiet_nan(float v)681 static bool is_quiet_nan(float v) {
682 return is_quiet_nan_impl<float, uint32_t, 0x7FC00000u>(v);
683 }
is_quiet_nan(double v)684 static bool is_quiet_nan(double v) {
685 return is_quiet_nan_impl<double, uint64_t, 0x7FF8000000000000ul>(v);
686 }
687
TestMonsterExtraFloats()688 void TestMonsterExtraFloats() {
689 TEST_EQ(is_quiet_nan(1.0), false);
690 TEST_EQ(is_quiet_nan(infinityd), false);
691 TEST_EQ(is_quiet_nan(-infinityf), false);
692 TEST_EQ(is_quiet_nan(std::numeric_limits<float>::quiet_NaN()), true);
693 TEST_EQ(is_quiet_nan(std::numeric_limits<double>::quiet_NaN()), true);
694
695 using namespace flatbuffers;
696 using namespace MyGame;
697 // Load FlatBuffer schema (.fbs) from disk.
698 std::string schemafile;
699 TEST_EQ(LoadFile((test_data_path + "monster_extra.fbs").c_str(), false,
700 &schemafile),
701 true);
702 // Parse schema first, so we can use it to parse the data after.
703 Parser parser;
704 auto include_test_path = ConCatPathFileName(test_data_path, "include_test");
705 const char *include_directories[] = { test_data_path.c_str(),
706 include_test_path.c_str(), nullptr };
707 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
708 // Create empty extra and store to json.
709 parser.opts.output_default_scalars_in_json = true;
710 parser.opts.output_enum_identifiers = true;
711 FlatBufferBuilder builder;
712 const auto def_root = MonsterExtraBuilder(builder).Finish();
713 FinishMonsterExtraBuffer(builder, def_root);
714 const auto def_obj = builder.GetBufferPointer();
715 const auto def_extra = GetMonsterExtra(def_obj);
716 TEST_NOTNULL(def_extra);
717 TEST_EQ(is_quiet_nan(def_extra->f0()), true);
718 TEST_EQ(is_quiet_nan(def_extra->f1()), true);
719 TEST_EQ(def_extra->f2(), +infinityf);
720 TEST_EQ(def_extra->f3(), -infinityf);
721 TEST_EQ(is_quiet_nan(def_extra->d0()), true);
722 TEST_EQ(is_quiet_nan(def_extra->d1()), true);
723 TEST_EQ(def_extra->d2(), +infinityd);
724 TEST_EQ(def_extra->d3(), -infinityd);
725 std::string jsongen;
726 auto result = GenerateText(parser, def_obj, &jsongen);
727 TEST_EQ(result, true);
728 // Check expected default values.
729 TEST_EQ(std::string::npos != jsongen.find("f0: nan"), true);
730 TEST_EQ(std::string::npos != jsongen.find("f1: nan"), true);
731 TEST_EQ(std::string::npos != jsongen.find("f2: inf"), true);
732 TEST_EQ(std::string::npos != jsongen.find("f3: -inf"), true);
733 TEST_EQ(std::string::npos != jsongen.find("d0: nan"), true);
734 TEST_EQ(std::string::npos != jsongen.find("d1: nan"), true);
735 TEST_EQ(std::string::npos != jsongen.find("d2: inf"), true);
736 TEST_EQ(std::string::npos != jsongen.find("d3: -inf"), true);
737 // Parse 'mosterdata_extra.json'.
738 const auto extra_base = test_data_path + "monsterdata_extra";
739 jsongen = "";
740 TEST_EQ(LoadFile((extra_base + ".json").c_str(), false, &jsongen), true);
741 TEST_EQ(parser.Parse(jsongen.c_str()), true);
742 const auto test_file = parser.builder_.GetBufferPointer();
743 const auto test_size = parser.builder_.GetSize();
744 Verifier verifier(test_file, test_size);
745 TEST_ASSERT(VerifyMonsterExtraBuffer(verifier));
746 const auto extra = GetMonsterExtra(test_file);
747 TEST_NOTNULL(extra);
748 TEST_EQ(is_quiet_nan(extra->f0()), true);
749 TEST_EQ(is_quiet_nan(extra->f1()), true);
750 TEST_EQ(extra->f2(), +infinityf);
751 TEST_EQ(extra->f3(), -infinityf);
752 TEST_EQ(is_quiet_nan(extra->d0()), true);
753 TEST_EQ(extra->d1(), +infinityd);
754 TEST_EQ(extra->d2(), -infinityd);
755 TEST_EQ(is_quiet_nan(extra->d3()), true);
756 TEST_NOTNULL(extra->fvec());
757 TEST_EQ(extra->fvec()->size(), 4);
758 TEST_EQ(extra->fvec()->Get(0), 1.0f);
759 TEST_EQ(extra->fvec()->Get(1), -infinityf);
760 TEST_EQ(extra->fvec()->Get(2), +infinityf);
761 TEST_EQ(is_quiet_nan(extra->fvec()->Get(3)), true);
762 TEST_NOTNULL(extra->dvec());
763 TEST_EQ(extra->dvec()->size(), 4);
764 TEST_EQ(extra->dvec()->Get(0), 2.0);
765 TEST_EQ(extra->dvec()->Get(1), +infinityd);
766 TEST_EQ(extra->dvec()->Get(2), -infinityd);
767 TEST_EQ(is_quiet_nan(extra->dvec()->Get(3)), true);
768 }
769 #else
TestMonsterExtraFloats()770 void TestMonsterExtraFloats() {}
771 #endif
772
773 // example of parsing text straight into a buffer, and generating
774 // text back from it:
ParseAndGenerateTextTest(bool binary)775 void ParseAndGenerateTextTest(bool binary) {
776 // load FlatBuffer schema (.fbs) and JSON from disk
777 std::string schemafile;
778 std::string jsonfile;
779 TEST_EQ(flatbuffers::LoadFile(
780 (test_data_path + "monster_test." + (binary ? "bfbs" : "fbs"))
781 .c_str(),
782 binary, &schemafile),
783 true);
784 TEST_EQ(flatbuffers::LoadFile(
785 (test_data_path + "monsterdata_test.golden").c_str(), false,
786 &jsonfile),
787 true);
788
789 auto include_test_path =
790 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
791 const char *include_directories[] = { test_data_path.c_str(),
792 include_test_path.c_str(), nullptr };
793
794 // parse schema first, so we can use it to parse the data after
795 flatbuffers::Parser parser;
796 if (binary) {
797 flatbuffers::Verifier verifier(
798 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
799 schemafile.size());
800 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
801 // auto schema = reflection::GetSchema(schemafile.c_str());
802 TEST_EQ(parser.Deserialize((const uint8_t *)schemafile.c_str(),
803 schemafile.size()),
804 true);
805 } else {
806 TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
807 }
808 TEST_EQ(parser.Parse(jsonfile.c_str(), include_directories), true);
809
810 // here, parser.builder_ contains a binary buffer that is the parsed data.
811
812 // First, verify it, just in case:
813 flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
814 parser.builder_.GetSize());
815 TEST_EQ(VerifyMonsterBuffer(verifier), true);
816
817 AccessFlatBufferTest(parser.builder_.GetBufferPointer(),
818 parser.builder_.GetSize(), false);
819
820 // to ensure it is correct, we now generate text back from the binary,
821 // and compare the two:
822 std::string jsongen;
823 auto result =
824 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
825 TEST_EQ(result, true);
826 TEST_EQ_STR(jsongen.c_str(), jsonfile.c_str());
827
828 // We can also do the above using the convenient Registry that knows about
829 // a set of file_identifiers mapped to schemas.
830 flatbuffers::Registry registry;
831 // Make sure schemas can find their includes.
832 registry.AddIncludeDirectory(test_data_path.c_str());
833 registry.AddIncludeDirectory(include_test_path.c_str());
834 // Call this with many schemas if possible.
835 registry.Register(MonsterIdentifier(),
836 (test_data_path + "monster_test.fbs").c_str());
837 // Now we got this set up, we can parse by just specifying the identifier,
838 // the correct schema will be loaded on the fly:
839 auto buf = registry.TextToFlatBuffer(jsonfile.c_str(), MonsterIdentifier());
840 // If this fails, check registry.lasterror_.
841 TEST_NOTNULL(buf.data());
842 // Test the buffer, to be sure:
843 AccessFlatBufferTest(buf.data(), buf.size(), false);
844 // We can use the registry to turn this back into text, in this case it
845 // will get the file_identifier from the binary:
846 std::string text;
847 auto ok = registry.FlatBufferToText(buf.data(), buf.size(), &text);
848 // If this fails, check registry.lasterror_.
849 TEST_EQ(ok, true);
850 TEST_EQ_STR(text.c_str(), jsonfile.c_str());
851
852 // Generate text for UTF-8 strings without escapes.
853 std::string jsonfile_utf8;
854 TEST_EQ(flatbuffers::LoadFile((test_data_path + "unicode_test.json").c_str(),
855 false, &jsonfile_utf8),
856 true);
857 TEST_EQ(parser.Parse(jsonfile_utf8.c_str(), include_directories), true);
858 // To ensure it is correct, generate utf-8 text back from the binary.
859 std::string jsongen_utf8;
860 // request natural printing for utf-8 strings
861 parser.opts.natural_utf8 = true;
862 parser.opts.strict_json = true;
863 TEST_EQ(
864 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen_utf8),
865 true);
866 TEST_EQ_STR(jsongen_utf8.c_str(), jsonfile_utf8.c_str());
867 }
868
ReflectionTest(uint8_t * flatbuf,size_t length)869 void ReflectionTest(uint8_t *flatbuf, size_t length) {
870 // Load a binary schema.
871 std::string bfbsfile;
872 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.bfbs").c_str(),
873 true, &bfbsfile),
874 true);
875
876 // Verify it, just in case:
877 flatbuffers::Verifier verifier(
878 reinterpret_cast<const uint8_t *>(bfbsfile.c_str()), bfbsfile.length());
879 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
880
881 // Make sure the schema is what we expect it to be.
882 auto &schema = *reflection::GetSchema(bfbsfile.c_str());
883 auto root_table = schema.root_table();
884 TEST_EQ_STR(root_table->name()->c_str(), "MyGame.Example.Monster");
885 auto fields = root_table->fields();
886 auto hp_field_ptr = fields->LookupByKey("hp");
887 TEST_NOTNULL(hp_field_ptr);
888 auto &hp_field = *hp_field_ptr;
889 TEST_EQ_STR(hp_field.name()->c_str(), "hp");
890 TEST_EQ(hp_field.id(), 2);
891 TEST_EQ(hp_field.type()->base_type(), reflection::Short);
892 auto friendly_field_ptr = fields->LookupByKey("friendly");
893 TEST_NOTNULL(friendly_field_ptr);
894 TEST_NOTNULL(friendly_field_ptr->attributes());
895 TEST_NOTNULL(friendly_field_ptr->attributes()->LookupByKey("priority"));
896
897 // Make sure the table index is what we expect it to be.
898 auto pos_field_ptr = fields->LookupByKey("pos");
899 TEST_NOTNULL(pos_field_ptr);
900 TEST_EQ(pos_field_ptr->type()->base_type(), reflection::Obj);
901 auto pos_table_ptr = schema.objects()->Get(pos_field_ptr->type()->index());
902 TEST_NOTNULL(pos_table_ptr);
903 TEST_EQ_STR(pos_table_ptr->name()->c_str(), "MyGame.Example.Vec3");
904
905 // Now use it to dynamically access a buffer.
906 auto &root = *flatbuffers::GetAnyRoot(flatbuf);
907
908 // Verify the buffer first using reflection based verification
909 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), flatbuf, length),
910 true);
911
912 auto hp = flatbuffers::GetFieldI<uint16_t>(root, hp_field);
913 TEST_EQ(hp, 80);
914
915 // Rather than needing to know the type, we can also get the value of
916 // any field as an int64_t/double/string, regardless of what it actually is.
917 auto hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
918 TEST_EQ(hp_int64, 80);
919 auto hp_double = flatbuffers::GetAnyFieldF(root, hp_field);
920 TEST_EQ(hp_double, 80.0);
921 auto hp_string = flatbuffers::GetAnyFieldS(root, hp_field, &schema);
922 TEST_EQ_STR(hp_string.c_str(), "80");
923
924 // Get struct field through reflection
925 auto pos_struct = flatbuffers::GetFieldStruct(root, *pos_field_ptr);
926 TEST_NOTNULL(pos_struct);
927 TEST_EQ(flatbuffers::GetAnyFieldF(*pos_struct,
928 *pos_table_ptr->fields()->LookupByKey("z")),
929 3.0f);
930
931 auto test3_field = pos_table_ptr->fields()->LookupByKey("test3");
932 auto test3_struct = flatbuffers::GetFieldStruct(*pos_struct, *test3_field);
933 TEST_NOTNULL(test3_struct);
934 auto test3_object = schema.objects()->Get(test3_field->type()->index());
935
936 TEST_EQ(flatbuffers::GetAnyFieldF(*test3_struct,
937 *test3_object->fields()->LookupByKey("a")),
938 10);
939
940 // We can also modify it.
941 flatbuffers::SetField<uint16_t>(&root, hp_field, 200);
942 hp = flatbuffers::GetFieldI<uint16_t>(root, hp_field);
943 TEST_EQ(hp, 200);
944
945 // We can also set fields generically:
946 flatbuffers::SetAnyFieldI(&root, hp_field, 300);
947 hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
948 TEST_EQ(hp_int64, 300);
949 flatbuffers::SetAnyFieldF(&root, hp_field, 300.5);
950 hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
951 TEST_EQ(hp_int64, 300);
952 flatbuffers::SetAnyFieldS(&root, hp_field, "300");
953 hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
954 TEST_EQ(hp_int64, 300);
955
956 // Test buffer is valid after the modifications
957 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), flatbuf, length),
958 true);
959
960 // Reset it, for further tests.
961 flatbuffers::SetField<uint16_t>(&root, hp_field, 80);
962
963 // More advanced functionality: changing the size of items in-line!
964 // First we put the FlatBuffer inside an std::vector.
965 std::vector<uint8_t> resizingbuf(flatbuf, flatbuf + length);
966 // Find the field we want to modify.
967 auto &name_field = *fields->LookupByKey("name");
968 // Get the root.
969 // This time we wrap the result from GetAnyRoot in a smartpointer that
970 // will keep rroot valid as resizingbuf resizes.
971 auto rroot = flatbuffers::piv(
972 flatbuffers::GetAnyRoot(flatbuffers::vector_data(resizingbuf)),
973 resizingbuf);
974 SetString(schema, "totally new string", GetFieldS(**rroot, name_field),
975 &resizingbuf);
976 // Here resizingbuf has changed, but rroot is still valid.
977 TEST_EQ_STR(GetFieldS(**rroot, name_field)->c_str(), "totally new string");
978 // Now lets extend a vector by 100 elements (10 -> 110).
979 auto &inventory_field = *fields->LookupByKey("inventory");
980 auto rinventory = flatbuffers::piv(
981 flatbuffers::GetFieldV<uint8_t>(**rroot, inventory_field), resizingbuf);
982 flatbuffers::ResizeVector<uint8_t>(schema, 110, 50, *rinventory,
983 &resizingbuf);
984 // rinventory still valid, so lets read from it.
985 TEST_EQ(rinventory->Get(10), 50);
986
987 // For reflection uses not covered already, there is a more powerful way:
988 // we can simply generate whatever object we want to add/modify in a
989 // FlatBuffer of its own, then add that to an existing FlatBuffer:
990 // As an example, let's add a string to an array of strings.
991 // First, find our field:
992 auto &testarrayofstring_field = *fields->LookupByKey("testarrayofstring");
993 // Find the vector value:
994 auto rtestarrayofstring = flatbuffers::piv(
995 flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>(
996 **rroot, testarrayofstring_field),
997 resizingbuf);
998 // It's a vector of 2 strings, to which we add one more, initialized to
999 // offset 0.
1000 flatbuffers::ResizeVector<flatbuffers::Offset<flatbuffers::String>>(
1001 schema, 3, 0, *rtestarrayofstring, &resizingbuf);
1002 // Here we just create a buffer that contans a single string, but this
1003 // could also be any complex set of tables and other values.
1004 flatbuffers::FlatBufferBuilder stringfbb;
1005 stringfbb.Finish(stringfbb.CreateString("hank"));
1006 // Add the contents of it to our existing FlatBuffer.
1007 // We do this last, so the pointer doesn't get invalidated (since it is
1008 // at the end of the buffer):
1009 auto string_ptr = flatbuffers::AddFlatBuffer(
1010 resizingbuf, stringfbb.GetBufferPointer(), stringfbb.GetSize());
1011 // Finally, set the new value in the vector.
1012 rtestarrayofstring->MutateOffset(2, string_ptr);
1013 TEST_EQ_STR(rtestarrayofstring->Get(0)->c_str(), "bob");
1014 TEST_EQ_STR(rtestarrayofstring->Get(2)->c_str(), "hank");
1015 // Test integrity of all resize operations above.
1016 flatbuffers::Verifier resize_verifier(
1017 reinterpret_cast<const uint8_t *>(flatbuffers::vector_data(resizingbuf)),
1018 resizingbuf.size());
1019 TEST_EQ(VerifyMonsterBuffer(resize_verifier), true);
1020
1021 // Test buffer is valid using reflection as well
1022 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(),
1023 flatbuffers::vector_data(resizingbuf),
1024 resizingbuf.size()),
1025 true);
1026
1027 // As an additional test, also set it on the name field.
1028 // Note: unlike the name change above, this just overwrites the offset,
1029 // rather than changing the string in-place.
1030 SetFieldT(*rroot, name_field, string_ptr);
1031 TEST_EQ_STR(GetFieldS(**rroot, name_field)->c_str(), "hank");
1032
1033 // Using reflection, rather than mutating binary FlatBuffers, we can also copy
1034 // tables and other things out of other FlatBuffers into a FlatBufferBuilder,
1035 // either part or whole.
1036 flatbuffers::FlatBufferBuilder fbb;
1037 auto root_offset = flatbuffers::CopyTable(
1038 fbb, schema, *root_table, *flatbuffers::GetAnyRoot(flatbuf), true);
1039 fbb.Finish(root_offset, MonsterIdentifier());
1040 // Test that it was copied correctly:
1041 AccessFlatBufferTest(fbb.GetBufferPointer(), fbb.GetSize());
1042
1043 // Test buffer is valid using reflection as well
1044 TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(),
1045 fbb.GetBufferPointer(), fbb.GetSize()),
1046 true);
1047 }
1048
MiniReflectFlatBuffersTest(uint8_t * flatbuf)1049 void MiniReflectFlatBuffersTest(uint8_t *flatbuf) {
1050 auto s =
1051 flatbuffers::FlatBufferToString(flatbuf, Monster::MiniReflectTypeTable());
1052 TEST_EQ_STR(
1053 s.c_str(),
1054 "{ "
1055 "pos: { x: 1.0, y: 2.0, z: 3.0, test1: 0.0, test2: Red, test3: "
1056 "{ a: 10, b: 20 } }, "
1057 "hp: 80, "
1058 "name: \"MyMonster\", "
1059 "inventory: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "
1060 "test_type: Monster, "
1061 "test: { name: \"Fred\" }, "
1062 "test4: [ { a: 10, b: 20 }, { a: 30, b: 40 } ], "
1063 "testarrayofstring: [ \"bob\", \"fred\", \"bob\", \"fred\" ], "
1064 "testarrayoftables: [ { hp: 1000, name: \"Barney\" }, { name: \"Fred\" "
1065 "}, "
1066 "{ name: \"Wilma\" } ], "
1067 // TODO(wvo): should really print this nested buffer correctly.
1068 "testnestedflatbuffer: [ 20, 0, 0, 0, 77, 79, 78, 83, 12, 0, 12, 0, 0, "
1069 "0, "
1070 "4, 0, 6, 0, 8, 0, 12, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 13, 0, 0, 0, 78, "
1071 "101, 115, 116, 101, 100, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0 ], "
1072 "testarrayofstring2: [ \"jane\", \"mary\" ], "
1073 "testarrayofsortedstruct: [ { id: 1, distance: 10 }, "
1074 "{ id: 2, distance: 20 }, { id: 3, distance: 30 }, "
1075 "{ id: 4, distance: 40 } ], "
1076 "flex: [ 210, 4, 5, 2 ], "
1077 "test5: [ { a: 10, b: 20 }, { a: 30, b: 40 } ], "
1078 "vector_of_enums: [ Blue, Green ] "
1079 "}");
1080
1081 Test test(16, 32);
1082 Vec3 vec(1, 2, 3, 1.5, Color_Red, test);
1083 flatbuffers::FlatBufferBuilder vec_builder;
1084 vec_builder.Finish(vec_builder.CreateStruct(vec));
1085 auto vec_buffer = vec_builder.Release();
1086 auto vec_str = flatbuffers::FlatBufferToString(vec_buffer.data(),
1087 Vec3::MiniReflectTypeTable());
1088 TEST_EQ_STR(vec_str.c_str(),
1089 "{ x: 1.0, y: 2.0, z: 3.0, test1: 1.5, test2: Red, test3: { a: "
1090 "16, b: 32 } }");
1091 }
1092
1093 // Parse a .proto schema, output as .fbs
ParseProtoTest()1094 void ParseProtoTest() {
1095 // load the .proto and the golden file from disk
1096 std::string protofile;
1097 std::string goldenfile;
1098 std::string goldenunionfile;
1099 TEST_EQ(
1100 flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(),
1101 false, &protofile),
1102 true);
1103 TEST_EQ(
1104 flatbuffers::LoadFile((test_data_path + "prototest/test.golden").c_str(),
1105 false, &goldenfile),
1106 true);
1107 TEST_EQ(flatbuffers::LoadFile(
1108 (test_data_path + "prototest/test_union.golden").c_str(), false,
1109 &goldenunionfile),
1110 true);
1111
1112 flatbuffers::IDLOptions opts;
1113 opts.include_dependence_headers = false;
1114 opts.proto_mode = true;
1115
1116 // Parse proto.
1117 flatbuffers::Parser parser(opts);
1118 auto protopath = test_data_path + "prototest/";
1119 const char *include_directories[] = { protopath.c_str(), nullptr };
1120 TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true);
1121
1122 // Generate fbs.
1123 auto fbs = flatbuffers::GenerateFBS(parser, "test");
1124
1125 // Ensure generated file is parsable.
1126 flatbuffers::Parser parser2;
1127 TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
1128 TEST_EQ_STR(fbs.c_str(), goldenfile.c_str());
1129
1130 // Parse proto with --oneof-union option.
1131 opts.proto_oneof_union = true;
1132 flatbuffers::Parser parser3(opts);
1133 TEST_EQ(parser3.Parse(protofile.c_str(), include_directories), true);
1134
1135 // Generate fbs.
1136 auto fbs_union = flatbuffers::GenerateFBS(parser3, "test");
1137
1138 // Ensure generated file is parsable.
1139 flatbuffers::Parser parser4;
1140 TEST_EQ(parser4.Parse(fbs_union.c_str(), nullptr), true);
1141 TEST_EQ_STR(fbs_union.c_str(), goldenunionfile.c_str());
1142 }
1143
1144 // Parse a .proto schema, output as .fbs
ParseProtoTestWithSuffix()1145 void ParseProtoTestWithSuffix() {
1146 // load the .proto and the golden file from disk
1147 std::string protofile;
1148 std::string goldenfile;
1149 std::string goldenunionfile;
1150 TEST_EQ(
1151 flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(),
1152 false, &protofile),
1153 true);
1154 TEST_EQ(flatbuffers::LoadFile(
1155 (test_data_path + "prototest/test_suffix.golden").c_str(), false,
1156 &goldenfile),
1157 true);
1158 TEST_EQ(flatbuffers::LoadFile(
1159 (test_data_path + "prototest/test_union_suffix.golden").c_str(),
1160 false, &goldenunionfile),
1161 true);
1162
1163 flatbuffers::IDLOptions opts;
1164 opts.include_dependence_headers = false;
1165 opts.proto_mode = true;
1166 opts.proto_namespace_suffix = "test_namespace_suffix";
1167
1168 // Parse proto.
1169 flatbuffers::Parser parser(opts);
1170 auto protopath = test_data_path + "prototest/";
1171 const char *include_directories[] = { protopath.c_str(), nullptr };
1172 TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true);
1173
1174 // Generate fbs.
1175 auto fbs = flatbuffers::GenerateFBS(parser, "test");
1176
1177 // Ensure generated file is parsable.
1178 flatbuffers::Parser parser2;
1179 TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
1180 TEST_EQ_STR(fbs.c_str(), goldenfile.c_str());
1181
1182 // Parse proto with --oneof-union option.
1183 opts.proto_oneof_union = true;
1184 flatbuffers::Parser parser3(opts);
1185 TEST_EQ(parser3.Parse(protofile.c_str(), include_directories), true);
1186
1187 // Generate fbs.
1188 auto fbs_union = flatbuffers::GenerateFBS(parser3, "test");
1189
1190 // Ensure generated file is parsable.
1191 flatbuffers::Parser parser4;
1192 TEST_EQ(parser4.Parse(fbs_union.c_str(), nullptr), true);
1193 TEST_EQ_STR(fbs_union.c_str(), goldenunionfile.c_str());
1194 }
1195
1196 // Parse a .proto schema, output as .fbs
ParseProtoTestWithIncludes()1197 void ParseProtoTestWithIncludes() {
1198 // load the .proto and the golden file from disk
1199 std::string protofile;
1200 std::string goldenfile;
1201 std::string goldenunionfile;
1202 std::string importprotofile;
1203 TEST_EQ(
1204 flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(),
1205 false, &protofile),
1206 true);
1207 TEST_EQ(flatbuffers::LoadFile(
1208 (test_data_path + "prototest/imported.proto").c_str(), false,
1209 &importprotofile),
1210 true);
1211 TEST_EQ(flatbuffers::LoadFile(
1212 (test_data_path + "prototest/test_include.golden").c_str(), false,
1213 &goldenfile),
1214 true);
1215 TEST_EQ(flatbuffers::LoadFile(
1216 (test_data_path + "prototest/test_union_include.golden").c_str(),
1217 false, &goldenunionfile),
1218 true);
1219
1220 flatbuffers::IDLOptions opts;
1221 opts.include_dependence_headers = true;
1222 opts.proto_mode = true;
1223
1224 // Parse proto.
1225 flatbuffers::Parser parser(opts);
1226 auto protopath = test_data_path + "prototest/";
1227 const char *include_directories[] = { protopath.c_str(), nullptr };
1228 TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true);
1229
1230 // Generate fbs.
1231 auto fbs = flatbuffers::GenerateFBS(parser, "test");
1232
1233 // Generate fbs from import.proto
1234 flatbuffers::Parser import_parser(opts);
1235 TEST_EQ(import_parser.Parse(importprotofile.c_str(), include_directories),
1236 true);
1237 auto import_fbs = flatbuffers::GenerateFBS(import_parser, "test");
1238
1239 // Ensure generated file is parsable.
1240 flatbuffers::Parser parser2;
1241 TEST_EQ(
1242 parser2.Parse(import_fbs.c_str(), include_directories, "imported.fbs"),
1243 true);
1244 TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
1245 TEST_EQ_STR(fbs.c_str(), goldenfile.c_str());
1246
1247 // Parse proto with --oneof-union option.
1248 opts.proto_oneof_union = true;
1249 flatbuffers::Parser parser3(opts);
1250 TEST_EQ(parser3.Parse(protofile.c_str(), include_directories), true);
1251
1252 // Generate fbs.
1253 auto fbs_union = flatbuffers::GenerateFBS(parser3, "test");
1254
1255 // Ensure generated file is parsable.
1256 flatbuffers::Parser parser4;
1257 TEST_EQ(parser4.Parse(import_fbs.c_str(), nullptr, "imported.fbs"), true);
1258 TEST_EQ(parser4.Parse(fbs_union.c_str(), nullptr), true);
1259 TEST_EQ_STR(fbs_union.c_str(), goldenunionfile.c_str());
1260 }
1261
1262 template<typename T>
CompareTableFieldValue(flatbuffers::Table * table,flatbuffers::voffset_t voffset,T val)1263 void CompareTableFieldValue(flatbuffers::Table *table,
1264 flatbuffers::voffset_t voffset, T val) {
1265 T read = table->GetField(voffset, static_cast<T>(0));
1266 TEST_EQ(read, val);
1267 }
1268
1269 // Low level stress/fuzz test: serialize/deserialize a variety of
1270 // different kinds of data in different combinations
FuzzTest1()1271 void FuzzTest1() {
1272 // Values we're testing against: chosen to ensure no bits get chopped
1273 // off anywhere, and also be different from eachother.
1274 const uint8_t bool_val = true;
1275 const int8_t char_val = -127; // 0x81
1276 const uint8_t uchar_val = 0xFF;
1277 const int16_t short_val = -32222; // 0x8222;
1278 const uint16_t ushort_val = 0xFEEE;
1279 const int32_t int_val = 0x83333333;
1280 const uint32_t uint_val = 0xFDDDDDDD;
1281 const int64_t long_val = 0x8444444444444444LL;
1282 const uint64_t ulong_val = 0xFCCCCCCCCCCCCCCCULL;
1283 const float float_val = 3.14159f;
1284 const double double_val = 3.14159265359;
1285
1286 const int test_values_max = 11;
1287 const flatbuffers::voffset_t fields_per_object = 4;
1288 const int num_fuzz_objects = 10000; // The higher, the more thorough :)
1289
1290 flatbuffers::FlatBufferBuilder builder;
1291
1292 lcg_reset(); // Keep it deterministic.
1293
1294 flatbuffers::uoffset_t objects[num_fuzz_objects];
1295
1296 // Generate num_fuzz_objects random objects each consisting of
1297 // fields_per_object fields, each of a random type.
1298 for (int i = 0; i < num_fuzz_objects; i++) {
1299 auto start = builder.StartTable();
1300 for (flatbuffers::voffset_t f = 0; f < fields_per_object; f++) {
1301 int choice = lcg_rand() % test_values_max;
1302 auto off = flatbuffers::FieldIndexToOffset(f);
1303 switch (choice) {
1304 case 0: builder.AddElement<uint8_t>(off, bool_val, 0); break;
1305 case 1: builder.AddElement<int8_t>(off, char_val, 0); break;
1306 case 2: builder.AddElement<uint8_t>(off, uchar_val, 0); break;
1307 case 3: builder.AddElement<int16_t>(off, short_val, 0); break;
1308 case 4: builder.AddElement<uint16_t>(off, ushort_val, 0); break;
1309 case 5: builder.AddElement<int32_t>(off, int_val, 0); break;
1310 case 6: builder.AddElement<uint32_t>(off, uint_val, 0); break;
1311 case 7: builder.AddElement<int64_t>(off, long_val, 0); break;
1312 case 8: builder.AddElement<uint64_t>(off, ulong_val, 0); break;
1313 case 9: builder.AddElement<float>(off, float_val, 0); break;
1314 case 10: builder.AddElement<double>(off, double_val, 0); break;
1315 }
1316 }
1317 objects[i] = builder.EndTable(start);
1318 }
1319 builder.PreAlign<flatbuffers::largest_scalar_t>(0); // Align whole buffer.
1320
1321 lcg_reset(); // Reset.
1322
1323 uint8_t *eob = builder.GetCurrentBufferPointer() + builder.GetSize();
1324
1325 // Test that all objects we generated are readable and return the
1326 // expected values. We generate random objects in the same order
1327 // so this is deterministic.
1328 for (int i = 0; i < num_fuzz_objects; i++) {
1329 auto table = reinterpret_cast<flatbuffers::Table *>(eob - objects[i]);
1330 for (flatbuffers::voffset_t f = 0; f < fields_per_object; f++) {
1331 int choice = lcg_rand() % test_values_max;
1332 flatbuffers::voffset_t off = flatbuffers::FieldIndexToOffset(f);
1333 switch (choice) {
1334 case 0: CompareTableFieldValue(table, off, bool_val); break;
1335 case 1: CompareTableFieldValue(table, off, char_val); break;
1336 case 2: CompareTableFieldValue(table, off, uchar_val); break;
1337 case 3: CompareTableFieldValue(table, off, short_val); break;
1338 case 4: CompareTableFieldValue(table, off, ushort_val); break;
1339 case 5: CompareTableFieldValue(table, off, int_val); break;
1340 case 6: CompareTableFieldValue(table, off, uint_val); break;
1341 case 7: CompareTableFieldValue(table, off, long_val); break;
1342 case 8: CompareTableFieldValue(table, off, ulong_val); break;
1343 case 9: CompareTableFieldValue(table, off, float_val); break;
1344 case 10: CompareTableFieldValue(table, off, double_val); break;
1345 }
1346 }
1347 }
1348 }
1349
1350 // High level stress/fuzz test: generate a big schema and
1351 // matching json data in random combinations, then parse both,
1352 // generate json back from the binary, and compare with the original.
FuzzTest2()1353 void FuzzTest2() {
1354 lcg_reset(); // Keep it deterministic.
1355
1356 const int num_definitions = 30;
1357 const int num_struct_definitions = 5; // Subset of num_definitions.
1358 const int fields_per_definition = 15;
1359 const int instances_per_definition = 5;
1360 const int deprecation_rate = 10; // 1 in deprecation_rate fields will
1361 // be deprecated.
1362
1363 std::string schema = "namespace test;\n\n";
1364
1365 struct RndDef {
1366 std::string instances[instances_per_definition];
1367
1368 // Since we're generating schema and corresponding data in tandem,
1369 // this convenience function adds strings to both at once.
1370 static void Add(RndDef (&definitions_l)[num_definitions],
1371 std::string &schema_l, const int instances_per_definition_l,
1372 const char *schema_add, const char *instance_add,
1373 int definition) {
1374 schema_l += schema_add;
1375 for (int i = 0; i < instances_per_definition_l; i++)
1376 definitions_l[definition].instances[i] += instance_add;
1377 }
1378 };
1379
1380 // clang-format off
1381 #define AddToSchemaAndInstances(schema_add, instance_add) \
1382 RndDef::Add(definitions, schema, instances_per_definition, \
1383 schema_add, instance_add, definition)
1384
1385 #define Dummy() \
1386 RndDef::Add(definitions, schema, instances_per_definition, \
1387 "byte", "1", definition)
1388 // clang-format on
1389
1390 RndDef definitions[num_definitions];
1391
1392 // We are going to generate num_definitions, the first
1393 // num_struct_definitions will be structs, the rest tables. For each
1394 // generate random fields, some of which may be struct/table types
1395 // referring to previously generated structs/tables.
1396 // Simultanenously, we generate instances_per_definition JSON data
1397 // definitions, which will have identical structure to the schema
1398 // being generated. We generate multiple instances such that when creating
1399 // hierarchy, we get some variety by picking one randomly.
1400 for (int definition = 0; definition < num_definitions; definition++) {
1401 std::string definition_name = "D" + flatbuffers::NumToString(definition);
1402
1403 bool is_struct = definition < num_struct_definitions;
1404
1405 AddToSchemaAndInstances(
1406 ((is_struct ? "struct " : "table ") + definition_name + " {\n").c_str(),
1407 "{\n");
1408
1409 for (int field = 0; field < fields_per_definition; field++) {
1410 const bool is_last_field = field == fields_per_definition - 1;
1411
1412 // Deprecate 1 in deprecation_rate fields. Only table fields can be
1413 // deprecated.
1414 // Don't deprecate the last field to avoid dangling commas in JSON.
1415 const bool deprecated =
1416 !is_struct && !is_last_field && (lcg_rand() % deprecation_rate == 0);
1417
1418 std::string field_name = "f" + flatbuffers::NumToString(field);
1419 AddToSchemaAndInstances((" " + field_name + ":").c_str(),
1420 deprecated ? "" : (field_name + ": ").c_str());
1421 // Pick random type:
1422 auto base_type = static_cast<flatbuffers::BaseType>(
1423 lcg_rand() % (flatbuffers::BASE_TYPE_UNION + 1));
1424 switch (base_type) {
1425 case flatbuffers::BASE_TYPE_STRING:
1426 if (is_struct) {
1427 Dummy(); // No strings in structs.
1428 } else {
1429 AddToSchemaAndInstances("string", deprecated ? "" : "\"hi\"");
1430 }
1431 break;
1432 case flatbuffers::BASE_TYPE_VECTOR:
1433 if (is_struct) {
1434 Dummy(); // No vectors in structs.
1435 } else {
1436 AddToSchemaAndInstances("[ubyte]",
1437 deprecated ? "" : "[\n0,\n1,\n255\n]");
1438 }
1439 break;
1440 case flatbuffers::BASE_TYPE_NONE:
1441 case flatbuffers::BASE_TYPE_UTYPE:
1442 case flatbuffers::BASE_TYPE_STRUCT:
1443 case flatbuffers::BASE_TYPE_UNION:
1444 if (definition) {
1445 // Pick a random previous definition and random data instance of
1446 // that definition.
1447 int defref = lcg_rand() % definition;
1448 int instance = lcg_rand() % instances_per_definition;
1449 AddToSchemaAndInstances(
1450 ("D" + flatbuffers::NumToString(defref)).c_str(),
1451 deprecated ? ""
1452 : definitions[defref].instances[instance].c_str());
1453 } else {
1454 // If this is the first definition, we have no definition we can
1455 // refer to.
1456 Dummy();
1457 }
1458 break;
1459 case flatbuffers::BASE_TYPE_BOOL:
1460 AddToSchemaAndInstances(
1461 "bool", deprecated ? "" : (lcg_rand() % 2 ? "true" : "false"));
1462 break;
1463 case flatbuffers::BASE_TYPE_ARRAY:
1464 if (!is_struct) {
1465 AddToSchemaAndInstances(
1466 "ubyte",
1467 deprecated ? "" : "255"); // No fixed-length arrays in tables.
1468 } else {
1469 AddToSchemaAndInstances("[int:3]", deprecated ? "" : "[\n,\n,\n]");
1470 }
1471 break;
1472 default:
1473 // All the scalar types.
1474 schema += flatbuffers::kTypeNames[base_type];
1475
1476 if (!deprecated) {
1477 // We want each instance to use its own random value.
1478 for (int inst = 0; inst < instances_per_definition; inst++)
1479 definitions[definition].instances[inst] +=
1480 flatbuffers::IsFloat(base_type)
1481 ? flatbuffers::NumToString<double>(lcg_rand() % 128)
1482 .c_str()
1483 : flatbuffers::NumToString<int>(lcg_rand() % 128).c_str();
1484 }
1485 }
1486 AddToSchemaAndInstances(deprecated ? "(deprecated);\n" : ";\n",
1487 deprecated ? "" : is_last_field ? "\n" : ",\n");
1488 }
1489 AddToSchemaAndInstances("}\n\n", "}");
1490 }
1491
1492 schema += "root_type D" + flatbuffers::NumToString(num_definitions - 1);
1493 schema += ";\n";
1494
1495 flatbuffers::Parser parser;
1496
1497 // Will not compare against the original if we don't write defaults
1498 parser.builder_.ForceDefaults(true);
1499
1500 // Parse the schema, parse the generated data, then generate text back
1501 // from the binary and compare against the original.
1502 TEST_EQ(parser.Parse(schema.c_str()), true);
1503
1504 const std::string &json =
1505 definitions[num_definitions - 1].instances[0] + "\n";
1506
1507 TEST_EQ(parser.Parse(json.c_str()), true);
1508
1509 std::string jsongen;
1510 parser.opts.indent_step = 0;
1511 auto result =
1512 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
1513 TEST_EQ(result, true);
1514
1515 if (jsongen != json) {
1516 // These strings are larger than a megabyte, so we show the bytes around
1517 // the first bytes that are different rather than the whole string.
1518 size_t len = std::min(json.length(), jsongen.length());
1519 for (size_t i = 0; i < len; i++) {
1520 if (json[i] != jsongen[i]) {
1521 i -= std::min(static_cast<size_t>(10), i); // show some context;
1522 size_t end = std::min(len, i + 20);
1523 for (; i < end; i++)
1524 TEST_OUTPUT_LINE("at %d: found \"%c\", expected \"%c\"\n",
1525 static_cast<int>(i), jsongen[i], json[i]);
1526 break;
1527 }
1528 }
1529 TEST_NOTNULL(nullptr); //-V501 (this comment supresses CWE-570 warning)
1530 }
1531
1532 // clang-format off
1533 #ifdef FLATBUFFERS_TEST_VERBOSE
1534 TEST_OUTPUT_LINE("%dk schema tested with %dk of json\n",
1535 static_cast<int>(schema.length() / 1024),
1536 static_cast<int>(json.length() / 1024));
1537 #endif
1538 // clang-format on
1539 }
1540
1541 // 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)1542 void TestError_(const char *src, const char *error_substr, bool strict_json,
1543 const char *file, int line, const char *func) {
1544 flatbuffers::IDLOptions opts;
1545 opts.strict_json = strict_json;
1546 flatbuffers::Parser parser(opts);
1547 if (parser.Parse(src)) {
1548 TestFail("true", "false",
1549 ("parser.Parse(\"" + std::string(src) + "\")").c_str(), file, line,
1550 func);
1551 } else if (!strstr(parser.error_.c_str(), error_substr)) {
1552 TestFail(parser.error_.c_str(), error_substr,
1553 ("parser.Parse(\"" + std::string(src) + "\")").c_str(), file, line,
1554 func);
1555 }
1556 }
1557
TestError_(const char * src,const char * error_substr,const char * file,int line,const char * func)1558 void TestError_(const char *src, const char *error_substr, const char *file,
1559 int line, const char *func) {
1560 TestError_(src, error_substr, false, file, line, func);
1561 }
1562
1563 #ifdef _WIN32
1564 # define TestError(src, ...) \
1565 TestError_(src, __VA_ARGS__, __FILE__, __LINE__, __FUNCTION__)
1566 #else
1567 # define TestError(src, ...) \
1568 TestError_(src, __VA_ARGS__, __FILE__, __LINE__, __PRETTY_FUNCTION__)
1569 #endif
1570
1571 // Test that parsing errors occur as we'd expect.
1572 // Also useful for coverage, making sure these paths are run.
ErrorTest()1573 void ErrorTest() {
1574 // In order they appear in idl_parser.cpp
1575 TestError("table X { Y:byte; } root_type X; { Y: 999 }", "does not fit");
1576 TestError("\"\0", "illegal");
1577 TestError("\"\\q", "escape code");
1578 TestError("table ///", "documentation");
1579 TestError("@", "illegal");
1580 TestError("table 1", "expecting");
1581 TestError("table X { Y:[[int]]; }", "nested vector");
1582 TestError("table X { Y:1; }", "illegal type");
1583 TestError("table X { Y:int; Y:int; }", "field already");
1584 TestError("table Y {} table X { Y:int; }", "same as table");
1585 TestError("struct X { Y:string; }", "only scalar");
1586 TestError("table X { Y:string = \"\"; }", "default values");
1587 TestError("struct X { a:uint = 42; }", "default values");
1588 TestError("enum Y:byte { Z = 1 } table X { y:Y; }", "not part of enum");
1589 TestError("struct X { Y:int (deprecated); }", "deprecate");
1590 TestError("union Z { X } table X { Y:Z; } root_type X; { Y: {}, A:1 }",
1591 "missing type field");
1592 TestError("union Z { X } table X { Y:Z; } root_type X; { Y_type: 99, Y: {",
1593 "type id");
1594 TestError("table X { Y:int; } root_type X; { Z:", "unknown field");
1595 TestError("table X { Y:int; } root_type X; { Y:", "string constant", true);
1596 TestError("table X { Y:int; } root_type X; { \"Y\":1, }", "string constant",
1597 true);
1598 TestError(
1599 "struct X { Y:int; Z:int; } table W { V:X; } root_type W; "
1600 "{ V:{ Y:1 } }",
1601 "wrong number");
1602 TestError("enum E:byte { A } table X { Y:E; } root_type X; { Y:U }",
1603 "unknown enum value");
1604 TestError("table X { Y:byte; } root_type X; { Y:; }", "starting");
1605 TestError("enum X:byte { Y } enum X {", "enum already");
1606 TestError("enum X:float {}", "underlying");
1607 TestError("enum X:byte { Y, Y }", "value already");
1608 TestError("enum X:byte { Y=2, Z=1 }", "ascending");
1609 TestError("table X { Y:int; } table X {", "datatype already");
1610 TestError("struct X (force_align: 7) { Y:int; }", "force_align");
1611 TestError("struct X {}", "size 0");
1612 TestError("{}", "no root");
1613 TestError("table X { Y:byte; } root_type X; { Y:1 } { Y:1 }", "end of file");
1614 TestError("table X { Y:byte; } root_type X; { Y:1 } table Y{ Z:int }",
1615 "end of file");
1616 TestError("root_type X;", "unknown root");
1617 TestError("struct X { Y:int; } root_type X;", "a table");
1618 TestError("union X { Y }", "referenced");
1619 TestError("union Z { X } struct X { Y:int; }", "only tables");
1620 TestError("table X { Y:[int]; YLength:int; }", "clash");
1621 TestError("table X { Y:byte; } root_type X; { Y:1, Y:2 }", "more than once");
1622 // float to integer conversion is forbidden
1623 TestError("table X { Y:int; } root_type X; { Y:1.0 }", "float");
1624 TestError("table X { Y:bool; } root_type X; { Y:1.0 }", "float");
1625 TestError("enum X:bool { Y = true }", "must be integral");
1626 }
1627
1628 template<typename T>
TestValue(const char * json,const char * type_name,const char * decls=nullptr)1629 T TestValue(const char *json, const char *type_name,
1630 const char *decls = nullptr) {
1631 flatbuffers::Parser parser;
1632 parser.builder_.ForceDefaults(true); // return defaults
1633 auto check_default = json ? false : true;
1634 if (check_default) { parser.opts.output_default_scalars_in_json = true; }
1635 // Simple schema.
1636 std::string schema = std::string(decls ? decls : "") + "\n" +
1637 "table X { Y:" + std::string(type_name) +
1638 "; } root_type X;";
1639 auto schema_done = parser.Parse(schema.c_str());
1640 TEST_EQ_STR(parser.error_.c_str(), "");
1641 TEST_EQ(schema_done, true);
1642
1643 auto done = parser.Parse(check_default ? "{}" : json);
1644 TEST_EQ_STR(parser.error_.c_str(), "");
1645 TEST_EQ(done, true);
1646
1647 // Check with print.
1648 std::string print_back;
1649 parser.opts.indent_step = -1;
1650 TEST_EQ(GenerateText(parser, parser.builder_.GetBufferPointer(), &print_back),
1651 true);
1652 // restore value from its default
1653 if (check_default) { TEST_EQ(parser.Parse(print_back.c_str()), true); }
1654
1655 auto root = flatbuffers::GetRoot<flatbuffers::Table>(
1656 parser.builder_.GetBufferPointer());
1657 return root->GetField<T>(flatbuffers::FieldIndexToOffset(0), 0);
1658 }
1659
FloatCompare(float a,float b)1660 bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; }
1661
1662 // Additional parser testing not covered elsewhere.
ValueTest()1663 void ValueTest() {
1664 // Test scientific notation numbers.
1665 TEST_EQ(
1666 FloatCompare(TestValue<float>("{ Y:0.0314159e+2 }", "float"), 3.14159f),
1667 true);
1668 // number in string
1669 TEST_EQ(FloatCompare(TestValue<float>("{ Y:\"0.0314159e+2\" }", "float"),
1670 3.14159f),
1671 true);
1672
1673 // Test conversion functions.
1674 TEST_EQ(FloatCompare(TestValue<float>("{ Y:cos(rad(180)) }", "float"), -1),
1675 true);
1676
1677 // int embedded to string
1678 TEST_EQ(TestValue<int>("{ Y:\"-876\" }", "int=-123"), -876);
1679 TEST_EQ(TestValue<int>("{ Y:\"876\" }", "int=-123"), 876);
1680
1681 // Test negative hex constant.
1682 TEST_EQ(TestValue<int>("{ Y:-0x8ea0 }", "int=-0x8ea0"), -36512);
1683 TEST_EQ(TestValue<int>(nullptr, "int=-0x8ea0"), -36512);
1684
1685 // positive hex constant
1686 TEST_EQ(TestValue<int>("{ Y:0x1abcdef }", "int=0x1"), 0x1abcdef);
1687 // with optional '+' sign
1688 TEST_EQ(TestValue<int>("{ Y:+0x1abcdef }", "int=+0x1"), 0x1abcdef);
1689 // hex in string
1690 TEST_EQ(TestValue<int>("{ Y:\"0x1abcdef\" }", "int=+0x1"), 0x1abcdef);
1691
1692 // Make sure we do unsigned 64bit correctly.
1693 TEST_EQ(TestValue<uint64_t>("{ Y:12335089644688340133 }", "ulong"),
1694 12335089644688340133ULL);
1695
1696 // bool in string
1697 TEST_EQ(TestValue<bool>("{ Y:\"false\" }", "bool=true"), false);
1698 TEST_EQ(TestValue<bool>("{ Y:\"true\" }", "bool=\"true\""), true);
1699 TEST_EQ(TestValue<bool>("{ Y:'false' }", "bool=true"), false);
1700 TEST_EQ(TestValue<bool>("{ Y:'true' }", "bool=\"true\""), true);
1701
1702 // check comments before and after json object
1703 TEST_EQ(TestValue<int>("/*before*/ { Y:1 } /*after*/", "int"), 1);
1704 TEST_EQ(TestValue<int>("//before \n { Y:1 } //after", "int"), 1);
1705 }
1706
NestedListTest()1707 void NestedListTest() {
1708 flatbuffers::Parser parser1;
1709 TEST_EQ(parser1.Parse("struct Test { a:short; b:byte; } table T { F:[Test]; }"
1710 "root_type T;"
1711 "{ F:[ [10,20], [30,40]] }"),
1712 true);
1713 }
1714
EnumStringsTest()1715 void EnumStringsTest() {
1716 flatbuffers::Parser parser1;
1717 TEST_EQ(parser1.Parse("enum E:byte { A, B, C } table T { F:[E]; }"
1718 "root_type T;"
1719 "{ F:[ A, B, \"C\", \"A B C\" ] }"),
1720 true);
1721 flatbuffers::Parser parser2;
1722 TEST_EQ(parser2.Parse("enum E:byte { A, B, C } table T { F:[int]; }"
1723 "root_type T;"
1724 "{ F:[ \"E.C\", \"E.A E.B E.C\" ] }"),
1725 true);
1726 // unsigned bit_flags
1727 flatbuffers::Parser parser3;
1728 TEST_EQ(
1729 parser3.Parse("enum E:uint16 (bit_flags) { F0, F07=7, F08, F14=14, F15 }"
1730 " table T { F: E = \"F15 F08\"; }"
1731 "root_type T;"),
1732 true);
1733 }
1734
EnumNamesTest()1735 void EnumNamesTest() {
1736 TEST_EQ_STR("Red", EnumNameColor(Color_Red));
1737 TEST_EQ_STR("Green", EnumNameColor(Color_Green));
1738 TEST_EQ_STR("Blue", EnumNameColor(Color_Blue));
1739 // Check that Color to string don't crash while decode a mixture of Colors.
1740 // 1) Example::Color enum is enum with unfixed underlying type.
1741 // 2) Valid enum range: [0; 2^(ceil(log2(Color_ANY))) - 1].
1742 // Consequence: A value is out of this range will lead to UB (since C++17).
1743 // For details see C++17 standard or explanation on the SO:
1744 // stackoverflow.com/questions/18195312/what-happens-if-you-static-cast-invalid-value-to-enum-class
1745 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(0)));
1746 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY - 1)));
1747 TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY + 1)));
1748 }
1749
EnumOutOfRangeTest()1750 void EnumOutOfRangeTest() {
1751 TestError("enum X:byte { Y = 128 }", "enum value does not fit");
1752 TestError("enum X:byte { Y = -129 }", "enum value does not fit");
1753 TestError("enum X:byte { Y = 126, Z0, Z1 }", "enum value does not fit");
1754 TestError("enum X:ubyte { Y = -1 }", "enum value does not fit");
1755 TestError("enum X:ubyte { Y = 256 }", "enum value does not fit");
1756 TestError("enum X:ubyte { Y = 255, Z }", "enum value does not fit");
1757 // Unions begin with an implicit "NONE = 0".
1758 TestError("table Y{} union X { Y = -1 }",
1759 "enum values must be specified in ascending order");
1760 TestError("table Y{} union X { Y = 256 }", "enum value does not fit");
1761 TestError("table Y{} union X { Y = 255, Z:Y }", "enum value does not fit");
1762 TestError("enum X:int { Y = -2147483649 }", "enum value does not fit");
1763 TestError("enum X:int { Y = 2147483648 }", "enum value does not fit");
1764 TestError("enum X:uint { Y = -1 }", "enum value does not fit");
1765 TestError("enum X:uint { Y = 4294967297 }", "enum value does not fit");
1766 TestError("enum X:long { Y = 9223372036854775808 }", "does not fit");
1767 TestError("enum X:long { Y = 9223372036854775807, Z }",
1768 "enum value does not fit");
1769 TestError("enum X:ulong { Y = -1 }", "does not fit");
1770 TestError("enum X:ubyte (bit_flags) { Y=8 }", "bit flag out");
1771 TestError("enum X:byte (bit_flags) { Y=7 }", "must be unsigned"); // -128
1772 // bit_flgs out of range
1773 TestError("enum X:ubyte (bit_flags) { Y0,Y1,Y2,Y3,Y4,Y5,Y6,Y7,Y8 }",
1774 "out of range");
1775 }
1776
EnumValueTest()1777 void EnumValueTest() {
1778 // json: "{ Y:0 }", schema: table X { Y : "E"}
1779 // 0 in enum (V=0) E then Y=0 is valid.
1780 TEST_EQ(TestValue<int>("{ Y:0 }", "E", "enum E:int { V }"), 0);
1781 TEST_EQ(TestValue<int>("{ Y:V }", "E", "enum E:int { V }"), 0);
1782 // A default value of Y is 0.
1783 TEST_EQ(TestValue<int>("{ }", "E", "enum E:int { V }"), 0);
1784 TEST_EQ(TestValue<int>("{ Y:5 }", "E=V", "enum E:int { V=5 }"), 5);
1785 // Generate json with defaults and check.
1786 TEST_EQ(TestValue<int>(nullptr, "E=V", "enum E:int { V=5 }"), 5);
1787 // 5 in enum
1788 TEST_EQ(TestValue<int>("{ Y:5 }", "E", "enum E:int { Z, V=5 }"), 5);
1789 TEST_EQ(TestValue<int>("{ Y:5 }", "E=V", "enum E:int { Z, V=5 }"), 5);
1790 // Generate json with defaults and check.
1791 TEST_EQ(TestValue<int>(nullptr, "E", "enum E:int { Z, V=5 }"), 0);
1792 TEST_EQ(TestValue<int>(nullptr, "E=V", "enum E:int { Z, V=5 }"), 5);
1793 // u84 test
1794 TEST_EQ(TestValue<uint64_t>(nullptr, "E=V",
1795 "enum E:ulong { V = 13835058055282163712 }"),
1796 13835058055282163712ULL);
1797 TEST_EQ(TestValue<uint64_t>(nullptr, "E=V",
1798 "enum E:ulong { V = 18446744073709551615 }"),
1799 18446744073709551615ULL);
1800 // Assign non-enum value to enum field. Is it right?
1801 TEST_EQ(TestValue<int>("{ Y:7 }", "E", "enum E:int { V = 0 }"), 7);
1802 }
1803
IntegerOutOfRangeTest()1804 void IntegerOutOfRangeTest() {
1805 TestError("table T { F:byte; } root_type T; { F:128 }",
1806 "constant does not fit");
1807 TestError("table T { F:byte; } root_type T; { F:-129 }",
1808 "constant does not fit");
1809 TestError("table T { F:ubyte; } root_type T; { F:256 }",
1810 "constant does not fit");
1811 TestError("table T { F:ubyte; } root_type T; { F:-1 }",
1812 "constant does not fit");
1813 TestError("table T { F:short; } root_type T; { F:32768 }",
1814 "constant does not fit");
1815 TestError("table T { F:short; } root_type T; { F:-32769 }",
1816 "constant does not fit");
1817 TestError("table T { F:ushort; } root_type T; { F:65536 }",
1818 "constant does not fit");
1819 TestError("table T { F:ushort; } root_type T; { F:-1 }",
1820 "constant does not fit");
1821 TestError("table T { F:int; } root_type T; { F:2147483648 }",
1822 "constant does not fit");
1823 TestError("table T { F:int; } root_type T; { F:-2147483649 }",
1824 "constant does not fit");
1825 TestError("table T { F:uint; } root_type T; { F:4294967296 }",
1826 "constant does not fit");
1827 TestError("table T { F:uint; } root_type T; { F:-1 }",
1828 "constant does not fit");
1829 // Check fixed width aliases
1830 TestError("table X { Y:uint8; } root_type X; { Y: -1 }", "does not fit");
1831 TestError("table X { Y:uint8; } root_type X; { Y: 256 }", "does not fit");
1832 TestError("table X { Y:uint16; } root_type X; { Y: -1 }", "does not fit");
1833 TestError("table X { Y:uint16; } root_type X; { Y: 65536 }", "does not fit");
1834 TestError("table X { Y:uint32; } root_type X; { Y: -1 }", "");
1835 TestError("table X { Y:uint32; } root_type X; { Y: 4294967296 }",
1836 "does not fit");
1837 TestError("table X { Y:uint64; } root_type X; { Y: -1 }", "");
1838 TestError("table X { Y:uint64; } root_type X; { Y: -9223372036854775809 }",
1839 "does not fit");
1840 TestError("table X { Y:uint64; } root_type X; { Y: 18446744073709551616 }",
1841 "does not fit");
1842
1843 TestError("table X { Y:int8; } root_type X; { Y: -129 }", "does not fit");
1844 TestError("table X { Y:int8; } root_type X; { Y: 128 }", "does not fit");
1845 TestError("table X { Y:int16; } root_type X; { Y: -32769 }", "does not fit");
1846 TestError("table X { Y:int16; } root_type X; { Y: 32768 }", "does not fit");
1847 TestError("table X { Y:int32; } root_type X; { Y: -2147483649 }", "");
1848 TestError("table X { Y:int32; } root_type X; { Y: 2147483648 }",
1849 "does not fit");
1850 TestError("table X { Y:int64; } root_type X; { Y: -9223372036854775809 }",
1851 "does not fit");
1852 TestError("table X { Y:int64; } root_type X; { Y: 9223372036854775808 }",
1853 "does not fit");
1854 // check out-of-int64 as int8
1855 TestError("table X { Y:int8; } root_type X; { Y: -9223372036854775809 }",
1856 "does not fit");
1857 TestError("table X { Y:int8; } root_type X; { Y: 9223372036854775808 }",
1858 "does not fit");
1859
1860 // Check default values
1861 TestError("table X { Y:int64=-9223372036854775809; } root_type X; {}",
1862 "does not fit");
1863 TestError("table X { Y:int64= 9223372036854775808; } root_type X; {}",
1864 "does not fit");
1865 TestError("table X { Y:uint64; } root_type X; { Y: -1 }", "");
1866 TestError("table X { Y:uint64=-9223372036854775809; } root_type X; {}",
1867 "does not fit");
1868 TestError("table X { Y:uint64= 18446744073709551616; } root_type X; {}",
1869 "does not fit");
1870 }
1871
IntegerBoundaryTest()1872 void IntegerBoundaryTest() {
1873 // Check numerical compatibility with non-C++ languages.
1874 // By the C++ standard, std::numerical_limits<int64_t>::min() ==
1875 // -9223372036854775807 (-2^63+1) or less* The Flatbuffers grammar and most of
1876 // the languages (C#, Java, Rust) expect that minimum values are: -128,
1877 // -32768,.., -9223372036854775808. Since C++20,
1878 // static_cast<int64>(0x8000000000000000ULL) is well-defined two's complement
1879 // cast. Therefore -9223372036854775808 should be valid negative value.
1880 TEST_EQ(flatbuffers::numeric_limits<int8_t>::min(), -128);
1881 TEST_EQ(flatbuffers::numeric_limits<int8_t>::max(), 127);
1882 TEST_EQ(flatbuffers::numeric_limits<int16_t>::min(), -32768);
1883 TEST_EQ(flatbuffers::numeric_limits<int16_t>::max(), 32767);
1884 TEST_EQ(flatbuffers::numeric_limits<int32_t>::min() + 1, -2147483647);
1885 TEST_EQ(flatbuffers::numeric_limits<int32_t>::max(), 2147483647ULL);
1886 TEST_EQ(flatbuffers::numeric_limits<int64_t>::min() + 1LL,
1887 -9223372036854775807LL);
1888 TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(), 9223372036854775807ULL);
1889 TEST_EQ(flatbuffers::numeric_limits<uint8_t>::max(), 255);
1890 TEST_EQ(flatbuffers::numeric_limits<uint16_t>::max(), 65535);
1891 TEST_EQ(flatbuffers::numeric_limits<uint32_t>::max(), 4294967295ULL);
1892 TEST_EQ(flatbuffers::numeric_limits<uint64_t>::max(),
1893 18446744073709551615ULL);
1894
1895 TEST_EQ(TestValue<int8_t>("{ Y:127 }", "byte"), 127);
1896 TEST_EQ(TestValue<int8_t>("{ Y:-128 }", "byte"), -128);
1897 TEST_EQ(TestValue<uint8_t>("{ Y:255 }", "ubyte"), 255);
1898 TEST_EQ(TestValue<uint8_t>("{ Y:0 }", "ubyte"), 0);
1899 TEST_EQ(TestValue<int16_t>("{ Y:32767 }", "short"), 32767);
1900 TEST_EQ(TestValue<int16_t>("{ Y:-32768 }", "short"), -32768);
1901 TEST_EQ(TestValue<uint16_t>("{ Y:65535 }", "ushort"), 65535);
1902 TEST_EQ(TestValue<uint16_t>("{ Y:0 }", "ushort"), 0);
1903 TEST_EQ(TestValue<int32_t>("{ Y:2147483647 }", "int"), 2147483647);
1904 TEST_EQ(TestValue<int32_t>("{ Y:-2147483648 }", "int") + 1, -2147483647);
1905 TEST_EQ(TestValue<uint32_t>("{ Y:4294967295 }", "uint"), 4294967295);
1906 TEST_EQ(TestValue<uint32_t>("{ Y:0 }", "uint"), 0);
1907 TEST_EQ(TestValue<int64_t>("{ Y:9223372036854775807 }", "long"),
1908 9223372036854775807LL);
1909 TEST_EQ(TestValue<int64_t>("{ Y:-9223372036854775808 }", "long") + 1LL,
1910 -9223372036854775807LL);
1911 TEST_EQ(TestValue<uint64_t>("{ Y:18446744073709551615 }", "ulong"),
1912 18446744073709551615ULL);
1913 TEST_EQ(TestValue<uint64_t>("{ Y:0 }", "ulong"), 0);
1914 TEST_EQ(TestValue<uint64_t>("{ Y: 18446744073709551615 }", "uint64"),
1915 18446744073709551615ULL);
1916 // check that the default works
1917 TEST_EQ(TestValue<uint64_t>(nullptr, "uint64 = 18446744073709551615"),
1918 18446744073709551615ULL);
1919 }
1920
ValidFloatTest()1921 void ValidFloatTest() {
1922 // check rounding to infinity
1923 TEST_EQ(TestValue<float>("{ Y:+3.4029e+38 }", "float"), +infinityf);
1924 TEST_EQ(TestValue<float>("{ Y:-3.4029e+38 }", "float"), -infinityf);
1925 TEST_EQ(TestValue<double>("{ Y:+1.7977e+308 }", "double"), +infinityd);
1926 TEST_EQ(TestValue<double>("{ Y:-1.7977e+308 }", "double"), -infinityd);
1927
1928 TEST_EQ(
1929 FloatCompare(TestValue<float>("{ Y:0.0314159e+2 }", "float"), 3.14159f),
1930 true);
1931 // float in string
1932 TEST_EQ(FloatCompare(TestValue<float>("{ Y:\" 0.0314159e+2 \" }", "float"),
1933 3.14159f),
1934 true);
1935
1936 TEST_EQ(TestValue<float>("{ Y:1 }", "float"), 1.0f);
1937 TEST_EQ(TestValue<float>("{ Y:1.0 }", "float"), 1.0f);
1938 TEST_EQ(TestValue<float>("{ Y:1. }", "float"), 1.0f);
1939 TEST_EQ(TestValue<float>("{ Y:+1. }", "float"), 1.0f);
1940 TEST_EQ(TestValue<float>("{ Y:-1. }", "float"), -1.0f);
1941 TEST_EQ(TestValue<float>("{ Y:1.e0 }", "float"), 1.0f);
1942 TEST_EQ(TestValue<float>("{ Y:1.e+0 }", "float"), 1.0f);
1943 TEST_EQ(TestValue<float>("{ Y:1.e-0 }", "float"), 1.0f);
1944 TEST_EQ(TestValue<float>("{ Y:0.125 }", "float"), 0.125f);
1945 TEST_EQ(TestValue<float>("{ Y:.125 }", "float"), 0.125f);
1946 TEST_EQ(TestValue<float>("{ Y:-.125 }", "float"), -0.125f);
1947 TEST_EQ(TestValue<float>("{ Y:+.125 }", "float"), +0.125f);
1948 TEST_EQ(TestValue<float>("{ Y:5 }", "float"), 5.0f);
1949 TEST_EQ(TestValue<float>("{ Y:\"5\" }", "float"), 5.0f);
1950
1951 #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
1952 // Old MSVC versions may have problem with this check.
1953 // https://www.exploringbinary.com/visual-c-plus-plus-strtod-still-broken/
1954 TEST_EQ(TestValue<double>("{ Y:6.9294956446009195e15 }", "double"),
1955 6929495644600920.0);
1956 // check nan's
1957 TEST_EQ(std::isnan(TestValue<double>("{ Y:nan }", "double")), true);
1958 TEST_EQ(std::isnan(TestValue<float>("{ Y:nan }", "float")), true);
1959 TEST_EQ(std::isnan(TestValue<float>("{ Y:\"nan\" }", "float")), true);
1960 TEST_EQ(std::isnan(TestValue<float>("{ Y:+nan }", "float")), true);
1961 TEST_EQ(std::isnan(TestValue<float>("{ Y:-nan }", "float")), true);
1962 TEST_EQ(std::isnan(TestValue<float>(nullptr, "float=nan")), true);
1963 TEST_EQ(std::isnan(TestValue<float>(nullptr, "float=-nan")), true);
1964 // check inf
1965 TEST_EQ(TestValue<float>("{ Y:inf }", "float"), infinityf);
1966 TEST_EQ(TestValue<float>("{ Y:\"inf\" }", "float"), infinityf);
1967 TEST_EQ(TestValue<float>("{ Y:+inf }", "float"), infinityf);
1968 TEST_EQ(TestValue<float>("{ Y:-inf }", "float"), -infinityf);
1969 TEST_EQ(TestValue<float>(nullptr, "float=inf"), infinityf);
1970 TEST_EQ(TestValue<float>(nullptr, "float=-inf"), -infinityf);
1971 TestValue<double>(
1972 "{ Y : [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, "
1973 "3.0e2] }",
1974 "[double]");
1975 TestValue<float>(
1976 "{ Y : [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, "
1977 "3.0e2] }",
1978 "[float]");
1979
1980 // Test binary format of float point.
1981 // https://en.cppreference.com/w/cpp/language/floating_literal
1982 // 0x11.12p-1 = (1*16^1 + 2*16^0 + 3*16^-1 + 4*16^-2) * 2^-1 =
1983 TEST_EQ(TestValue<double>("{ Y:0x12.34p-1 }", "double"), 9.1015625);
1984 // hex fraction 1.2 (decimal 1.125) scaled by 2^3, that is 9.0
1985 TEST_EQ(TestValue<float>("{ Y:-0x0.2p0 }", "float"), -0.125f);
1986 TEST_EQ(TestValue<float>("{ Y:-0x.2p1 }", "float"), -0.25f);
1987 TEST_EQ(TestValue<float>("{ Y:0x1.2p3 }", "float"), 9.0f);
1988 TEST_EQ(TestValue<float>("{ Y:0x10.1p0 }", "float"), 16.0625f);
1989 TEST_EQ(TestValue<double>("{ Y:0x1.2p3 }", "double"), 9.0);
1990 TEST_EQ(TestValue<double>("{ Y:0x10.1p0 }", "double"), 16.0625);
1991 TEST_EQ(TestValue<double>("{ Y:0xC.68p+2 }", "double"), 49.625);
1992 TestValue<double>("{ Y : [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[double]");
1993 TestValue<float>("{ Y : [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[float]");
1994
1995 #else // FLATBUFFERS_HAS_NEW_STRTOD
1996 TEST_OUTPUT_LINE("FLATBUFFERS_HAS_NEW_STRTOD tests skipped");
1997 #endif // !FLATBUFFERS_HAS_NEW_STRTOD
1998 }
1999
InvalidFloatTest()2000 void InvalidFloatTest() {
2001 auto invalid_msg = "invalid number";
2002 auto comma_msg = "expecting: ,";
2003 TestError("table T { F:float; } root_type T; { F:1,0 }", "");
2004 TestError("table T { F:float; } root_type T; { F:. }", "");
2005 TestError("table T { F:float; } root_type T; { F:- }", invalid_msg);
2006 TestError("table T { F:float; } root_type T; { F:+ }", invalid_msg);
2007 TestError("table T { F:float; } root_type T; { F:-. }", invalid_msg);
2008 TestError("table T { F:float; } root_type T; { F:+. }", invalid_msg);
2009 TestError("table T { F:float; } root_type T; { F:.e }", "");
2010 TestError("table T { F:float; } root_type T; { F:-e }", invalid_msg);
2011 TestError("table T { F:float; } root_type T; { F:+e }", invalid_msg);
2012 TestError("table T { F:float; } root_type T; { F:-.e }", invalid_msg);
2013 TestError("table T { F:float; } root_type T; { F:+.e }", invalid_msg);
2014 TestError("table T { F:float; } root_type T; { F:-e1 }", invalid_msg);
2015 TestError("table T { F:float; } root_type T; { F:+e1 }", invalid_msg);
2016 TestError("table T { F:float; } root_type T; { F:1.0e+ }", invalid_msg);
2017 TestError("table T { F:float; } root_type T; { F:1.0e- }", invalid_msg);
2018 // exponent pP is mandatory for hex-float
2019 TestError("table T { F:float; } root_type T; { F:0x0 }", invalid_msg);
2020 TestError("table T { F:float; } root_type T; { F:-0x. }", invalid_msg);
2021 TestError("table T { F:float; } root_type T; { F:0x. }", invalid_msg);
2022 // eE not exponent in hex-float!
2023 TestError("table T { F:float; } root_type T; { F:0x0.0e+ }", invalid_msg);
2024 TestError("table T { F:float; } root_type T; { F:0x0.0e- }", invalid_msg);
2025 TestError("table T { F:float; } root_type T; { F:0x0.0p }", invalid_msg);
2026 TestError("table T { F:float; } root_type T; { F:0x0.0p+ }", invalid_msg);
2027 TestError("table T { F:float; } root_type T; { F:0x0.0p- }", invalid_msg);
2028 TestError("table T { F:float; } root_type T; { F:0x0.0pa1 }", invalid_msg);
2029 TestError("table T { F:float; } root_type T; { F:0x0.0e+ }", invalid_msg);
2030 TestError("table T { F:float; } root_type T; { F:0x0.0e- }", invalid_msg);
2031 TestError("table T { F:float; } root_type T; { F:0x0.0e+0 }", invalid_msg);
2032 TestError("table T { F:float; } root_type T; { F:0x0.0e-0 }", invalid_msg);
2033 TestError("table T { F:float; } root_type T; { F:0x0.0ep+ }", invalid_msg);
2034 TestError("table T { F:float; } root_type T; { F:0x0.0ep- }", invalid_msg);
2035 TestError("table T { F:float; } root_type T; { F:1.2.3 }", invalid_msg);
2036 TestError("table T { F:float; } root_type T; { F:1.2.e3 }", invalid_msg);
2037 TestError("table T { F:float; } root_type T; { F:1.2e.3 }", invalid_msg);
2038 TestError("table T { F:float; } root_type T; { F:1.2e0.3 }", invalid_msg);
2039 TestError("table T { F:float; } root_type T; { F:1.2e3. }", invalid_msg);
2040 TestError("table T { F:float; } root_type T; { F:1.2e3.0 }", invalid_msg);
2041 TestError("table T { F:float; } root_type T; { F:+-1.0 }", invalid_msg);
2042 TestError("table T { F:float; } root_type T; { F:1.0e+-1 }", invalid_msg);
2043 TestError("table T { F:float; } root_type T; { F:\"1.0e+-1\" }", invalid_msg);
2044 TestError("table T { F:float; } root_type T; { F:1.e0e }", comma_msg);
2045 TestError("table T { F:float; } root_type T; { F:0x1.p0e }", comma_msg);
2046 TestError("table T { F:float; } root_type T; { F:\" 0x10 \" }", invalid_msg);
2047 // floats in string
2048 TestError("table T { F:float; } root_type T; { F:\"1,2.\" }", invalid_msg);
2049 TestError("table T { F:float; } root_type T; { F:\"1.2e3.\" }", invalid_msg);
2050 TestError("table T { F:float; } root_type T; { F:\"0x1.p0e\" }", invalid_msg);
2051 TestError("table T { F:float; } root_type T; { F:\"0x1.0\" }", invalid_msg);
2052 TestError("table T { F:float; } root_type T; { F:\" 0x1.0\" }", invalid_msg);
2053 TestError("table T { F:float; } root_type T; { F:\"+ 0\" }", invalid_msg);
2054 // disable escapes for "number-in-string"
2055 TestError("table T { F:float; } root_type T; { F:\"\\f1.2e3.\" }", "invalid");
2056 TestError("table T { F:float; } root_type T; { F:\"\\t1.2e3.\" }", "invalid");
2057 TestError("table T { F:float; } root_type T; { F:\"\\n1.2e3.\" }", "invalid");
2058 TestError("table T { F:float; } root_type T; { F:\"\\r1.2e3.\" }", "invalid");
2059 TestError("table T { F:float; } root_type T; { F:\"4\\x005\" }", "invalid");
2060 TestError("table T { F:float; } root_type T; { F:\"\'12\'\" }", invalid_msg);
2061 // null is not a number constant!
2062 TestError("table T { F:float; } root_type T; { F:\"null\" }", invalid_msg);
2063 TestError("table T { F:float; } root_type T; { F:null }", invalid_msg);
2064 }
2065
GenerateTableTextTest()2066 void GenerateTableTextTest() {
2067 std::string schemafile;
2068 std::string jsonfile;
2069 bool ok =
2070 flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
2071 false, &schemafile) &&
2072 flatbuffers::LoadFile((test_data_path + "monsterdata_test.json").c_str(),
2073 false, &jsonfile);
2074 TEST_EQ(ok, true);
2075 auto include_test_path =
2076 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
2077 const char *include_directories[] = { test_data_path.c_str(),
2078 include_test_path.c_str(), nullptr };
2079 flatbuffers::IDLOptions opt;
2080 opt.indent_step = -1;
2081 flatbuffers::Parser parser(opt);
2082 ok = parser.Parse(schemafile.c_str(), include_directories) &&
2083 parser.Parse(jsonfile.c_str(), include_directories);
2084 TEST_EQ(ok, true);
2085 // Test root table
2086 const Monster *monster = GetMonster(parser.builder_.GetBufferPointer());
2087 std::string jsongen;
2088 auto result = GenerateTextFromTable(parser, monster, "MyGame.Example.Monster",
2089 &jsongen);
2090 TEST_EQ(result, true);
2091 // Test sub table
2092 const Vec3 *pos = monster->pos();
2093 jsongen.clear();
2094 result = GenerateTextFromTable(parser, pos, "MyGame.Example.Vec3", &jsongen);
2095 TEST_EQ(result, true);
2096 TEST_EQ_STR(
2097 jsongen.c_str(),
2098 "{x: 1.0,y: 2.0,z: 3.0,test1: 3.0,test2: \"Green\",test3: {a: 5,b: 6}}");
2099 const Test &test3 = pos->test3();
2100 jsongen.clear();
2101 result =
2102 GenerateTextFromTable(parser, &test3, "MyGame.Example.Test", &jsongen);
2103 TEST_EQ(result, true);
2104 TEST_EQ_STR(jsongen.c_str(), "{a: 5,b: 6}");
2105 const Test *test4 = monster->test4()->Get(0);
2106 jsongen.clear();
2107 result =
2108 GenerateTextFromTable(parser, test4, "MyGame.Example.Test", &jsongen);
2109 TEST_EQ(result, true);
2110 TEST_EQ_STR(jsongen.c_str(), "{a: 10,b: 20}");
2111 }
2112
2113 template<typename T>
NumericUtilsTestInteger(const char * lower,const char * upper)2114 void NumericUtilsTestInteger(const char *lower, const char *upper) {
2115 T x;
2116 TEST_EQ(flatbuffers::StringToNumber("1q", &x), false);
2117 TEST_EQ(x, 0);
2118 TEST_EQ(flatbuffers::StringToNumber(upper, &x), false);
2119 TEST_EQ(x, flatbuffers::numeric_limits<T>::max());
2120 TEST_EQ(flatbuffers::StringToNumber(lower, &x), false);
2121 auto expval = flatbuffers::is_unsigned<T>::value
2122 ? flatbuffers::numeric_limits<T>::max()
2123 : flatbuffers::numeric_limits<T>::lowest();
2124 TEST_EQ(x, expval);
2125 }
2126
2127 template<typename T>
NumericUtilsTestFloat(const char * lower,const char * upper)2128 void NumericUtilsTestFloat(const char *lower, const char *upper) {
2129 T f;
2130 TEST_EQ(flatbuffers::StringToNumber("", &f), false);
2131 TEST_EQ(flatbuffers::StringToNumber("1q", &f), false);
2132 TEST_EQ(f, 0);
2133 TEST_EQ(flatbuffers::StringToNumber(upper, &f), true);
2134 TEST_EQ(f, +flatbuffers::numeric_limits<T>::infinity());
2135 TEST_EQ(flatbuffers::StringToNumber(lower, &f), true);
2136 TEST_EQ(f, -flatbuffers::numeric_limits<T>::infinity());
2137 }
2138
NumericUtilsTest()2139 void NumericUtilsTest() {
2140 NumericUtilsTestInteger<uint64_t>("-1", "18446744073709551616");
2141 NumericUtilsTestInteger<uint8_t>("-1", "256");
2142 NumericUtilsTestInteger<int64_t>("-9223372036854775809",
2143 "9223372036854775808");
2144 NumericUtilsTestInteger<int8_t>("-129", "128");
2145 NumericUtilsTestFloat<float>("-3.4029e+38", "+3.4029e+38");
2146 NumericUtilsTestFloat<float>("-1.7977e+308", "+1.7977e+308");
2147 }
2148
IsAsciiUtilsTest()2149 void IsAsciiUtilsTest() {
2150 char c = -128;
2151 for (int cnt = 0; cnt < 256; cnt++) {
2152 auto alpha = (('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z'));
2153 auto dec = (('0' <= c) && (c <= '9'));
2154 auto hex = (('a' <= c) && (c <= 'f')) || (('A' <= c) && (c <= 'F'));
2155 TEST_EQ(flatbuffers::is_alpha(c), alpha);
2156 TEST_EQ(flatbuffers::is_alnum(c), alpha || dec);
2157 TEST_EQ(flatbuffers::is_digit(c), dec);
2158 TEST_EQ(flatbuffers::is_xdigit(c), dec || hex);
2159 c += 1;
2160 }
2161 }
2162
UnicodeTest()2163 void UnicodeTest() {
2164 flatbuffers::Parser parser;
2165 // Without setting allow_non_utf8 = true, we treat \x sequences as byte
2166 // sequences which are then validated as UTF-8.
2167 TEST_EQ(parser.Parse("table T { F:string; }"
2168 "root_type T;"
2169 "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2170 "\\u5225\\u30B5\\u30A4\\u30C8\\xE2\\x82\\xAC\\u0080\\uD8"
2171 "3D\\uDE0E\" }"),
2172 true);
2173 std::string jsongen;
2174 parser.opts.indent_step = -1;
2175 auto result =
2176 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2177 TEST_EQ(result, true);
2178 TEST_EQ_STR(jsongen.c_str(),
2179 "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2180 "\\u5225\\u30B5\\u30A4\\u30C8\\u20AC\\u0080\\uD83D\\uDE0E\"}");
2181 }
2182
UnicodeTestAllowNonUTF8()2183 void UnicodeTestAllowNonUTF8() {
2184 flatbuffers::Parser parser;
2185 parser.opts.allow_non_utf8 = true;
2186 TEST_EQ(
2187 parser.Parse(
2188 "table T { F:string; }"
2189 "root_type T;"
2190 "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2191 "\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\\u0080\\uD83D\\uDE0E\" }"),
2192 true);
2193 std::string jsongen;
2194 parser.opts.indent_step = -1;
2195 auto result =
2196 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2197 TEST_EQ(result, true);
2198 TEST_EQ_STR(
2199 jsongen.c_str(),
2200 "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2201 "\\u5225\\u30B5\\u30A4\\u30C8\\u0001\\x80\\u0080\\uD83D\\uDE0E\"}");
2202 }
2203
UnicodeTestGenerateTextFailsOnNonUTF8()2204 void UnicodeTestGenerateTextFailsOnNonUTF8() {
2205 flatbuffers::Parser parser;
2206 // Allow non-UTF-8 initially to model what happens when we load a binary
2207 // flatbuffer from disk which contains non-UTF-8 strings.
2208 parser.opts.allow_non_utf8 = true;
2209 TEST_EQ(
2210 parser.Parse(
2211 "table T { F:string; }"
2212 "root_type T;"
2213 "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
2214 "\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\\u0080\\uD83D\\uDE0E\" }"),
2215 true);
2216 std::string jsongen;
2217 parser.opts.indent_step = -1;
2218 // Now, disallow non-UTF-8 (the default behavior) so GenerateText indicates
2219 // failure.
2220 parser.opts.allow_non_utf8 = false;
2221 auto result =
2222 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2223 TEST_EQ(result, false);
2224 }
2225
UnicodeSurrogatesTest()2226 void UnicodeSurrogatesTest() {
2227 flatbuffers::Parser parser;
2228
2229 TEST_EQ(parser.Parse("table T { F:string (id: 0); }"
2230 "root_type T;"
2231 "{ F:\"\\uD83D\\uDCA9\"}"),
2232 true);
2233 auto root = flatbuffers::GetRoot<flatbuffers::Table>(
2234 parser.builder_.GetBufferPointer());
2235 auto string = root->GetPointer<flatbuffers::String *>(
2236 flatbuffers::FieldIndexToOffset(0));
2237 TEST_EQ_STR(string->c_str(), "\xF0\x9F\x92\xA9");
2238 }
2239
UnicodeInvalidSurrogatesTest()2240 void UnicodeInvalidSurrogatesTest() {
2241 TestError(
2242 "table T { F:string; }"
2243 "root_type T;"
2244 "{ F:\"\\uD800\"}",
2245 "unpaired high surrogate");
2246 TestError(
2247 "table T { F:string; }"
2248 "root_type T;"
2249 "{ F:\"\\uD800abcd\"}",
2250 "unpaired high surrogate");
2251 TestError(
2252 "table T { F:string; }"
2253 "root_type T;"
2254 "{ F:\"\\uD800\\n\"}",
2255 "unpaired high surrogate");
2256 TestError(
2257 "table T { F:string; }"
2258 "root_type T;"
2259 "{ F:\"\\uD800\\uD800\"}",
2260 "multiple high surrogates");
2261 TestError(
2262 "table T { F:string; }"
2263 "root_type T;"
2264 "{ F:\"\\uDC00\"}",
2265 "unpaired low surrogate");
2266 }
2267
InvalidUTF8Test()2268 void InvalidUTF8Test() {
2269 // "1 byte" pattern, under min length of 2 bytes
2270 TestError(
2271 "table T { F:string; }"
2272 "root_type T;"
2273 "{ F:\"\x80\"}",
2274 "illegal UTF-8 sequence");
2275 // 2 byte pattern, string too short
2276 TestError(
2277 "table T { F:string; }"
2278 "root_type T;"
2279 "{ F:\"\xDF\"}",
2280 "illegal UTF-8 sequence");
2281 // 3 byte pattern, string too short
2282 TestError(
2283 "table T { F:string; }"
2284 "root_type T;"
2285 "{ F:\"\xEF\xBF\"}",
2286 "illegal UTF-8 sequence");
2287 // 4 byte pattern, string too short
2288 TestError(
2289 "table T { F:string; }"
2290 "root_type T;"
2291 "{ F:\"\xF7\xBF\xBF\"}",
2292 "illegal UTF-8 sequence");
2293 // "5 byte" pattern, string too short
2294 TestError(
2295 "table T { F:string; }"
2296 "root_type T;"
2297 "{ F:\"\xFB\xBF\xBF\xBF\"}",
2298 "illegal UTF-8 sequence");
2299 // "6 byte" pattern, string too short
2300 TestError(
2301 "table T { F:string; }"
2302 "root_type T;"
2303 "{ F:\"\xFD\xBF\xBF\xBF\xBF\"}",
2304 "illegal UTF-8 sequence");
2305 // "7 byte" pattern, string too short
2306 TestError(
2307 "table T { F:string; }"
2308 "root_type T;"
2309 "{ F:\"\xFE\xBF\xBF\xBF\xBF\xBF\"}",
2310 "illegal UTF-8 sequence");
2311 // "5 byte" pattern, over max length of 4 bytes
2312 TestError(
2313 "table T { F:string; }"
2314 "root_type T;"
2315 "{ F:\"\xFB\xBF\xBF\xBF\xBF\"}",
2316 "illegal UTF-8 sequence");
2317 // "6 byte" pattern, over max length of 4 bytes
2318 TestError(
2319 "table T { F:string; }"
2320 "root_type T;"
2321 "{ F:\"\xFD\xBF\xBF\xBF\xBF\xBF\"}",
2322 "illegal UTF-8 sequence");
2323 // "7 byte" pattern, over max length of 4 bytes
2324 TestError(
2325 "table T { F:string; }"
2326 "root_type T;"
2327 "{ F:\"\xFE\xBF\xBF\xBF\xBF\xBF\xBF\"}",
2328 "illegal UTF-8 sequence");
2329
2330 // Three invalid encodings for U+000A (\n, aka NEWLINE)
2331 TestError(
2332 "table T { F:string; }"
2333 "root_type T;"
2334 "{ F:\"\xC0\x8A\"}",
2335 "illegal UTF-8 sequence");
2336 TestError(
2337 "table T { F:string; }"
2338 "root_type T;"
2339 "{ F:\"\xE0\x80\x8A\"}",
2340 "illegal UTF-8 sequence");
2341 TestError(
2342 "table T { F:string; }"
2343 "root_type T;"
2344 "{ F:\"\xF0\x80\x80\x8A\"}",
2345 "illegal UTF-8 sequence");
2346
2347 // Two invalid encodings for U+00A9 (COPYRIGHT SYMBOL)
2348 TestError(
2349 "table T { F:string; }"
2350 "root_type T;"
2351 "{ F:\"\xE0\x81\xA9\"}",
2352 "illegal UTF-8 sequence");
2353 TestError(
2354 "table T { F:string; }"
2355 "root_type T;"
2356 "{ F:\"\xF0\x80\x81\xA9\"}",
2357 "illegal UTF-8 sequence");
2358
2359 // Invalid encoding for U+20AC (EURO SYMBOL)
2360 TestError(
2361 "table T { F:string; }"
2362 "root_type T;"
2363 "{ F:\"\xF0\x82\x82\xAC\"}",
2364 "illegal UTF-8 sequence");
2365
2366 // UTF-16 surrogate values between U+D800 and U+DFFF cannot be encoded in
2367 // UTF-8
2368 TestError(
2369 "table T { F:string; }"
2370 "root_type T;"
2371 // U+10400 "encoded" as U+D801 U+DC00
2372 "{ F:\"\xED\xA0\x81\xED\xB0\x80\"}",
2373 "illegal UTF-8 sequence");
2374
2375 // Check independence of identifier from locale.
2376 std::string locale_ident;
2377 locale_ident += "table T { F";
2378 locale_ident += static_cast<char>(-32); // unsigned 0xE0
2379 locale_ident += " :string; }";
2380 locale_ident += "root_type T;";
2381 locale_ident += "{}";
2382 TestError(locale_ident.c_str(), "");
2383 }
2384
UnknownFieldsTest()2385 void UnknownFieldsTest() {
2386 flatbuffers::IDLOptions opts;
2387 opts.skip_unexpected_fields_in_json = true;
2388 flatbuffers::Parser parser(opts);
2389
2390 TEST_EQ(parser.Parse("table T { str:string; i:int;}"
2391 "root_type T;"
2392 "{ str:\"test\","
2393 "unknown_string:\"test\","
2394 "\"unknown_string\":\"test\","
2395 "unknown_int:10,"
2396 "unknown_float:1.0,"
2397 "unknown_array: [ 1, 2, 3, 4],"
2398 "unknown_object: { i: 10 },"
2399 "\"unknown_object\": { \"i\": 10 },"
2400 "i:10}"),
2401 true);
2402
2403 std::string jsongen;
2404 parser.opts.indent_step = -1;
2405 auto result =
2406 GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
2407 TEST_EQ(result, true);
2408 TEST_EQ_STR(jsongen.c_str(), "{str: \"test\",i: 10}");
2409 }
2410
ParseUnionTest()2411 void ParseUnionTest() {
2412 // Unions must be parseable with the type field following the object.
2413 flatbuffers::Parser parser;
2414 TEST_EQ(parser.Parse("table T { A:int; }"
2415 "union U { T }"
2416 "table V { X:U; }"
2417 "root_type V;"
2418 "{ X:{ A:1 }, X_type: T }"),
2419 true);
2420 // Unions must be parsable with prefixed namespace.
2421 flatbuffers::Parser parser2;
2422 TEST_EQ(parser2.Parse("namespace N; table A {} namespace; union U { N.A }"
2423 "table B { e:U; } root_type B;"
2424 "{ e_type: N_A, e: {} }"),
2425 true);
2426 }
2427
InvalidNestedFlatbufferTest()2428 void InvalidNestedFlatbufferTest() {
2429 // First, load and parse FlatBuffer schema (.fbs)
2430 std::string schemafile;
2431 TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(),
2432 false, &schemafile),
2433 true);
2434 auto include_test_path =
2435 flatbuffers::ConCatPathFileName(test_data_path, "include_test");
2436 const char *include_directories[] = { test_data_path.c_str(),
2437 include_test_path.c_str(), nullptr };
2438 flatbuffers::Parser parser1;
2439 TEST_EQ(parser1.Parse(schemafile.c_str(), include_directories), true);
2440
2441 // "color" inside nested flatbuffer contains invalid enum value
2442 TEST_EQ(parser1.Parse("{ name: \"Bender\", testnestedflatbuffer: { name: "
2443 "\"Leela\", color: \"nonexistent\"}}"),
2444 false);
2445 }
2446
EvolutionTest()2447 void EvolutionTest() {
2448 // VS10 does not support typed enums, exclude from tests
2449 #if !defined(_MSC_VER) || _MSC_VER >= 1700
2450 const int NUM_VERSIONS = 2;
2451 std::string schemas[NUM_VERSIONS];
2452 std::string jsonfiles[NUM_VERSIONS];
2453 std::vector<uint8_t> binaries[NUM_VERSIONS];
2454
2455 flatbuffers::IDLOptions idl_opts;
2456 idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
2457 flatbuffers::Parser parser(idl_opts);
2458
2459 // Load all the schema versions and their associated data.
2460 for (int i = 0; i < NUM_VERSIONS; ++i) {
2461 std::string schema = test_data_path + "evolution_test/evolution_v" +
2462 flatbuffers::NumToString(i + 1) + ".fbs";
2463 TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i]));
2464 std::string json = test_data_path + "evolution_test/evolution_v" +
2465 flatbuffers::NumToString(i + 1) + ".json";
2466 TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i]));
2467
2468 TEST_ASSERT(parser.Parse(schemas[i].c_str()));
2469 TEST_ASSERT(parser.Parse(jsonfiles[i].c_str()));
2470
2471 auto bufLen = parser.builder_.GetSize();
2472 auto buf = parser.builder_.GetBufferPointer();
2473 binaries[i].reserve(bufLen);
2474 std::copy(buf, buf + bufLen, std::back_inserter(binaries[i]));
2475 }
2476
2477 // Assert that all the verifiers for the different schema versions properly
2478 // verify any version data.
2479 for (int i = 0; i < NUM_VERSIONS; ++i) {
2480 flatbuffers::Verifier verifier(&binaries[i].front(), binaries[i].size());
2481 TEST_ASSERT(Evolution::V1::VerifyRootBuffer(verifier));
2482 TEST_ASSERT(Evolution::V2::VerifyRootBuffer(verifier));
2483 }
2484
2485 // Test backwards compatibility by reading old data with an evolved schema.
2486 auto root_v1_viewed_from_v2 = Evolution::V2::GetRoot(&binaries[0].front());
2487 // field 'j' is new in version 2, so it should be null.
2488 TEST_ASSERT(nullptr == root_v1_viewed_from_v2->j());
2489 // field 'k' is new in version 2 with a default of 56.
2490 TEST_EQ(root_v1_viewed_from_v2->k(), 56);
2491 // field 'c' of 'TableA' is new in version 2, so it should be null.
2492 TEST_ASSERT(nullptr == root_v1_viewed_from_v2->e()->c());
2493 // 'TableC' was added to field 'c' union in version 2, so it should be null.
2494 TEST_ASSERT(nullptr == root_v1_viewed_from_v2->c_as_TableC());
2495 // The field 'c' union should be of type 'TableB' regardless of schema version
2496 TEST_ASSERT(root_v1_viewed_from_v2->c_type() == Evolution::V2::Union::TableB);
2497 // The field 'f' was renamed to 'ff' in version 2, it should still be
2498 // readable.
2499 TEST_EQ(root_v1_viewed_from_v2->ff()->a(), 16);
2500
2501 // Test forwards compatibility by reading new data with an old schema.
2502 auto root_v2_viewed_from_v1 = Evolution::V1::GetRoot(&binaries[1].front());
2503 // The field 'c' union in version 2 is a new table (index = 3) and should
2504 // still be accessible, but not interpretable.
2505 TEST_EQ(static_cast<uint8_t>(root_v2_viewed_from_v1->c_type()), 3);
2506 TEST_NOTNULL(root_v2_viewed_from_v1->c());
2507 // The field 'd' enum in verison 2 has new members and should still be
2508 // accessible, but not interpretable.
2509 TEST_EQ(static_cast<int8_t>(root_v2_viewed_from_v1->d()), 3);
2510 // The field 'a' in version 2 is deprecated and should return the default
2511 // value (0) instead of the value stored in the in the buffer (42).
2512 TEST_EQ(root_v2_viewed_from_v1->a(), 0);
2513 // The field 'ff' was originally named 'f' in version 1, it should still be
2514 // readable.
2515 TEST_EQ(root_v2_viewed_from_v1->f()->a(), 35);
2516 #endif
2517 }
2518
UnionVectorTest()2519 void UnionVectorTest() {
2520 // load FlatBuffer fbs schema and json.
2521 std::string schemafile, jsonfile;
2522 TEST_EQ(flatbuffers::LoadFile(
2523 (test_data_path + "union_vector/union_vector.fbs").c_str(), false,
2524 &schemafile),
2525 true);
2526 TEST_EQ(flatbuffers::LoadFile(
2527 (test_data_path + "union_vector/union_vector.json").c_str(),
2528 false, &jsonfile),
2529 true);
2530
2531 // parse schema.
2532 flatbuffers::IDLOptions idl_opts;
2533 idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
2534 flatbuffers::Parser parser(idl_opts);
2535 TEST_EQ(parser.Parse(schemafile.c_str()), true);
2536
2537 flatbuffers::FlatBufferBuilder fbb;
2538
2539 // union types.
2540 std::vector<uint8_t> types;
2541 types.push_back(static_cast<uint8_t>(Character_Belle));
2542 types.push_back(static_cast<uint8_t>(Character_MuLan));
2543 types.push_back(static_cast<uint8_t>(Character_BookFan));
2544 types.push_back(static_cast<uint8_t>(Character_Other));
2545 types.push_back(static_cast<uint8_t>(Character_Unused));
2546
2547 // union values.
2548 std::vector<flatbuffers::Offset<void>> characters;
2549 characters.push_back(fbb.CreateStruct(BookReader(/*books_read=*/7)).Union());
2550 characters.push_back(CreateAttacker(fbb, /*sword_attack_damage=*/5).Union());
2551 characters.push_back(fbb.CreateStruct(BookReader(/*books_read=*/2)).Union());
2552 characters.push_back(fbb.CreateString("Other").Union());
2553 characters.push_back(fbb.CreateString("Unused").Union());
2554
2555 // create Movie.
2556 const auto movie_offset =
2557 CreateMovie(fbb, Character_Rapunzel,
2558 fbb.CreateStruct(Rapunzel(/*hair_length=*/6)).Union(),
2559 fbb.CreateVector(types), fbb.CreateVector(characters));
2560 FinishMovieBuffer(fbb, movie_offset);
2561
2562 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
2563 TEST_EQ(VerifyMovieBuffer(verifier), true);
2564
2565 auto flat_movie = GetMovie(fbb.GetBufferPointer());
2566
2567 auto TestMovie = [](const Movie *movie) {
2568 TEST_EQ(movie->main_character_type() == Character_Rapunzel, true);
2569
2570 auto cts = movie->characters_type();
2571 TEST_EQ(movie->characters_type()->size(), 5);
2572 TEST_EQ(cts->GetEnum<Character>(0) == Character_Belle, true);
2573 TEST_EQ(cts->GetEnum<Character>(1) == Character_MuLan, true);
2574 TEST_EQ(cts->GetEnum<Character>(2) == Character_BookFan, true);
2575 TEST_EQ(cts->GetEnum<Character>(3) == Character_Other, true);
2576 TEST_EQ(cts->GetEnum<Character>(4) == Character_Unused, true);
2577
2578 auto rapunzel = movie->main_character_as_Rapunzel();
2579 TEST_NOTNULL(rapunzel);
2580 TEST_EQ(rapunzel->hair_length(), 6);
2581
2582 auto cs = movie->characters();
2583 TEST_EQ(cs->size(), 5);
2584 auto belle = cs->GetAs<BookReader>(0);
2585 TEST_EQ(belle->books_read(), 7);
2586 auto mu_lan = cs->GetAs<Attacker>(1);
2587 TEST_EQ(mu_lan->sword_attack_damage(), 5);
2588 auto book_fan = cs->GetAs<BookReader>(2);
2589 TEST_EQ(book_fan->books_read(), 2);
2590 auto other = cs->GetAsString(3);
2591 TEST_EQ_STR(other->c_str(), "Other");
2592 auto unused = cs->GetAsString(4);
2593 TEST_EQ_STR(unused->c_str(), "Unused");
2594 };
2595
2596 TestMovie(flat_movie);
2597
2598 // Also test the JSON we loaded above.
2599 TEST_EQ(parser.Parse(jsonfile.c_str()), true);
2600 auto jbuf = parser.builder_.GetBufferPointer();
2601 flatbuffers::Verifier jverifier(jbuf, parser.builder_.GetSize());
2602 TEST_EQ(VerifyMovieBuffer(jverifier), true);
2603 TestMovie(GetMovie(jbuf));
2604
2605 auto movie_object = flat_movie->UnPack();
2606 TEST_EQ(movie_object->main_character.AsRapunzel()->hair_length(), 6);
2607 TEST_EQ(movie_object->characters[0].AsBelle()->books_read(), 7);
2608 TEST_EQ(movie_object->characters[1].AsMuLan()->sword_attack_damage, 5);
2609 TEST_EQ(movie_object->characters[2].AsBookFan()->books_read(), 2);
2610 TEST_EQ_STR(movie_object->characters[3].AsOther()->c_str(), "Other");
2611 TEST_EQ_STR(movie_object->characters[4].AsUnused()->c_str(), "Unused");
2612
2613 fbb.Clear();
2614 fbb.Finish(Movie::Pack(fbb, movie_object));
2615
2616 delete movie_object;
2617
2618 auto repacked_movie = GetMovie(fbb.GetBufferPointer());
2619
2620 TestMovie(repacked_movie);
2621
2622 // Generate text using mini-reflection.
2623 auto s =
2624 flatbuffers::FlatBufferToString(fbb.GetBufferPointer(), MovieTypeTable());
2625 TEST_EQ_STR(
2626 s.c_str(),
2627 "{ main_character_type: Rapunzel, main_character: { hair_length: 6 }, "
2628 "characters_type: [ Belle, MuLan, BookFan, Other, Unused ], "
2629 "characters: [ { books_read: 7 }, { sword_attack_damage: 5 }, "
2630 "{ books_read: 2 }, \"Other\", \"Unused\" ] }");
2631
2632 flatbuffers::ToStringVisitor visitor("\n", true, " ");
2633 IterateFlatBuffer(fbb.GetBufferPointer(), MovieTypeTable(), &visitor);
2634 TEST_EQ_STR(visitor.s.c_str(),
2635 "{\n"
2636 " \"main_character_type\": \"Rapunzel\",\n"
2637 " \"main_character\": {\n"
2638 " \"hair_length\": 6\n"
2639 " },\n"
2640 " \"characters_type\": [\n"
2641 " \"Belle\",\n"
2642 " \"MuLan\",\n"
2643 " \"BookFan\",\n"
2644 " \"Other\",\n"
2645 " \"Unused\"\n"
2646 " ],\n"
2647 " \"characters\": [\n"
2648 " {\n"
2649 " \"books_read\": 7\n"
2650 " },\n"
2651 " {\n"
2652 " \"sword_attack_damage\": 5\n"
2653 " },\n"
2654 " {\n"
2655 " \"books_read\": 2\n"
2656 " },\n"
2657 " \"Other\",\n"
2658 " \"Unused\"\n"
2659 " ]\n"
2660 "}");
2661
2662 // Generate text using parsed schema.
2663 std::string jsongen;
2664 auto result = GenerateText(parser, fbb.GetBufferPointer(), &jsongen);
2665 TEST_EQ(result, true);
2666 TEST_EQ_STR(jsongen.c_str(),
2667 "{\n"
2668 " main_character_type: \"Rapunzel\",\n"
2669 " main_character: {\n"
2670 " hair_length: 6\n"
2671 " },\n"
2672 " characters_type: [\n"
2673 " \"Belle\",\n"
2674 " \"MuLan\",\n"
2675 " \"BookFan\",\n"
2676 " \"Other\",\n"
2677 " \"Unused\"\n"
2678 " ],\n"
2679 " characters: [\n"
2680 " {\n"
2681 " books_read: 7\n"
2682 " },\n"
2683 " {\n"
2684 " sword_attack_damage: 5\n"
2685 " },\n"
2686 " {\n"
2687 " books_read: 2\n"
2688 " },\n"
2689 " \"Other\",\n"
2690 " \"Unused\"\n"
2691 " ]\n"
2692 "}\n");
2693
2694 // Simple test with reflection.
2695 parser.Serialize();
2696 auto schema = reflection::GetSchema(parser.builder_.GetBufferPointer());
2697 auto ok = flatbuffers::Verify(*schema, *schema->root_table(),
2698 fbb.GetBufferPointer(), fbb.GetSize());
2699 TEST_EQ(ok, true);
2700
2701 flatbuffers::Parser parser2(idl_opts);
2702 TEST_EQ(parser2.Parse("struct Bool { b:bool; }"
2703 "union Any { Bool }"
2704 "table Root { a:Any; }"
2705 "root_type Root;"),
2706 true);
2707 TEST_EQ(parser2.Parse("{a_type:Bool,a:{b:true}}"), true);
2708 }
2709
ConformTest()2710 void ConformTest() {
2711 flatbuffers::Parser parser;
2712 TEST_EQ(parser.Parse("table T { A:int; } enum E:byte { A }"), true);
2713
2714 auto test_conform = [](flatbuffers::Parser &parser1, const char *test,
2715 const char *expected_err) {
2716 flatbuffers::Parser parser2;
2717 TEST_EQ(parser2.Parse(test), true);
2718 auto err = parser2.ConformTo(parser1);
2719 TEST_NOTNULL(strstr(err.c_str(), expected_err));
2720 };
2721
2722 test_conform(parser, "table T { A:byte; }", "types differ for field");
2723 test_conform(parser, "table T { B:int; A:int; }", "offsets differ for field");
2724 test_conform(parser, "table T { A:int = 1; }", "defaults differ for field");
2725 test_conform(parser, "table T { B:float; }",
2726 "field renamed to different type");
2727 test_conform(parser, "enum E:byte { B, A }", "values differ for enum");
2728 }
2729
ParseProtoBufAsciiTest()2730 void ParseProtoBufAsciiTest() {
2731 // We can put the parser in a mode where it will accept JSON that looks more
2732 // like Protobuf ASCII, for users that have data in that format.
2733 // This uses no "" for field names (which we already support by default,
2734 // omits `,`, `:` before `{` and a couple of other features.
2735 flatbuffers::Parser parser;
2736 parser.opts.protobuf_ascii_alike = true;
2737 TEST_EQ(
2738 parser.Parse("table S { B:int; } table T { A:[int]; C:S; } root_type T;"),
2739 true);
2740 TEST_EQ(parser.Parse("{ A [1 2] C { B:2 }}"), true);
2741 // Similarly, in text output, it should omit these.
2742 std::string text;
2743 auto ok = flatbuffers::GenerateText(
2744 parser, parser.builder_.GetBufferPointer(), &text);
2745 TEST_EQ(ok, true);
2746 TEST_EQ_STR(text.c_str(),
2747 "{\n A [\n 1\n 2\n ]\n C {\n B: 2\n }\n}\n");
2748 }
2749
FlexBuffersTest()2750 void FlexBuffersTest() {
2751 flexbuffers::Builder slb(512,
2752 flexbuffers::BUILDER_FLAG_SHARE_KEYS_AND_STRINGS);
2753
2754 // Write the equivalent of:
2755 // { vec: [ -100, "Fred", 4.0, false ], bar: [ 1, 2, 3 ], bar3: [ 1, 2, 3 ],
2756 // foo: 100, bool: true, mymap: { foo: "Fred" } }
2757 // clang-format off
2758 #ifndef FLATBUFFERS_CPP98_STL
2759 // It's possible to do this without std::function support as well.
2760 slb.Map([&]() {
2761 slb.Vector("vec", [&]() {
2762 slb += -100; // Equivalent to slb.Add(-100) or slb.Int(-100);
2763 slb += "Fred";
2764 slb.IndirectFloat(4.0f);
2765 auto i_f = slb.LastValue();
2766 uint8_t blob[] = { 77 };
2767 slb.Blob(blob, 1);
2768 slb += false;
2769 slb.ReuseValue(i_f);
2770 });
2771 int ints[] = { 1, 2, 3 };
2772 slb.Vector("bar", ints, 3);
2773 slb.FixedTypedVector("bar3", ints, 3);
2774 bool bools[] = {true, false, true, false};
2775 slb.Vector("bools", bools, 4);
2776 slb.Bool("bool", true);
2777 slb.Double("foo", 100);
2778 slb.Map("mymap", [&]() {
2779 slb.String("foo", "Fred"); // Testing key and string reuse.
2780 });
2781 });
2782 slb.Finish();
2783 #else
2784 // It's possible to do this without std::function support as well.
2785 slb.Map([](flexbuffers::Builder& slb2) {
2786 slb2.Vector("vec", [](flexbuffers::Builder& slb3) {
2787 slb3 += -100; // Equivalent to slb.Add(-100) or slb.Int(-100);
2788 slb3 += "Fred";
2789 slb3.IndirectFloat(4.0f);
2790 auto i_f = slb3.LastValue();
2791 uint8_t blob[] = { 77 };
2792 slb3.Blob(blob, 1);
2793 slb3 += false;
2794 slb3.ReuseValue(i_f);
2795 }, slb2);
2796 int ints[] = { 1, 2, 3 };
2797 slb2.Vector("bar", ints, 3);
2798 slb2.FixedTypedVector("bar3", ints, 3);
2799 slb2.Bool("bool", true);
2800 slb2.Double("foo", 100);
2801 slb2.Map("mymap", [](flexbuffers::Builder& slb3) {
2802 slb3.String("foo", "Fred"); // Testing key and string reuse.
2803 }, slb2);
2804 }, slb);
2805 slb.Finish();
2806 #endif // FLATBUFFERS_CPP98_STL
2807
2808 #ifdef FLATBUFFERS_TEST_VERBOSE
2809 for (size_t i = 0; i < slb.GetBuffer().size(); i++)
2810 printf("%d ", flatbuffers::vector_data(slb.GetBuffer())[i]);
2811 printf("\n");
2812 #endif
2813 // clang-format on
2814
2815 auto map = flexbuffers::GetRoot(slb.GetBuffer()).AsMap();
2816 TEST_EQ(map.size(), 7);
2817 auto vec = map["vec"].AsVector();
2818 TEST_EQ(vec.size(), 6);
2819 TEST_EQ(vec[0].AsInt64(), -100);
2820 TEST_EQ_STR(vec[1].AsString().c_str(), "Fred");
2821 TEST_EQ(vec[1].AsInt64(), 0); // Number parsing failed.
2822 TEST_EQ(vec[2].AsDouble(), 4.0);
2823 TEST_EQ(vec[2].AsString().IsTheEmptyString(), true); // Wrong Type.
2824 TEST_EQ_STR(vec[2].AsString().c_str(), ""); // This still works though.
2825 TEST_EQ_STR(vec[2].ToString().c_str(), "4.0"); // Or have it converted.
2826 // Few tests for templated version of As.
2827 TEST_EQ(vec[0].As<int64_t>(), -100);
2828 TEST_EQ_STR(vec[1].As<std::string>().c_str(), "Fred");
2829 TEST_EQ(vec[1].As<int64_t>(), 0); // Number parsing failed.
2830 TEST_EQ(vec[2].As<double>(), 4.0);
2831 // Test that the blob can be accessed.
2832 TEST_EQ(vec[3].IsBlob(), true);
2833 auto blob = vec[3].AsBlob();
2834 TEST_EQ(blob.size(), 1);
2835 TEST_EQ(blob.data()[0], 77);
2836 TEST_EQ(vec[4].IsBool(), true); // Check if type is a bool
2837 TEST_EQ(vec[4].AsBool(), false); // Check if value is false
2838 TEST_EQ(vec[5].AsDouble(), 4.0); // This is shared with vec[2] !
2839 auto tvec = map["bar"].AsTypedVector();
2840 TEST_EQ(tvec.size(), 3);
2841 TEST_EQ(tvec[2].AsInt8(), 3);
2842 auto tvec3 = map["bar3"].AsFixedTypedVector();
2843 TEST_EQ(tvec3.size(), 3);
2844 TEST_EQ(tvec3[2].AsInt8(), 3);
2845 TEST_EQ(map["bool"].AsBool(), true);
2846 auto tvecb = map["bools"].AsTypedVector();
2847 TEST_EQ(tvecb.ElementType(), flexbuffers::FBT_BOOL);
2848 TEST_EQ(map["foo"].AsUInt8(), 100);
2849 TEST_EQ(map["unknown"].IsNull(), true);
2850 auto mymap = map["mymap"].AsMap();
2851 // These should be equal by pointer equality, since key and value are shared.
2852 TEST_EQ(mymap.Keys()[0].AsKey(), map.Keys()[4].AsKey());
2853 TEST_EQ(mymap.Values()[0].AsString().c_str(), vec[1].AsString().c_str());
2854 // We can mutate values in the buffer.
2855 TEST_EQ(vec[0].MutateInt(-99), true);
2856 TEST_EQ(vec[0].AsInt64(), -99);
2857 TEST_EQ(vec[1].MutateString("John"), true); // Size must match.
2858 TEST_EQ_STR(vec[1].AsString().c_str(), "John");
2859 TEST_EQ(vec[1].MutateString("Alfred"), false); // Too long.
2860 TEST_EQ(vec[2].MutateFloat(2.0f), true);
2861 TEST_EQ(vec[2].AsFloat(), 2.0f);
2862 TEST_EQ(vec[2].MutateFloat(3.14159), false); // Double does not fit in float.
2863 TEST_EQ(vec[4].AsBool(), false); // Is false before change
2864 TEST_EQ(vec[4].MutateBool(true), true); // Can change a bool
2865 TEST_EQ(vec[4].AsBool(), true); // Changed bool is now true
2866
2867 // Parse from JSON:
2868 flatbuffers::Parser parser;
2869 slb.Clear();
2870 auto jsontest = "{ a: [ 123, 456.0 ], b: \"hello\", c: true, d: false }";
2871 TEST_EQ(parser.ParseFlexBuffer(jsontest, nullptr, &slb), true);
2872 auto jroot = flexbuffers::GetRoot(slb.GetBuffer());
2873 auto jmap = jroot.AsMap();
2874 auto jvec = jmap["a"].AsVector();
2875 TEST_EQ(jvec[0].AsInt64(), 123);
2876 TEST_EQ(jvec[1].AsDouble(), 456.0);
2877 TEST_EQ_STR(jmap["b"].AsString().c_str(), "hello");
2878 TEST_EQ(jmap["c"].IsBool(), true); // Parsed correctly to a bool
2879 TEST_EQ(jmap["c"].AsBool(), true); // Parsed correctly to true
2880 TEST_EQ(jmap["d"].IsBool(), true); // Parsed correctly to a bool
2881 TEST_EQ(jmap["d"].AsBool(), false); // Parsed correctly to false
2882 // And from FlexBuffer back to JSON:
2883 auto jsonback = jroot.ToString();
2884 TEST_EQ_STR(jsontest, jsonback.c_str());
2885 }
2886
FlexBuffersDeprecatedTest()2887 void FlexBuffersDeprecatedTest() {
2888 // FlexBuffers as originally designed had a flaw involving the
2889 // FBT_VECTOR_STRING datatype, and this test documents/tests the fix for it.
2890 // Discussion: https://github.com/google/flatbuffers/issues/5627
2891 flexbuffers::Builder slb;
2892 // FBT_VECTOR_* are "typed vectors" where all elements are of the same type.
2893 // Problem is, when storing FBT_STRING elements, it relies on that type to
2894 // get the bit-width for the size field of the string, which in this case
2895 // isn't present, and instead defaults to 8-bit. This means that any strings
2896 // stored inside such a vector, when accessed thru the old API that returns
2897 // a String reference, will appear to be truncated if the string stored is
2898 // actually >=256 bytes.
2899 std::string test_data(300, 'A');
2900 auto start = slb.StartVector();
2901 // This one will have a 16-bit size field.
2902 slb.String(test_data);
2903 // This one will have an 8-bit size field.
2904 slb.String("hello");
2905 // We're asking this to be serialized as a typed vector (true), but not
2906 // fixed size (false). The type will be FBT_VECTOR_STRING with a bit-width
2907 // of whatever the offsets in the vector need, the bit-widths of the strings
2908 // are not stored(!) <- the actual design flaw.
2909 // Note that even in the fixed code, we continue to serialize the elements of
2910 // FBT_VECTOR_STRING as FBT_STRING, since there may be old code out there
2911 // reading new data that we want to continue to function.
2912 // Thus, FBT_VECTOR_STRING, while deprecated, will always be represented the
2913 // same way, the fix lies on the reading side.
2914 slb.EndVector(start, true, false);
2915 slb.Finish();
2916 // So now lets read this data back.
2917 // For existing data, since we have no way of knowing what the actual
2918 // bit-width of the size field of the string is, we are going to ignore this
2919 // field, and instead treat these strings as FBT_KEY (null-terminated), so we
2920 // can deal with strings of arbitrary length. This of course truncates strings
2921 // with embedded nulls, but we think that that is preferrable over truncating
2922 // strings >= 256 bytes.
2923 auto vec = flexbuffers::GetRoot(slb.GetBuffer()).AsTypedVector();
2924 // Even though this was serialized as FBT_VECTOR_STRING, it is read as
2925 // FBT_VECTOR_KEY:
2926 TEST_EQ(vec.ElementType(), flexbuffers::FBT_KEY);
2927 // Access the long string. Previously, this would return a string of size 1,
2928 // since it would read the high-byte of the 16-bit length.
2929 // This should now correctly test the full 300 bytes, using AsKey():
2930 TEST_EQ_STR(vec[0].AsKey(), test_data.c_str());
2931 // Old code that called AsString will continue to work, as the String
2932 // accessor objects now use a cached size that can come from a key as well.
2933 TEST_EQ_STR(vec[0].AsString().c_str(), test_data.c_str());
2934 // Short strings work as before:
2935 TEST_EQ_STR(vec[1].AsKey(), "hello");
2936 TEST_EQ_STR(vec[1].AsString().c_str(), "hello");
2937 // So, while existing code and data mostly "just work" with the fixes applied
2938 // to AsTypedVector and AsString, what do you do going forward?
2939 // Code accessing existing data doesn't necessarily need to change, though
2940 // you could consider using AsKey instead of AsString for a) documenting
2941 // that you are accessing keys, or b) a speedup if you don't actually use
2942 // the string size.
2943 // For new data, or data that doesn't need to be backwards compatible,
2944 // instead serialize as FBT_VECTOR (call EndVector with typed = false, then
2945 // read elements with AsString), or, for maximum compactness, use
2946 // FBT_VECTOR_KEY (call slb.Key above instead, read with AsKey or AsString).
2947 }
2948
TypeAliasesTest()2949 void TypeAliasesTest() {
2950 flatbuffers::FlatBufferBuilder builder;
2951
2952 builder.Finish(CreateTypeAliases(
2953 builder, flatbuffers::numeric_limits<int8_t>::min(),
2954 flatbuffers::numeric_limits<uint8_t>::max(),
2955 flatbuffers::numeric_limits<int16_t>::min(),
2956 flatbuffers::numeric_limits<uint16_t>::max(),
2957 flatbuffers::numeric_limits<int32_t>::min(),
2958 flatbuffers::numeric_limits<uint32_t>::max(),
2959 flatbuffers::numeric_limits<int64_t>::min(),
2960 flatbuffers::numeric_limits<uint64_t>::max(), 2.3f, 2.3));
2961
2962 auto p = builder.GetBufferPointer();
2963 auto ta = flatbuffers::GetRoot<TypeAliases>(p);
2964
2965 TEST_EQ(ta->i8(), flatbuffers::numeric_limits<int8_t>::min());
2966 TEST_EQ(ta->u8(), flatbuffers::numeric_limits<uint8_t>::max());
2967 TEST_EQ(ta->i16(), flatbuffers::numeric_limits<int16_t>::min());
2968 TEST_EQ(ta->u16(), flatbuffers::numeric_limits<uint16_t>::max());
2969 TEST_EQ(ta->i32(), flatbuffers::numeric_limits<int32_t>::min());
2970 TEST_EQ(ta->u32(), flatbuffers::numeric_limits<uint32_t>::max());
2971 TEST_EQ(ta->i64(), flatbuffers::numeric_limits<int64_t>::min());
2972 TEST_EQ(ta->u64(), flatbuffers::numeric_limits<uint64_t>::max());
2973 TEST_EQ(ta->f32(), 2.3f);
2974 TEST_EQ(ta->f64(), 2.3);
2975 using namespace flatbuffers; // is_same
2976 static_assert(is_same<decltype(ta->i8()), int8_t>::value, "invalid type");
2977 static_assert(is_same<decltype(ta->i16()), int16_t>::value, "invalid type");
2978 static_assert(is_same<decltype(ta->i32()), int32_t>::value, "invalid type");
2979 static_assert(is_same<decltype(ta->i64()), int64_t>::value, "invalid type");
2980 static_assert(is_same<decltype(ta->u8()), uint8_t>::value, "invalid type");
2981 static_assert(is_same<decltype(ta->u16()), uint16_t>::value, "invalid type");
2982 static_assert(is_same<decltype(ta->u32()), uint32_t>::value, "invalid type");
2983 static_assert(is_same<decltype(ta->u64()), uint64_t>::value, "invalid type");
2984 static_assert(is_same<decltype(ta->f32()), float>::value, "invalid type");
2985 static_assert(is_same<decltype(ta->f64()), double>::value, "invalid type");
2986 }
2987
EndianSwapTest()2988 void EndianSwapTest() {
2989 TEST_EQ(flatbuffers::EndianSwap(static_cast<int16_t>(0x1234)), 0x3412);
2990 TEST_EQ(flatbuffers::EndianSwap(static_cast<int32_t>(0x12345678)),
2991 0x78563412);
2992 TEST_EQ(flatbuffers::EndianSwap(static_cast<int64_t>(0x1234567890ABCDEF)),
2993 0xEFCDAB9078563412);
2994 TEST_EQ(flatbuffers::EndianSwap(flatbuffers::EndianSwap(3.14f)), 3.14f);
2995 }
2996
UninitializedVectorTest()2997 void UninitializedVectorTest() {
2998 flatbuffers::FlatBufferBuilder builder;
2999
3000 Test *buf = nullptr;
3001 auto vector_offset =
3002 builder.CreateUninitializedVectorOfStructs<Test>(2, &buf);
3003 TEST_NOTNULL(buf);
3004 buf[0] = Test(10, 20);
3005 buf[1] = Test(30, 40);
3006
3007 auto required_name = builder.CreateString("myMonster");
3008 auto monster_builder = MonsterBuilder(builder);
3009 monster_builder.add_name(
3010 required_name); // required field mandated for monster.
3011 monster_builder.add_test4(vector_offset);
3012 builder.Finish(monster_builder.Finish());
3013
3014 auto p = builder.GetBufferPointer();
3015 auto uvt = flatbuffers::GetRoot<Monster>(p);
3016 TEST_NOTNULL(uvt);
3017 auto vec = uvt->test4();
3018 TEST_NOTNULL(vec);
3019 auto test_0 = vec->Get(0);
3020 auto test_1 = vec->Get(1);
3021 TEST_EQ(test_0->a(), 10);
3022 TEST_EQ(test_0->b(), 20);
3023 TEST_EQ(test_1->a(), 30);
3024 TEST_EQ(test_1->b(), 40);
3025 }
3026
EqualOperatorTest()3027 void EqualOperatorTest() {
3028 MonsterT a;
3029 MonsterT b;
3030 TEST_EQ(b == a, true);
3031 TEST_EQ(b != a, false);
3032
3033 b.mana = 33;
3034 TEST_EQ(b == a, false);
3035 TEST_EQ(b != a, true);
3036 b.mana = 150;
3037 TEST_EQ(b == a, true);
3038 TEST_EQ(b != a, false);
3039
3040 b.inventory.push_back(3);
3041 TEST_EQ(b == a, false);
3042 TEST_EQ(b != a, true);
3043 b.inventory.clear();
3044 TEST_EQ(b == a, true);
3045 TEST_EQ(b != a, false);
3046
3047 b.test.type = Any_Monster;
3048 TEST_EQ(b == a, false);
3049 TEST_EQ(b != a, true);
3050 }
3051
3052 // For testing any binaries, e.g. from fuzzing.
LoadVerifyBinaryTest()3053 void LoadVerifyBinaryTest() {
3054 std::string binary;
3055 if (flatbuffers::LoadFile(
3056 (test_data_path + "fuzzer/your-filename-here").c_str(), true,
3057 &binary)) {
3058 flatbuffers::Verifier verifier(
3059 reinterpret_cast<const uint8_t *>(binary.data()), binary.size());
3060 TEST_EQ(VerifyMonsterBuffer(verifier), true);
3061 }
3062 }
3063
CreateSharedStringTest()3064 void CreateSharedStringTest() {
3065 flatbuffers::FlatBufferBuilder builder;
3066 const auto one1 = builder.CreateSharedString("one");
3067 const auto two = builder.CreateSharedString("two");
3068 const auto one2 = builder.CreateSharedString("one");
3069 TEST_EQ(one1.o, one2.o);
3070 const auto onetwo = builder.CreateSharedString("onetwo");
3071 TEST_EQ(onetwo.o != one1.o, true);
3072 TEST_EQ(onetwo.o != two.o, true);
3073
3074 // Support for embedded nulls
3075 const char chars_b[] = { 'a', '\0', 'b' };
3076 const char chars_c[] = { 'a', '\0', 'c' };
3077 const auto null_b1 = builder.CreateSharedString(chars_b, sizeof(chars_b));
3078 const auto null_c = builder.CreateSharedString(chars_c, sizeof(chars_c));
3079 const auto null_b2 = builder.CreateSharedString(chars_b, sizeof(chars_b));
3080 TEST_EQ(null_b1.o != null_c.o, true); // Issue#5058 repro
3081 TEST_EQ(null_b1.o, null_b2.o);
3082
3083 // Put the strings into an array for round trip verification.
3084 const flatbuffers::Offset<flatbuffers::String> array[7] = {
3085 one1, two, one2, onetwo, null_b1, null_c, null_b2
3086 };
3087 const auto vector_offset =
3088 builder.CreateVector(array, flatbuffers::uoffset_t(7));
3089 MonsterBuilder monster_builder(builder);
3090 monster_builder.add_name(two);
3091 monster_builder.add_testarrayofstring(vector_offset);
3092 builder.Finish(monster_builder.Finish());
3093
3094 // Read the Monster back.
3095 const auto *monster =
3096 flatbuffers::GetRoot<Monster>(builder.GetBufferPointer());
3097 TEST_EQ_STR(monster->name()->c_str(), "two");
3098 const auto *testarrayofstring = monster->testarrayofstring();
3099 TEST_EQ(testarrayofstring->size(), flatbuffers::uoffset_t(7));
3100 const auto &a = *testarrayofstring;
3101 TEST_EQ_STR(a[0]->c_str(), "one");
3102 TEST_EQ_STR(a[1]->c_str(), "two");
3103 TEST_EQ_STR(a[2]->c_str(), "one");
3104 TEST_EQ_STR(a[3]->c_str(), "onetwo");
3105 TEST_EQ(a[4]->str(), (std::string(chars_b, sizeof(chars_b))));
3106 TEST_EQ(a[5]->str(), (std::string(chars_c, sizeof(chars_c))));
3107 TEST_EQ(a[6]->str(), (std::string(chars_b, sizeof(chars_b))));
3108
3109 // Make sure String::operator< works, too, since it is related to
3110 // StringOffsetCompare.
3111 TEST_EQ((*a[0]) < (*a[1]), true);
3112 TEST_EQ((*a[1]) < (*a[0]), false);
3113 TEST_EQ((*a[1]) < (*a[2]), false);
3114 TEST_EQ((*a[2]) < (*a[1]), true);
3115 TEST_EQ((*a[4]) < (*a[3]), true);
3116 TEST_EQ((*a[5]) < (*a[4]), false);
3117 TEST_EQ((*a[5]) < (*a[4]), false);
3118 TEST_EQ((*a[6]) < (*a[5]), true);
3119 }
3120
FixedLengthArrayTest()3121 void FixedLengthArrayTest() {
3122 // VS10 does not support typed enums, exclude from tests
3123 #if !defined(_MSC_VER) || _MSC_VER >= 1700
3124 // Generate an ArrayTable containing one ArrayStruct.
3125 flatbuffers::FlatBufferBuilder fbb;
3126 MyGame::Example::NestedStruct nStruct0(MyGame::Example::TestEnum::B);
3127 TEST_NOTNULL(nStruct0.mutable_a());
3128 nStruct0.mutable_a()->Mutate(0, 1);
3129 nStruct0.mutable_a()->Mutate(1, 2);
3130 TEST_NOTNULL(nStruct0.mutable_c());
3131 nStruct0.mutable_c()->Mutate(0, MyGame::Example::TestEnum::C);
3132 nStruct0.mutable_c()->Mutate(1, MyGame::Example::TestEnum::A);
3133 TEST_NOTNULL(nStruct0.mutable_d());
3134 nStruct0.mutable_d()->Mutate(0, flatbuffers::numeric_limits<int64_t>::max());
3135 nStruct0.mutable_d()->Mutate(1, flatbuffers::numeric_limits<int64_t>::min());
3136 MyGame::Example::NestedStruct nStruct1(MyGame::Example::TestEnum::C);
3137 TEST_NOTNULL(nStruct1.mutable_a());
3138 nStruct1.mutable_a()->Mutate(0, 3);
3139 nStruct1.mutable_a()->Mutate(1, 4);
3140 TEST_NOTNULL(nStruct1.mutable_c());
3141 nStruct1.mutable_c()->Mutate(0, MyGame::Example::TestEnum::C);
3142 nStruct1.mutable_c()->Mutate(1, MyGame::Example::TestEnum::A);
3143 TEST_NOTNULL(nStruct1.mutable_d());
3144 nStruct1.mutable_d()->Mutate(0, flatbuffers::numeric_limits<int64_t>::min());
3145 nStruct1.mutable_d()->Mutate(1, flatbuffers::numeric_limits<int64_t>::max());
3146 MyGame::Example::ArrayStruct aStruct(2, 12, 1);
3147 TEST_NOTNULL(aStruct.b());
3148 TEST_NOTNULL(aStruct.mutable_b());
3149 TEST_NOTNULL(aStruct.mutable_d());
3150 TEST_NOTNULL(aStruct.mutable_f());
3151 for (int i = 0; i < aStruct.b()->size(); i++)
3152 aStruct.mutable_b()->Mutate(i, i + 1);
3153 aStruct.mutable_d()->Mutate(0, nStruct0);
3154 aStruct.mutable_d()->Mutate(1, nStruct1);
3155 auto aTable = MyGame::Example::CreateArrayTable(fbb, &aStruct);
3156 fbb.Finish(aTable);
3157
3158 // Verify correctness of the ArrayTable.
3159 flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
3160 MyGame::Example::VerifyArrayTableBuffer(verifier);
3161 auto p = MyGame::Example::GetMutableArrayTable(fbb.GetBufferPointer());
3162 auto mArStruct = p->mutable_a();
3163 TEST_NOTNULL(mArStruct);
3164 TEST_NOTNULL(mArStruct->b());
3165 TEST_NOTNULL(mArStruct->d());
3166 TEST_NOTNULL(mArStruct->f());
3167 TEST_NOTNULL(mArStruct->mutable_b());
3168 TEST_NOTNULL(mArStruct->mutable_d());
3169 TEST_NOTNULL(mArStruct->mutable_f());
3170 mArStruct->mutable_b()->Mutate(14, -14);
3171 TEST_EQ(mArStruct->a(), 2);
3172 TEST_EQ(mArStruct->b()->size(), 15);
3173 TEST_EQ(mArStruct->b()->Get(aStruct.b()->size() - 1), -14);
3174 TEST_EQ(mArStruct->c(), 12);
3175 TEST_NOTNULL(mArStruct->d()->Get(0));
3176 TEST_NOTNULL(mArStruct->d()->Get(0)->a());
3177 TEST_EQ(mArStruct->d()->Get(0)->a()->Get(0), 1);
3178 TEST_EQ(mArStruct->d()->Get(0)->a()->Get(1), 2);
3179 TEST_NOTNULL(mArStruct->d()->Get(1));
3180 TEST_NOTNULL(mArStruct->d()->Get(1)->a());
3181 TEST_EQ(mArStruct->d()->Get(1)->a()->Get(0), 3);
3182 TEST_EQ(mArStruct->d()->Get(1)->a()->Get(1), 4);
3183 TEST_NOTNULL(mArStruct->mutable_d()->GetMutablePointer(1));
3184 TEST_NOTNULL(mArStruct->mutable_d()->GetMutablePointer(1)->mutable_a());
3185 mArStruct->mutable_d()->GetMutablePointer(1)->mutable_a()->Mutate(1, 5);
3186 TEST_EQ(5, mArStruct->d()->Get(1)->a()->Get(1));
3187 TEST_EQ(MyGame::Example::TestEnum::B, mArStruct->d()->Get(0)->b());
3188 TEST_NOTNULL(mArStruct->d()->Get(0)->c());
3189 TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(0)->c()->Get(0));
3190 TEST_EQ(MyGame::Example::TestEnum::A, mArStruct->d()->Get(0)->c()->Get(1));
3191 TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(),
3192 mArStruct->d()->Get(0)->d()->Get(0));
3193 TEST_EQ(flatbuffers::numeric_limits<int64_t>::min(),
3194 mArStruct->d()->Get(0)->d()->Get(1));
3195 TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(1)->b());
3196 TEST_NOTNULL(mArStruct->d()->Get(1)->c());
3197 TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(1)->c()->Get(0));
3198 TEST_EQ(MyGame::Example::TestEnum::A, mArStruct->d()->Get(1)->c()->Get(1));
3199 TEST_EQ(flatbuffers::numeric_limits<int64_t>::min(),
3200 mArStruct->d()->Get(1)->d()->Get(0));
3201 TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(),
3202 mArStruct->d()->Get(1)->d()->Get(1));
3203 for (int i = 0; i < mArStruct->b()->size() - 1; i++)
3204 TEST_EQ(mArStruct->b()->Get(i), i + 1);
3205 // Check alignment
3206 TEST_EQ(0, reinterpret_cast<uintptr_t>(mArStruct->d()) % 8);
3207 TEST_EQ(0, reinterpret_cast<uintptr_t>(mArStruct->f()) % 8);
3208 #endif
3209 }
3210
NativeTypeTest()3211 void NativeTypeTest() {
3212 const int N = 3;
3213
3214 Geometry::ApplicationDataT src_data;
3215 src_data.vectors.reserve(N);
3216
3217 for (int i = 0; i < N; ++i) {
3218 src_data.vectors.push_back(
3219 Native::Vector3D(10 * i + 0.1f, 10 * i + 0.2f, 10 * i + 0.3f));
3220 }
3221
3222 flatbuffers::FlatBufferBuilder fbb;
3223 fbb.Finish(Geometry::ApplicationData::Pack(fbb, &src_data));
3224
3225 auto dstDataT = Geometry::UnPackApplicationData(fbb.GetBufferPointer());
3226
3227 for (int i = 0; i < N; ++i) {
3228 Native::Vector3D &v = dstDataT->vectors[i];
3229 TEST_EQ(v.x, 10 * i + 0.1f);
3230 TEST_EQ(v.y, 10 * i + 0.2f);
3231 TEST_EQ(v.z, 10 * i + 0.3f);
3232 }
3233 }
3234
FixedLengthArrayJsonTest(bool binary)3235 void FixedLengthArrayJsonTest(bool binary) {
3236 // VS10 does not support typed enums, exclude from tests
3237 #if !defined(_MSC_VER) || _MSC_VER >= 1700
3238 // load FlatBuffer schema (.fbs) and JSON from disk
3239 std::string schemafile;
3240 std::string jsonfile;
3241 TEST_EQ(
3242 flatbuffers::LoadFile(
3243 (test_data_path + "arrays_test." + (binary ? "bfbs" : "fbs")).c_str(),
3244 binary, &schemafile),
3245 true);
3246 TEST_EQ(flatbuffers::LoadFile((test_data_path + "arrays_test.golden").c_str(),
3247 false, &jsonfile),
3248 true);
3249
3250 // parse schema first, so we can use it to parse the data after
3251 flatbuffers::Parser parserOrg, parserGen;
3252 if (binary) {
3253 flatbuffers::Verifier verifier(
3254 reinterpret_cast<const uint8_t *>(schemafile.c_str()),
3255 schemafile.size());
3256 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
3257 TEST_EQ(parserOrg.Deserialize((const uint8_t *)schemafile.c_str(),
3258 schemafile.size()),
3259 true);
3260 TEST_EQ(parserGen.Deserialize((const uint8_t *)schemafile.c_str(),
3261 schemafile.size()),
3262 true);
3263 } else {
3264 TEST_EQ(parserOrg.Parse(schemafile.c_str()), true);
3265 TEST_EQ(parserGen.Parse(schemafile.c_str()), true);
3266 }
3267 TEST_EQ(parserOrg.Parse(jsonfile.c_str()), true);
3268
3269 // First, verify it, just in case:
3270 flatbuffers::Verifier verifierOrg(parserOrg.builder_.GetBufferPointer(),
3271 parserOrg.builder_.GetSize());
3272 TEST_EQ(VerifyArrayTableBuffer(verifierOrg), true);
3273
3274 // Export to JSON
3275 std::string jsonGen;
3276 TEST_EQ(
3277 GenerateText(parserOrg, parserOrg.builder_.GetBufferPointer(), &jsonGen),
3278 true);
3279
3280 // Import from JSON
3281 TEST_EQ(parserGen.Parse(jsonGen.c_str()), true);
3282
3283 // Verify buffer from generated JSON
3284 flatbuffers::Verifier verifierGen(parserGen.builder_.GetBufferPointer(),
3285 parserGen.builder_.GetSize());
3286 TEST_EQ(VerifyArrayTableBuffer(verifierGen), true);
3287
3288 // Compare generated buffer to original
3289 TEST_EQ(parserOrg.builder_.GetSize(), parserGen.builder_.GetSize());
3290 TEST_EQ(std::memcmp(parserOrg.builder_.GetBufferPointer(),
3291 parserGen.builder_.GetBufferPointer(),
3292 parserOrg.builder_.GetSize()),
3293 0);
3294 #else
3295 (void)binary;
3296 #endif
3297 }
3298
TestEmbeddedBinarySchema()3299 void TestEmbeddedBinarySchema() {
3300 // load JSON from disk
3301 std::string jsonfile;
3302 TEST_EQ(flatbuffers::LoadFile(
3303 (test_data_path + "monsterdata_test.golden").c_str(), false,
3304 &jsonfile),
3305 true);
3306
3307 // parse schema first, so we can use it to parse the data after
3308 flatbuffers::Parser parserOrg, parserGen;
3309 flatbuffers::Verifier verifier(MyGame::Example::MonsterBinarySchema::data(),
3310 MyGame::Example::MonsterBinarySchema::size());
3311 TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
3312 TEST_EQ(parserOrg.Deserialize(MyGame::Example::MonsterBinarySchema::data(),
3313 MyGame::Example::MonsterBinarySchema::size()),
3314 true);
3315 TEST_EQ(parserGen.Deserialize(MyGame::Example::MonsterBinarySchema::data(),
3316 MyGame::Example::MonsterBinarySchema::size()),
3317 true);
3318 TEST_EQ(parserOrg.Parse(jsonfile.c_str()), true);
3319
3320 // First, verify it, just in case:
3321 flatbuffers::Verifier verifierOrg(parserOrg.builder_.GetBufferPointer(),
3322 parserOrg.builder_.GetSize());
3323 TEST_EQ(VerifyMonsterBuffer(verifierOrg), true);
3324
3325 // Export to JSON
3326 std::string jsonGen;
3327 TEST_EQ(
3328 GenerateText(parserOrg, parserOrg.builder_.GetBufferPointer(), &jsonGen),
3329 true);
3330
3331 // Import from JSON
3332 TEST_EQ(parserGen.Parse(jsonGen.c_str()), true);
3333
3334 // Verify buffer from generated JSON
3335 flatbuffers::Verifier verifierGen(parserGen.builder_.GetBufferPointer(),
3336 parserGen.builder_.GetSize());
3337 TEST_EQ(VerifyMonsterBuffer(verifierGen), true);
3338
3339 // Compare generated buffer to original
3340 TEST_EQ(parserOrg.builder_.GetSize(), parserGen.builder_.GetSize());
3341 TEST_EQ(std::memcmp(parserOrg.builder_.GetBufferPointer(),
3342 parserGen.builder_.GetBufferPointer(),
3343 parserOrg.builder_.GetSize()),
3344 0);
3345 }
3346
FlatBufferTests()3347 int FlatBufferTests() {
3348 // clang-format off
3349
3350 // Run our various test suites:
3351
3352 std::string rawbuf;
3353 auto flatbuf1 = CreateFlatBufferTest(rawbuf);
3354 #if !defined(FLATBUFFERS_CPP98_STL)
3355 auto flatbuf = std::move(flatbuf1); // Test move assignment.
3356 #else
3357 auto &flatbuf = flatbuf1;
3358 #endif // !defined(FLATBUFFERS_CPP98_STL)
3359
3360 TriviallyCopyableTest();
3361
3362 AccessFlatBufferTest(reinterpret_cast<const uint8_t *>(rawbuf.c_str()),
3363 rawbuf.length());
3364 AccessFlatBufferTest(flatbuf.data(), flatbuf.size());
3365
3366 MutateFlatBuffersTest(flatbuf.data(), flatbuf.size());
3367
3368 ObjectFlatBuffersTest(flatbuf.data());
3369
3370 MiniReflectFlatBuffersTest(flatbuf.data());
3371
3372 SizePrefixedTest();
3373
3374 #ifndef FLATBUFFERS_NO_FILE_TESTS
3375 #ifdef FLATBUFFERS_TEST_PATH_PREFIX
3376 test_data_path = FLATBUFFERS_STRING(FLATBUFFERS_TEST_PATH_PREFIX) +
3377 test_data_path;
3378 #endif
3379 ParseAndGenerateTextTest(false);
3380 ParseAndGenerateTextTest(true);
3381 FixedLengthArrayJsonTest(false);
3382 FixedLengthArrayJsonTest(true);
3383 ReflectionTest(flatbuf.data(), flatbuf.size());
3384 ParseProtoTest();
3385 ParseProtoTestWithSuffix();
3386 ParseProtoTestWithIncludes();
3387 EvolutionTest();
3388 UnionVectorTest();
3389 LoadVerifyBinaryTest();
3390 GenerateTableTextTest();
3391 TestEmbeddedBinarySchema();
3392 #endif
3393 // clang-format on
3394
3395 FuzzTest1();
3396 FuzzTest2();
3397
3398 ErrorTest();
3399 ValueTest();
3400 EnumValueTest();
3401 EnumStringsTest();
3402 EnumNamesTest();
3403 EnumOutOfRangeTest();
3404 IntegerOutOfRangeTest();
3405 IntegerBoundaryTest();
3406 UnicodeTest();
3407 UnicodeTestAllowNonUTF8();
3408 UnicodeTestGenerateTextFailsOnNonUTF8();
3409 UnicodeSurrogatesTest();
3410 UnicodeInvalidSurrogatesTest();
3411 InvalidUTF8Test();
3412 UnknownFieldsTest();
3413 ParseUnionTest();
3414 InvalidNestedFlatbufferTest();
3415 ConformTest();
3416 ParseProtoBufAsciiTest();
3417 TypeAliasesTest();
3418 EndianSwapTest();
3419 CreateSharedStringTest();
3420 JsonDefaultTest();
3421 JsonEnumsTest();
3422 FlexBuffersTest();
3423 FlexBuffersDeprecatedTest();
3424 UninitializedVectorTest();
3425 EqualOperatorTest();
3426 NumericUtilsTest();
3427 IsAsciiUtilsTest();
3428 ValidFloatTest();
3429 InvalidFloatTest();
3430 TestMonsterExtraFloats();
3431 FixedLengthArrayTest();
3432 NativeTypeTest();
3433 return 0;
3434 }
3435
main(int,const char * [])3436 int main(int /*argc*/, const char * /*argv*/[]) {
3437 InitTestEngine();
3438
3439 std::string req_locale;
3440 if (flatbuffers::ReadEnvironmentVariable("FLATBUFFERS_TEST_LOCALE",
3441 &req_locale)) {
3442 TEST_OUTPUT_LINE("The environment variable FLATBUFFERS_TEST_LOCALE=%s",
3443 req_locale.c_str());
3444 req_locale = flatbuffers::RemoveStringQuotes(req_locale);
3445 std::string the_locale;
3446 TEST_ASSERT_FUNC(
3447 flatbuffers::SetGlobalTestLocale(req_locale.c_str(), &the_locale));
3448 TEST_OUTPUT_LINE("The global C-locale changed: %s", the_locale.c_str());
3449 }
3450
3451 FlatBufferTests();
3452 FlatBufferBuilderTest();
3453
3454 if (!testing_fails) {
3455 TEST_OUTPUT_LINE("ALL TESTS PASSED");
3456 } else {
3457 TEST_OUTPUT_LINE("%d FAILED TESTS", testing_fails);
3458 }
3459 return CloseTestEngine();
3460 }
3461