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