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