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