• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "flatbuffers/flatbuffers.h"
18 #include "flatbuffers/idl.h"
19 #include "flatbuffers/util.h"
20 
21 #include "monster_test_generated.h"
22 #include "namespace_test/namespace_test1_generated.h"
23 #include "namespace_test/namespace_test2_generated.h"
24 #include "union_vector/union_vector_generated.h"
25 
26 #ifndef FLATBUFFERS_CPP98_STL
27   #include <random>
28 #endif
29 
30 #include "flatbuffers/flexbuffers.h"
31 
32 using namespace MyGame::Example;
33 
34 #ifdef __ANDROID__
35   #include <android/log.h>
36   #define TEST_OUTPUT_LINE(...) \
37     __android_log_print(ANDROID_LOG_INFO, "FlatBuffers", __VA_ARGS__)
38   #define FLATBUFFERS_NO_FILE_TESTS
39 #else
40   #define TEST_OUTPUT_LINE(...) \
41     { printf(__VA_ARGS__); printf("\n"); }
42 #endif
43 
44 int testing_fails = 0;
45 
TestFail(const char * expval,const char * val,const char * exp,const char * file,int line)46 void TestFail(const char *expval, const char *val, const char *exp,
47               const char *file, int line) {
48   TEST_OUTPUT_LINE("TEST FAILED: %s:%d, %s (%s) != %s", file, line,
49                    exp, expval, val);
50   assert(0);
51   testing_fails++;
52 }
53 
TestEqStr(const char * expval,const char * val,const char * exp,const char * file,int line)54 void TestEqStr(const char *expval, const char *val, const char *exp,
55                const char *file, int line) {
56   if (strcmp(expval, val) != 0) {
57     TestFail(expval, val, exp, file, line);
58   }
59 }
60 
61 template<typename T, typename U>
TestEq(T expval,U val,const char * exp,const char * file,int line)62 void TestEq(T expval, U val, const char *exp, const char *file, int line) {
63   if (U(expval) != val) {
64     TestFail(flatbuffers::NumToString(expval).c_str(),
65              flatbuffers::NumToString(val).c_str(),
66              exp, file, line);
67   }
68 }
69 
70 #define TEST_EQ(exp, val) TestEq(exp,         val,   #exp, __FILE__, __LINE__)
71 #define TEST_NOTNULL(exp) TestEq(exp == NULL, false, #exp, __FILE__, __LINE__)
72 #define TEST_EQ_STR(exp, val) TestEqStr(exp,  val,   #exp, __FILE__, __LINE__)
73 
74 // Include simple random number generator to ensure results will be the
75 // same cross platform.
76 // http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator
77 uint32_t lcg_seed = 48271;
lcg_rand()78 uint32_t lcg_rand() {
79     return lcg_seed = ((uint64_t)lcg_seed * 279470273UL) % 4294967291UL;
80 }
lcg_reset()81 void lcg_reset() { lcg_seed = 48271; }
82 
83 // example of how to build up a serialized buffer algorithmically:
CreateFlatBufferTest(std::string & buffer)84 flatbuffers::unique_ptr_t CreateFlatBufferTest(std::string &buffer) {
85   flatbuffers::FlatBufferBuilder builder;
86 
87   auto vec = Vec3(1, 2, 3, 0, Color_Red, Test(10, 20));
88 
89   auto name = builder.CreateString("MyMonster");
90 
91   unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
92   auto inventory = builder.CreateVector(inv_data, 10);
93 
94   // Alternatively, create the vector first, and fill in data later:
95   // unsigned char *inv_buf = nullptr;
96   // auto inventory = builder.CreateUninitializedVector<unsigned char>(
97   //                                                              10, &inv_buf);
98   // memcpy(inv_buf, inv_data, 10);
99 
100   Test tests[] = { Test(10, 20), Test(30, 40) };
101   auto testv = builder.CreateVectorOfStructs(tests, 2);
102 
103   // create monster with very few fields set:
104   // (same functionality as CreateMonster below, but sets fields manually)
105   flatbuffers::Offset<Monster> mlocs[3];
106   auto fred = builder.CreateString("Fred");
107   auto barney = builder.CreateString("Barney");
108   auto wilma = builder.CreateString("Wilma");
109   MonsterBuilder mb1(builder);
110   mb1.add_name(fred);
111   mlocs[0] = mb1.Finish();
112   MonsterBuilder mb2(builder);
113   mb2.add_name(barney);
114   mb2.add_hp(1000);
115   mlocs[1] = mb2.Finish();
116   MonsterBuilder mb3(builder);
117   mb3.add_name(wilma);
118   mlocs[2] = mb3.Finish();
119 
120   // Create an array of strings. Also test string pooling, and lambdas.
121   const char *names[] = { "bob", "fred", "bob", "fred" };
122   auto vecofstrings =
123       builder.CreateVector<flatbuffers::Offset<flatbuffers::String>>(4,
124         [&](size_t i) {
125     return builder.CreateSharedString(names[i]);
126   });
127 
128   // Creating vectors of strings in one convenient call.
129   std::vector<std::string> names2;
130   names2.push_back("jane");
131   names2.push_back("mary");
132   auto vecofstrings2 = builder.CreateVectorOfStrings(names2);
133 
134   // Create an array of sorted tables, can be used with binary search when read:
135   auto vecoftables = builder.CreateVectorOfSortedTables(mlocs, 3);
136 
137   // shortcut for creating monster with all fields set:
138   auto mloc = CreateMonster(builder, &vec, 150, 80, name, inventory, Color_Blue,
139                             Any_Monster, mlocs[1].Union(), // Store a union.
140                             testv, vecofstrings, vecoftables, 0, 0, 0, false,
141                             0, 0, 0, 0, 0, 0, 0, 0, 0, 3.14159f, 3.0f, 0.0f,
142                             vecofstrings2);
143 
144   FinishMonsterBuffer(builder, mloc);
145 
146   #ifdef FLATBUFFERS_TEST_VERBOSE
147   // print byte data for debugging:
148   auto p = builder.GetBufferPointer();
149   for (flatbuffers::uoffset_t i = 0; i < builder.GetSize(); i++)
150     printf("%d ", p[i]);
151   #endif
152 
153   // return the buffer for the caller to use.
154   auto bufferpointer =
155     reinterpret_cast<const char *>(builder.GetBufferPointer());
156   buffer.assign(bufferpointer, bufferpointer + builder.GetSize());
157 
158   return builder.ReleaseBufferPointer();
159 }
160 
161 //  example of accessing a buffer loaded in memory:
AccessFlatBufferTest(const uint8_t * flatbuf,size_t length,bool pooled=true)162 void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length,
163                           bool pooled = true) {
164 
165   // First, verify the buffers integrity (optional)
166   flatbuffers::Verifier verifier(flatbuf, length);
167   TEST_EQ(VerifyMonsterBuffer(verifier), true);
168 
169   std::vector<uint8_t> test_buff;
170   test_buff.resize(length * 2);
171   std::memcpy(&test_buff[0], flatbuf , length);
172   std::memcpy(&test_buff[length], flatbuf , length);
173 
174   flatbuffers::Verifier verifierl(&test_buff[0], length - 1);
175   TEST_EQ(VerifyMonsterBuffer(verifierl), false);
176   TEST_EQ(verifierl.GetComputedSize(), 0);
177 
178   flatbuffers::Verifier verifier1(&test_buff[0], length);
179   TEST_EQ(VerifyMonsterBuffer(verifier1), true);
180   TEST_EQ(verifier1.GetComputedSize(), length);
181 
182   flatbuffers::Verifier verifier2(&test_buff[length], length);
183   TEST_EQ(VerifyMonsterBuffer(verifier2), true);
184   TEST_EQ(verifier2.GetComputedSize(), length);
185 
186   TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0);
187   TEST_EQ(MonsterBufferHasIdentifier(flatbuf), true);
188   TEST_EQ(strcmp(MonsterExtension(), "mon"), 0);
189 
190   // Access the buffer from the root.
191   auto monster = GetMonster(flatbuf);
192 
193   TEST_EQ(monster->hp(), 80);
194   TEST_EQ(monster->mana(), 150);  // default
195   TEST_EQ_STR(monster->name()->c_str(), "MyMonster");
196   // Can't access the following field, it is deprecated in the schema,
197   // which means accessors are not generated:
198   // monster.friendly()
199 
200   auto pos = monster->pos();
201   TEST_NOTNULL(pos);
202   TEST_EQ(pos->z(), 3);
203   TEST_EQ(pos->test3().a(), 10);
204   TEST_EQ(pos->test3().b(), 20);
205 
206   auto inventory = monster->inventory();
207   TEST_EQ(VectorLength(inventory), 10UL);  // Works even if inventory is null.
208   TEST_NOTNULL(inventory);
209   unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
210   for (auto it = inventory->begin(); it != inventory->end(); ++it)
211     TEST_EQ(*it, inv_data[it - inventory->begin()]);
212 
213   TEST_EQ(monster->color(), Color_Blue);
214 
215   // Example of accessing a union:
216   TEST_EQ(monster->test_type(), Any_Monster);  // First make sure which it is.
217   auto monster2 = reinterpret_cast<const Monster *>(monster->test());
218   TEST_NOTNULL(monster2);
219   TEST_EQ_STR(monster2->name()->c_str(), "Fred");
220 
221   // Example of accessing a vector of strings:
222   auto vecofstrings = monster->testarrayofstring();
223   TEST_EQ(vecofstrings->Length(), 4U);
224   TEST_EQ_STR(vecofstrings->Get(0)->c_str(), "bob");
225   TEST_EQ_STR(vecofstrings->Get(1)->c_str(), "fred");
226   if (pooled) {
227     // These should have pointer equality because of string pooling.
228     TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str());
229     TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str());
230   }
231 
232   auto vecofstrings2 = monster->testarrayofstring2();
233   if (vecofstrings2) {
234     TEST_EQ(vecofstrings2->Length(), 2U);
235     TEST_EQ_STR(vecofstrings2->Get(0)->c_str(), "jane");
236     TEST_EQ_STR(vecofstrings2->Get(1)->c_str(), "mary");
237   }
238 
239   // Example of accessing a vector of tables:
240   auto vecoftables = monster->testarrayoftables();
241   TEST_EQ(vecoftables->Length(), 3U);
242   for (auto it = vecoftables->begin(); it != vecoftables->end(); ++it)
243     TEST_EQ(strlen(it->name()->c_str()) >= 4, true);
244   TEST_EQ_STR(vecoftables->Get(0)->name()->c_str(), "Barney");
245   TEST_EQ(vecoftables->Get(0)->hp(), 1000);
246   TEST_EQ_STR(vecoftables->Get(1)->name()->c_str(), "Fred");
247   TEST_EQ_STR(vecoftables->Get(2)->name()->c_str(), "Wilma");
248   TEST_NOTNULL(vecoftables->LookupByKey("Barney"));
249   TEST_NOTNULL(vecoftables->LookupByKey("Fred"));
250   TEST_NOTNULL(vecoftables->LookupByKey("Wilma"));
251 
252   // Since Flatbuffers uses explicit mechanisms to override the default
253   // compiler alignment, double check that the compiler indeed obeys them:
254   // (Test consists of a short and byte):
255   TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
256   TEST_EQ(sizeof(Test), 4UL);
257 
258   auto tests = monster->test4();
259   TEST_NOTNULL(tests);
260   auto test_0 = tests->Get(0);
261   auto test_1 = tests->Get(1);
262   TEST_EQ(test_0->a(), 10);
263   TEST_EQ(test_0->b(), 20);
264   TEST_EQ(test_1->a(), 30);
265   TEST_EQ(test_1->b(), 40);
266   for (auto it = tests->begin(); it != tests->end(); ++it) {
267     TEST_EQ(it->a() == 10 || it->a() == 30, true);  // Just testing iterators.
268   }
269 
270   // Checking for presence of fields:
271   TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_HP), true);
272   TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_MANA), false);
273 
274   // Obtaining a buffer from a root:
275   TEST_EQ(GetBufferStartFromRootPointer(monster), flatbuf);
276 }
277 
278 // Change a FlatBuffer in-place, after it has been constructed.
MutateFlatBuffersTest(uint8_t * flatbuf,std::size_t length)279 void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
280   // Get non-const pointer to root.
281   auto monster = GetMutableMonster(flatbuf);
282 
283   // Each of these tests mutates, then tests, then set back to the original,
284   // so we can test that the buffer in the end still passes our original test.
285   auto hp_ok = monster->mutate_hp(10);
286   TEST_EQ(hp_ok, true);  // Field was present.
287   TEST_EQ(monster->hp(), 10);
288   monster->mutate_hp(80);
289 
290   auto mana_ok = monster->mutate_mana(10);
291   TEST_EQ(mana_ok, false);  // Field was NOT present, because default value.
292 
293   // Mutate structs.
294   auto pos = monster->mutable_pos();
295   auto test3 = pos->mutable_test3();  // Struct inside a struct.
296   test3.mutate_a(50);                 // Struct fields never fail.
297   TEST_EQ(test3.a(), 50);
298   test3.mutate_a(10);
299 
300   // Mutate vectors.
301   auto inventory = monster->mutable_inventory();
302   inventory->Mutate(9, 100);
303   TEST_EQ(inventory->Get(9), 100);
304   inventory->Mutate(9, 9);
305 
306   auto tables = monster->mutable_testarrayoftables();
307   auto first = tables->GetMutableObject(0);
308   TEST_EQ(first->hp(), 1000);
309   first->mutate_hp(0);
310   TEST_EQ(first->hp(), 0);
311   first->mutate_hp(1000);
312 
313   // Run the verifier and the regular test to make sure we didn't trample on
314   // anything.
315   AccessFlatBufferTest(flatbuf, length);
316 }
317 
318 // Unpack a FlatBuffer into objects.
ObjectFlatBuffersTest(uint8_t * flatbuf)319 void ObjectFlatBuffersTest(uint8_t *flatbuf) {
320   // Optional: we can specify resolver and rehasher functions to turn hashed
321   // strings into object pointers and back, to implement remote references
322   // and such.
323   auto resolver = flatbuffers::resolver_function_t(
324                     [](void **pointer_adr, flatbuffers::hash_value_t hash) {
325     (void)pointer_adr;
326     (void)hash;
327     // Don't actually do anything, leave variable null.
328   });
329   auto rehasher = flatbuffers::rehasher_function_t(
330                     [](void *pointer) -> flatbuffers::hash_value_t {
331     (void)pointer;
332     return 0;
333   });
334 
335   // Turn a buffer into C++ objects.
336   auto monster1 = UnPackMonster(flatbuf, &resolver);
337 
338   // Re-serialize the data.
339   flatbuffers::FlatBufferBuilder fbb1;
340   fbb1.Finish(CreateMonster(fbb1, monster1.get(), &rehasher),
341               MonsterIdentifier());
342 
343   // Unpack again, and re-serialize again.
344   auto monster2 = UnPackMonster(fbb1.GetBufferPointer(), &resolver);
345   flatbuffers::FlatBufferBuilder fbb2;
346   fbb2.Finish(CreateMonster(fbb2, monster2.get(), &rehasher),
347               MonsterIdentifier());
348 
349   // Now we've gone full round-trip, the two buffers should match.
350   auto len1 = fbb1.GetSize();
351   auto len2 = fbb2.GetSize();
352   TEST_EQ(len1, len2);
353   TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(),
354                  len1), 0);
355 
356   // Test it with the original buffer test to make sure all data survived.
357   AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false);
358 
359   // Test accessing fields, similar to AccessFlatBufferTest above.
360   TEST_EQ(monster2->hp, 80);
361   TEST_EQ(monster2->mana, 150);  // default
362   TEST_EQ_STR(monster2->name.c_str(), "MyMonster");
363 
364   auto &pos = monster2->pos;
365   TEST_NOTNULL(pos);
366   TEST_EQ(pos->z(), 3);
367   TEST_EQ(pos->test3().a(), 10);
368   TEST_EQ(pos->test3().b(), 20);
369 
370   auto &inventory = monster2->inventory;
371   TEST_EQ(inventory.size(), 10UL);
372   unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
373   for (auto it = inventory.begin(); it != inventory.end(); ++it)
374     TEST_EQ(*it, inv_data[it - inventory.begin()]);
375 
376   TEST_EQ(monster2->color, Color_Blue);
377 
378   auto monster3 = monster2->test.AsMonster();
379   TEST_NOTNULL(monster3);
380   TEST_EQ_STR(monster3->name.c_str(), "Fred");
381 
382   auto &vecofstrings = monster2->testarrayofstring;
383   TEST_EQ(vecofstrings.size(), 4U);
384   TEST_EQ_STR(vecofstrings[0].c_str(), "bob");
385   TEST_EQ_STR(vecofstrings[1].c_str(), "fred");
386 
387   auto &vecofstrings2 = monster2->testarrayofstring2;
388   TEST_EQ(vecofstrings2.size(), 2U);
389   TEST_EQ_STR(vecofstrings2[0].c_str(), "jane");
390   TEST_EQ_STR(vecofstrings2[1].c_str(), "mary");
391 
392   auto &vecoftables = monster2->testarrayoftables;
393   TEST_EQ(vecoftables.size(), 3U);
394   TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney");
395   TEST_EQ(vecoftables[0]->hp, 1000);
396   TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred");
397   TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma");
398 
399   auto &tests = monster2->test4;
400   TEST_EQ(tests[0].a(), 10);
401   TEST_EQ(tests[0].b(), 20);
402   TEST_EQ(tests[1].a(), 30);
403   TEST_EQ(tests[1].b(), 40);
404 }
405 
406 // Prefix a FlatBuffer with a size field.
SizePrefixedTest()407 void SizePrefixedTest() {
408   // Create size prefixed buffer.
409   flatbuffers::FlatBufferBuilder fbb;
410   fbb.FinishSizePrefixed(CreateMonster(fbb, 0, 200, 300,
411                                        fbb.CreateString("bob")));
412 
413   // Verify it.
414   flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
415   TEST_EQ(verifier.VerifySizePrefixedBuffer<Monster>(nullptr), true);
416 
417   // Access it.
418   auto m = flatbuffers::GetSizePrefixedRoot<MyGame::Example::Monster>(
419                                                         fbb.GetBufferPointer());
420   TEST_EQ(m->mana(), 200);
421   TEST_EQ(m->hp(), 300);
422   TEST_EQ_STR(m->name()->c_str(), "bob");
423 }
424 
425 // example of parsing text straight into a buffer, and generating
426 // text back from it:
ParseAndGenerateTextTest()427 void ParseAndGenerateTextTest() {
428   // load FlatBuffer schema (.fbs) and JSON from disk
429   std::string schemafile;
430   std::string jsonfile;
431   TEST_EQ(flatbuffers::LoadFile(
432     "tests/monster_test.fbs", false, &schemafile), true);
433   TEST_EQ(flatbuffers::LoadFile(
434     "tests/monsterdata_test.golden", false, &jsonfile), true);
435 
436   // parse schema first, so we can use it to parse the data after
437   flatbuffers::Parser parser;
438   const char *include_directories[] = { "tests", nullptr };
439   TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
440   TEST_EQ(parser.Parse(jsonfile.c_str(), include_directories), true);
441 
442   // here, parser.builder_ contains a binary buffer that is the parsed data.
443 
444   // First, verify it, just in case:
445   flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
446                                  parser.builder_.GetSize());
447   TEST_EQ(VerifyMonsterBuffer(verifier), true);
448 
449   // to ensure it is correct, we now generate text back from the binary,
450   // and compare the two:
451   std::string jsongen;
452   auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
453   TEST_EQ(result, true);
454 
455   if (jsongen != jsonfile) {
456     printf("%s----------------\n%s", jsongen.c_str(), jsonfile.c_str());
457     TEST_NOTNULL(NULL);
458   }
459 }
460 
ReflectionTest(uint8_t * flatbuf,size_t length)461 void ReflectionTest(uint8_t *flatbuf, size_t length) {
462   // Load a binary schema.
463   std::string bfbsfile;
464   TEST_EQ(flatbuffers::LoadFile(
465     "tests/monster_test.bfbs", true, &bfbsfile), true);
466 
467   // Verify it, just in case:
468   flatbuffers::Verifier verifier(
469     reinterpret_cast<const uint8_t *>(bfbsfile.c_str()), bfbsfile.length());
470   TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
471 
472   // Make sure the schema is what we expect it to be.
473   auto &schema = *reflection::GetSchema(bfbsfile.c_str());
474   auto root_table = schema.root_table();
475   TEST_EQ_STR(root_table->name()->c_str(), "MyGame.Example.Monster");
476   auto fields = root_table->fields();
477   auto hp_field_ptr = fields->LookupByKey("hp");
478   TEST_NOTNULL(hp_field_ptr);
479   auto &hp_field = *hp_field_ptr;
480   TEST_EQ_STR(hp_field.name()->c_str(), "hp");
481   TEST_EQ(hp_field.id(), 2);
482   TEST_EQ(hp_field.type()->base_type(), reflection::Short);
483   auto friendly_field_ptr = fields->LookupByKey("friendly");
484   TEST_NOTNULL(friendly_field_ptr);
485   TEST_NOTNULL(friendly_field_ptr->attributes());
486   TEST_NOTNULL(friendly_field_ptr->attributes()->LookupByKey("priority"));
487 
488   // Make sure the table index is what we expect it to be.
489   auto pos_field_ptr = fields->LookupByKey("pos");
490   TEST_NOTNULL(pos_field_ptr);
491   TEST_EQ(pos_field_ptr->type()->base_type(), reflection::Obj);
492   auto pos_table_ptr = schema.objects()->Get(pos_field_ptr->type()->index());
493   TEST_NOTNULL(pos_table_ptr);
494   TEST_EQ_STR(pos_table_ptr->name()->c_str(), "MyGame.Example.Vec3");
495 
496   // Now use it to dynamically access a buffer.
497   auto &root = *flatbuffers::GetAnyRoot(flatbuf);
498 
499   // Verify the buffer first using reflection based verification
500   TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), flatbuf, length),
501           true);
502 
503   auto hp = flatbuffers::GetFieldI<uint16_t>(root, hp_field);
504   TEST_EQ(hp, 80);
505 
506   // Rather than needing to know the type, we can also get the value of
507   // any field as an int64_t/double/string, regardless of what it actually is.
508   auto hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
509   TEST_EQ(hp_int64, 80);
510   auto hp_double = flatbuffers::GetAnyFieldF(root, hp_field);
511   TEST_EQ(hp_double, 80.0);
512   auto hp_string = flatbuffers::GetAnyFieldS(root, hp_field, &schema);
513   TEST_EQ_STR(hp_string.c_str(), "80");
514 
515   // Get struct field through reflection
516   auto pos_struct = flatbuffers::GetFieldStruct(root, *pos_field_ptr);
517   TEST_NOTNULL(pos_struct);
518   TEST_EQ(flatbuffers::GetAnyFieldF(
519     *pos_struct, *pos_table_ptr->fields()->LookupByKey("z")), 3.0f);
520 
521   auto test3_field = pos_table_ptr->fields()->LookupByKey("test3");
522   auto test3_struct = flatbuffers::GetFieldStruct(*pos_struct, *test3_field);
523   TEST_NOTNULL(test3_struct);
524   auto test3_object = schema.objects()->Get(test3_field->type()->index());
525 
526   TEST_EQ(flatbuffers::GetAnyFieldF(
527     *test3_struct, *test3_object->fields()->LookupByKey("a")), 10);
528 
529   // We can also modify it.
530   flatbuffers::SetField<uint16_t>(&root, hp_field, 200);
531   hp = flatbuffers::GetFieldI<uint16_t>(root, hp_field);
532   TEST_EQ(hp, 200);
533 
534   // We can also set fields generically:
535   flatbuffers::SetAnyFieldI(&root, hp_field, 300);
536   hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
537   TEST_EQ(hp_int64, 300);
538   flatbuffers::SetAnyFieldF(&root, hp_field, 300.5);
539   hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
540   TEST_EQ(hp_int64, 300);
541   flatbuffers::SetAnyFieldS(&root, hp_field, "300");
542   hp_int64 = flatbuffers::GetAnyFieldI(root, hp_field);
543   TEST_EQ(hp_int64, 300);
544 
545   // Test buffer is valid after the modifications
546   TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), flatbuf, length),
547           true);
548 
549   // Reset it, for further tests.
550   flatbuffers::SetField<uint16_t>(&root, hp_field, 80);
551 
552   // More advanced functionality: changing the size of items in-line!
553   // First we put the FlatBuffer inside an std::vector.
554   std::vector<uint8_t> resizingbuf(flatbuf, flatbuf + length);
555   // Find the field we want to modify.
556   auto &name_field = *fields->LookupByKey("name");
557   // Get the root.
558   // This time we wrap the result from GetAnyRoot in a smartpointer that
559   // will keep rroot valid as resizingbuf resizes.
560   auto rroot = flatbuffers::piv(flatbuffers::GetAnyRoot(resizingbuf.data()),
561                                 resizingbuf);
562   SetString(schema, "totally new string", GetFieldS(**rroot, name_field),
563             &resizingbuf);
564   // Here resizingbuf has changed, but rroot is still valid.
565   TEST_EQ_STR(GetFieldS(**rroot, name_field)->c_str(), "totally new string");
566   // Now lets extend a vector by 100 elements (10 -> 110).
567   auto &inventory_field = *fields->LookupByKey("inventory");
568   auto rinventory = flatbuffers::piv(
569                      flatbuffers::GetFieldV<uint8_t>(**rroot, inventory_field),
570                      resizingbuf);
571   flatbuffers::ResizeVector<uint8_t>(schema, 110, 50, *rinventory,
572                                      &resizingbuf);
573   // rinventory still valid, so lets read from it.
574   TEST_EQ(rinventory->Get(10), 50);
575 
576   // For reflection uses not covered already, there is a more powerful way:
577   // we can simply generate whatever object we want to add/modify in a
578   // FlatBuffer of its own, then add that to an existing FlatBuffer:
579   // As an example, let's add a string to an array of strings.
580   // First, find our field:
581   auto &testarrayofstring_field = *fields->LookupByKey("testarrayofstring");
582   // Find the vector value:
583   auto rtestarrayofstring = flatbuffers::piv(
584          flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>(
585            **rroot, testarrayofstring_field),
586          resizingbuf);
587   // It's a vector of 2 strings, to which we add one more, initialized to
588   // offset 0.
589   flatbuffers::ResizeVector<flatbuffers::Offset<flatbuffers::String>>(
590         schema, 3, 0, *rtestarrayofstring, &resizingbuf);
591   // Here we just create a buffer that contans a single string, but this
592   // could also be any complex set of tables and other values.
593   flatbuffers::FlatBufferBuilder stringfbb;
594   stringfbb.Finish(stringfbb.CreateString("hank"));
595   // Add the contents of it to our existing FlatBuffer.
596   // We do this last, so the pointer doesn't get invalidated (since it is
597   // at the end of the buffer):
598   auto string_ptr = flatbuffers::AddFlatBuffer(resizingbuf,
599                                                stringfbb.GetBufferPointer(),
600                                                stringfbb.GetSize());
601   // Finally, set the new value in the vector.
602   rtestarrayofstring->MutateOffset(2, string_ptr);
603   TEST_EQ_STR(rtestarrayofstring->Get(0)->c_str(), "bob");
604   TEST_EQ_STR(rtestarrayofstring->Get(2)->c_str(), "hank");
605   // Test integrity of all resize operations above.
606   flatbuffers::Verifier resize_verifier(
607         reinterpret_cast<const uint8_t *>(resizingbuf.data()),
608         resizingbuf.size());
609   TEST_EQ(VerifyMonsterBuffer(resize_verifier), true);
610 
611   // Test buffer is valid using reflection as well
612   TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), resizingbuf.data(),
613                               resizingbuf.size()), true);
614 
615   // As an additional test, also set it on the name field.
616   // Note: unlike the name change above, this just overwrites the offset,
617   // rather than changing the string in-place.
618   SetFieldT(*rroot, name_field, string_ptr);
619   TEST_EQ_STR(GetFieldS(**rroot, name_field)->c_str(), "hank");
620 
621   // Using reflection, rather than mutating binary FlatBuffers, we can also copy
622   // tables and other things out of other FlatBuffers into a FlatBufferBuilder,
623   // either part or whole.
624   flatbuffers::FlatBufferBuilder fbb;
625   auto root_offset = flatbuffers::CopyTable(fbb, schema, *root_table,
626                                             *flatbuffers::GetAnyRoot(flatbuf),
627                                             true);
628   fbb.Finish(root_offset, MonsterIdentifier());
629   // Test that it was copied correctly:
630   AccessFlatBufferTest(fbb.GetBufferPointer(), fbb.GetSize());
631 
632   // Test buffer is valid using reflection as well
633   TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(),
634                               fbb.GetBufferPointer(), fbb.GetSize()), true);
635 }
636 
637 // Parse a .proto schema, output as .fbs
ParseProtoTest()638 void ParseProtoTest() {
639   // load the .proto and the golden file from disk
640   std::string protofile;
641   std::string goldenfile;
642   TEST_EQ(flatbuffers::LoadFile(
643     "tests/prototest/test.proto", false, &protofile), true);
644   TEST_EQ(flatbuffers::LoadFile(
645     "tests/prototest/test.golden", false, &goldenfile), true);
646 
647   flatbuffers::IDLOptions opts;
648   opts.include_dependence_headers = false;
649   opts.proto_mode = true;
650 
651   // Parse proto.
652   flatbuffers::Parser parser(opts);
653   const char *include_directories[] = { "tests/prototest", nullptr };
654   TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true);
655 
656   // Generate fbs.
657   auto fbs = flatbuffers::GenerateFBS(parser, "test");
658 
659   // Ensure generated file is parsable.
660   flatbuffers::Parser parser2;
661   TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
662 
663   if (fbs != goldenfile) {
664     printf("%s----------------\n%s", fbs.c_str(), goldenfile.c_str());
665     TEST_NOTNULL(NULL);
666   }
667 }
668 
CompareTableFieldValue(flatbuffers::Table * table,flatbuffers::voffset_t voffset,T val)669 template<typename T> void CompareTableFieldValue(flatbuffers::Table *table,
670                                                  flatbuffers::voffset_t voffset,
671                                                  T val) {
672   T read = table->GetField(voffset, static_cast<T>(0));
673   TEST_EQ(read, val);
674 }
675 
676 // Low level stress/fuzz test: serialize/deserialize a variety of
677 // different kinds of data in different combinations
FuzzTest1()678 void FuzzTest1() {
679 
680   // Values we're testing against: chosen to ensure no bits get chopped
681   // off anywhere, and also be different from eachother.
682   const uint8_t  bool_val   = true;
683   const int8_t   char_val   = -127;  // 0x81
684   const uint8_t  uchar_val  = 0xFF;
685   const int16_t  short_val  = -32222; // 0x8222;
686   const uint16_t ushort_val = 0xFEEE;
687   const int32_t  int_val    = 0x83333333;
688   const uint32_t uint_val   = 0xFDDDDDDD;
689   const int64_t  long_val   = 0x8444444444444444LL;
690   const uint64_t ulong_val  = 0xFCCCCCCCCCCCCCCCULL;
691   const float    float_val  = 3.14159f;
692   const double   double_val = 3.14159265359;
693 
694   const int test_values_max = 11;
695   const flatbuffers::voffset_t fields_per_object = 4;
696   const int num_fuzz_objects = 10000;  // The higher, the more thorough :)
697 
698   flatbuffers::FlatBufferBuilder builder;
699 
700   lcg_reset();  // Keep it deterministic.
701 
702   flatbuffers::uoffset_t objects[num_fuzz_objects];
703 
704   // Generate num_fuzz_objects random objects each consisting of
705   // fields_per_object fields, each of a random type.
706   for (int i = 0; i < num_fuzz_objects; i++) {
707     auto start = builder.StartTable();
708     for (flatbuffers::voffset_t f = 0; f < fields_per_object; f++) {
709       int choice = lcg_rand() % test_values_max;
710       auto off = flatbuffers::FieldIndexToOffset(f);
711       switch (choice) {
712         case 0:  builder.AddElement<uint8_t >(off, bool_val,   0); break;
713         case 1:  builder.AddElement<int8_t  >(off, char_val,   0); break;
714         case 2:  builder.AddElement<uint8_t >(off, uchar_val,  0); break;
715         case 3:  builder.AddElement<int16_t >(off, short_val,  0); break;
716         case 4:  builder.AddElement<uint16_t>(off, ushort_val, 0); break;
717         case 5:  builder.AddElement<int32_t >(off, int_val,    0); break;
718         case 6:  builder.AddElement<uint32_t>(off, uint_val,   0); break;
719         case 7:  builder.AddElement<int64_t >(off, long_val,   0); break;
720         case 8:  builder.AddElement<uint64_t>(off, ulong_val,  0); break;
721         case 9:  builder.AddElement<float   >(off, float_val,  0); break;
722         case 10: builder.AddElement<double  >(off, double_val, 0); break;
723       }
724     }
725     objects[i] = builder.EndTable(start, fields_per_object);
726   }
727   builder.PreAlign<flatbuffers::largest_scalar_t>(0);  // Align whole buffer.
728 
729   lcg_reset();  // Reset.
730 
731   uint8_t *eob = builder.GetCurrentBufferPointer() + builder.GetSize();
732 
733   // Test that all objects we generated are readable and return the
734   // expected values. We generate random objects in the same order
735   // so this is deterministic.
736   for (int i = 0; i < num_fuzz_objects; i++) {
737     auto table = reinterpret_cast<flatbuffers::Table *>(eob - objects[i]);
738     for (flatbuffers::voffset_t f = 0; f < fields_per_object; f++) {
739       int choice = lcg_rand() % test_values_max;
740       flatbuffers::voffset_t off = flatbuffers::FieldIndexToOffset(f);
741       switch (choice) {
742         case 0:  CompareTableFieldValue(table, off, bool_val  ); break;
743         case 1:  CompareTableFieldValue(table, off, char_val  ); break;
744         case 2:  CompareTableFieldValue(table, off, uchar_val ); break;
745         case 3:  CompareTableFieldValue(table, off, short_val ); break;
746         case 4:  CompareTableFieldValue(table, off, ushort_val); break;
747         case 5:  CompareTableFieldValue(table, off, int_val   ); break;
748         case 6:  CompareTableFieldValue(table, off, uint_val  ); break;
749         case 7:  CompareTableFieldValue(table, off, long_val  ); break;
750         case 8:  CompareTableFieldValue(table, off, ulong_val ); break;
751         case 9:  CompareTableFieldValue(table, off, float_val ); break;
752         case 10: CompareTableFieldValue(table, off, double_val); break;
753       }
754     }
755   }
756 }
757 
758 // High level stress/fuzz test: generate a big schema and
759 // matching json data in random combinations, then parse both,
760 // generate json back from the binary, and compare with the original.
FuzzTest2()761 void FuzzTest2() {
762   lcg_reset();  // Keep it deterministic.
763 
764   const int num_definitions = 30;
765   const int num_struct_definitions = 5;  // Subset of num_definitions.
766   const int fields_per_definition = 15;
767   const int instances_per_definition = 5;
768   const int deprecation_rate = 10;        // 1 in deprecation_rate fields will
769                                           // be deprecated.
770 
771   std::string schema = "namespace test;\n\n";
772 
773   struct RndDef {
774     std::string instances[instances_per_definition];
775 
776     // Since we're generating schema and corresponding data in tandem,
777     // this convenience function adds strings to both at once.
778     static void Add(RndDef (&definitions_l)[num_definitions],
779                     std::string &schema_l,
780                     const int instances_per_definition_l,
781                     const char *schema_add, const char *instance_add,
782                     int definition) {
783       schema_l += schema_add;
784       for (int i = 0; i < instances_per_definition_l; i++)
785         definitions_l[definition].instances[i] += instance_add;
786     }
787   };
788 
789   #define AddToSchemaAndInstances(schema_add, instance_add) \
790     RndDef::Add(definitions, schema, instances_per_definition, \
791                 schema_add, instance_add, definition)
792 
793   #define Dummy() \
794     RndDef::Add(definitions, schema, instances_per_definition, \
795                 "byte", "1", definition)
796 
797   RndDef definitions[num_definitions];
798 
799   // We are going to generate num_definitions, the first
800   // num_struct_definitions will be structs, the rest tables. For each
801   // generate random fields, some of which may be struct/table types
802   // referring to previously generated structs/tables.
803   // Simultanenously, we generate instances_per_definition JSON data
804   // definitions, which will have identical structure to the schema
805   // being generated. We generate multiple instances such that when creating
806   // hierarchy, we get some variety by picking one randomly.
807   for (int definition = 0; definition < num_definitions; definition++) {
808     std::string definition_name = "D" + flatbuffers::NumToString(definition);
809 
810     bool is_struct = definition < num_struct_definitions;
811 
812     AddToSchemaAndInstances(
813       ((is_struct ? "struct " : "table ") + definition_name + " {\n").c_str(),
814       "{\n");
815 
816     for (int field = 0; field < fields_per_definition; field++) {
817       const bool is_last_field = field == fields_per_definition - 1;
818 
819       // Deprecate 1 in deprecation_rate fields. Only table fields can be
820       // deprecated.
821       // Don't deprecate the last field to avoid dangling commas in JSON.
822       const bool deprecated = !is_struct &&
823                               !is_last_field &&
824                               (lcg_rand() % deprecation_rate == 0);
825 
826       std::string field_name = "f" + flatbuffers::NumToString(field);
827       AddToSchemaAndInstances(("  " + field_name + ":").c_str(),
828                               deprecated ? "" : (field_name + ": ").c_str());
829       // Pick random type:
830       int base_type = lcg_rand() % (flatbuffers::BASE_TYPE_UNION + 1);
831       switch (base_type) {
832         case flatbuffers::BASE_TYPE_STRING:
833           if (is_struct) {
834             Dummy();  // No strings in structs.
835           } else {
836             AddToSchemaAndInstances("string", deprecated ? "" : "\"hi\"");
837           }
838           break;
839         case flatbuffers::BASE_TYPE_VECTOR:
840           if (is_struct) {
841             Dummy();  // No vectors in structs.
842           }
843           else {
844             AddToSchemaAndInstances("[ubyte]",
845                                     deprecated ? "" : "[\n0,\n1,\n255\n]");
846           }
847           break;
848         case flatbuffers::BASE_TYPE_NONE:
849         case flatbuffers::BASE_TYPE_UTYPE:
850         case flatbuffers::BASE_TYPE_STRUCT:
851         case flatbuffers::BASE_TYPE_UNION:
852           if (definition) {
853             // Pick a random previous definition and random data instance of
854             // that definition.
855             int defref = lcg_rand() % definition;
856             int instance = lcg_rand() % instances_per_definition;
857             AddToSchemaAndInstances(
858               ("D" + flatbuffers::NumToString(defref)).c_str(),
859               deprecated
860                 ? ""
861                 : definitions[defref].instances[instance].c_str());
862           } else {
863             // If this is the first definition, we have no definition we can
864             // refer to.
865             Dummy();
866           }
867           break;
868         case flatbuffers::BASE_TYPE_BOOL:
869           AddToSchemaAndInstances("bool", deprecated
870                                   ? ""
871                                   : (lcg_rand() % 2 ? "true" : "false"));
872           break;
873         default:
874           // All the scalar types.
875           schema += flatbuffers::kTypeNames[base_type];
876 
877           if (!deprecated) {
878             // We want each instance to use its own random value.
879             for (int inst = 0; inst < instances_per_definition; inst++)
880               definitions[definition].instances[inst] +=
881               flatbuffers::NumToString(lcg_rand() % 128).c_str();
882           }
883       }
884       AddToSchemaAndInstances(
885         deprecated ? "(deprecated);\n" : ";\n",
886         deprecated ? "" : is_last_field ? "\n" : ",\n");
887     }
888     AddToSchemaAndInstances("}\n\n", "}");
889   }
890 
891   schema += "root_type D" + flatbuffers::NumToString(num_definitions - 1);
892   schema += ";\n";
893 
894   flatbuffers::Parser parser;
895 
896   // Will not compare against the original if we don't write defaults
897   parser.builder_.ForceDefaults(true);
898 
899   // Parse the schema, parse the generated data, then generate text back
900   // from the binary and compare against the original.
901   TEST_EQ(parser.Parse(schema.c_str()), true);
902 
903   const std::string &json =
904     definitions[num_definitions - 1].instances[0] + "\n";
905 
906   TEST_EQ(parser.Parse(json.c_str()), true);
907 
908   std::string jsongen;
909   parser.opts.indent_step = 0;
910   auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
911   TEST_EQ(result, true);
912 
913   if (jsongen != json) {
914     // These strings are larger than a megabyte, so we show the bytes around
915     // the first bytes that are different rather than the whole string.
916     size_t len = std::min(json.length(), jsongen.length());
917     for (size_t i = 0; i < len; i++) {
918       if (json[i] != jsongen[i]) {
919         i -= std::min(static_cast<size_t>(10), i); // show some context;
920         size_t end = std::min(len, i + 20);
921         for (; i < end; i++)
922           printf("at %d: found \"%c\", expected \"%c\"\n",
923                static_cast<int>(i), jsongen[i], json[i]);
924         break;
925       }
926     }
927     TEST_NOTNULL(NULL);
928   }
929 
930   printf("%dk schema tested with %dk of json\n",
931          static_cast<int>(schema.length() / 1024),
932          static_cast<int>(json.length() / 1024));
933 }
934 
935 // Test that parser errors are actually generated.
TestError(const char * src,const char * error_substr,bool strict_json=false)936 void TestError(const char *src, const char *error_substr,
937                bool strict_json = false) {
938   flatbuffers::IDLOptions opts;
939   opts.strict_json = strict_json;
940   flatbuffers::Parser parser(opts);
941   TEST_EQ(parser.Parse(src), false);  // Must signal error
942   // Must be the error we're expecting
943   TEST_NOTNULL(strstr(parser.error_.c_str(), error_substr));
944 }
945 
946 // Test that parsing errors occur as we'd expect.
947 // Also useful for coverage, making sure these paths are run.
ErrorTest()948 void ErrorTest() {
949   // In order they appear in idl_parser.cpp
950   TestError("table X { Y:byte; } root_type X; { Y: 999 }", "bit field");
951   TestError(".0", "floating point");
952   TestError("\"\0", "illegal");
953   TestError("\"\\q", "escape code");
954   TestError("table ///", "documentation");
955   TestError("@", "illegal");
956   TestError("table 1", "expecting");
957   TestError("table X { Y:[[int]]; }", "nested vector");
958   TestError("table X { Y:1; }", "illegal type");
959   TestError("table X { Y:int; Y:int; }", "field already");
960   TestError("struct X { Y:string; }", "only scalar");
961   TestError("struct X { Y:int (deprecated); }", "deprecate");
962   TestError("union Z { X } table X { Y:Z; } root_type X; { Y: {}, A:1 }",
963             "missing type field");
964   TestError("union Z { X } table X { Y:Z; } root_type X; { Y_type: 99, Y: {",
965             "type id");
966   TestError("table X { Y:int; } root_type X; { Z:", "unknown field");
967   TestError("table X { Y:int; } root_type X; { Y:", "string constant", true);
968   TestError("table X { Y:int; } root_type X; { \"Y\":1, }", "string constant",
969             true);
970   TestError("struct X { Y:int; Z:int; } table W { V:X; } root_type W; "
971             "{ V:{ Y:1 } }", "wrong number");
972   TestError("enum E:byte { A } table X { Y:E; } root_type X; { Y:U }",
973             "unknown enum value");
974   TestError("table X { Y:byte; } root_type X; { Y:; }", "starting");
975   TestError("enum X:byte { Y } enum X {", "enum already");
976   TestError("enum X:float {}", "underlying");
977   TestError("enum X:byte { Y, Y }", "value already");
978   TestError("enum X:byte { Y=2, Z=1 }", "ascending");
979   TestError("enum X:byte (bit_flags) { Y=8 }", "bit flag out");
980   TestError("table X { Y:int; } table X {", "datatype already");
981   TestError("struct X (force_align: 7) { Y:int; }", "force_align");
982   TestError("{}", "no root");
983   TestError("table X { Y:byte; } root_type X; { Y:1 } { Y:1 }", "one json");
984   TestError("root_type X;", "unknown root");
985   TestError("struct X { Y:int; } root_type X;", "a table");
986   TestError("union X { Y }", "referenced");
987   TestError("union Z { X } struct X { Y:int; }", "only tables");
988   TestError("table X { Y:[int]; YLength:int; }", "clash");
989   TestError("table X { Y:string = 1; }", "scalar");
990   TestError("table X { Y:byte; } root_type X; { Y:1, Y:2 }", "more than once");
991 }
992 
TestValue(const char * json,const char * type_name)993 template<typename T> T TestValue(const char *json, const char *type_name) {
994   flatbuffers::Parser parser;
995 
996   // Simple schema.
997   TEST_EQ(parser.Parse(std::string("table X { Y:" + std::string(type_name) +
998                                    "; } root_type X;").c_str()), true);
999 
1000   TEST_EQ(parser.Parse(json), true);
1001   auto root = flatbuffers::GetRoot<flatbuffers::Table>(
1002                 parser.builder_.GetBufferPointer());
1003   return root->GetField<T>(flatbuffers::FieldIndexToOffset(0), 0);
1004 }
1005 
FloatCompare(float a,float b)1006 bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; }
1007 
1008 // Additional parser testing not covered elsewhere.
ValueTest()1009 void ValueTest() {
1010   // Test scientific notation numbers.
1011   TEST_EQ(FloatCompare(TestValue<float>("{ Y:0.0314159e+2 }","float"),
1012                        (float)3.14159), true);
1013 
1014   // Test conversion functions.
1015   TEST_EQ(FloatCompare(TestValue<float>("{ Y:cos(rad(180)) }","float"), -1),
1016           true);
1017 
1018   // Test negative hex constant.
1019   TEST_EQ(TestValue<int>("{ Y:-0x80 }","int"), -128);
1020 
1021   // Make sure we do unsigned 64bit correctly.
1022   TEST_EQ(TestValue<uint64_t>("{ Y:12335089644688340133 }","ulong"),
1023                               12335089644688340133ULL);
1024 }
1025 
EnumStringsTest()1026 void EnumStringsTest() {
1027   flatbuffers::Parser parser1;
1028   TEST_EQ(parser1.Parse("enum E:byte { A, B, C } table T { F:[E]; }"
1029                         "root_type T;"
1030                         "{ F:[ A, B, \"C\", \"A B C\" ] }"), true);
1031   flatbuffers::Parser parser2;
1032   TEST_EQ(parser2.Parse("enum E:byte { A, B, C } table T { F:[int]; }"
1033                         "root_type T;"
1034                         "{ F:[ \"E.C\", \"E.A E.B E.C\" ] }"), true);
1035 }
1036 
IntegerOutOfRangeTest()1037 void IntegerOutOfRangeTest() {
1038   TestError("table T { F:byte; } root_type T; { F:256 }",
1039             "constant does not fit");
1040   TestError("table T { F:byte; } root_type T; { F:-257 }",
1041             "constant does not fit");
1042   TestError("table T { F:ubyte; } root_type T; { F:256 }",
1043             "constant does not fit");
1044   TestError("table T { F:ubyte; } root_type T; { F:-257 }",
1045             "constant does not fit");
1046   TestError("table T { F:short; } root_type T; { F:65536 }",
1047             "constant does not fit");
1048   TestError("table T { F:short; } root_type T; { F:-65537 }",
1049             "constant does not fit");
1050   TestError("table T { F:ushort; } root_type T; { F:65536 }",
1051             "constant does not fit");
1052   TestError("table T { F:ushort; } root_type T; { F:-65537 }",
1053             "constant does not fit");
1054   TestError("table T { F:int; } root_type T; { F:4294967296 }",
1055             "constant does not fit");
1056   TestError("table T { F:int; } root_type T; { F:-4294967297 }",
1057             "constant does not fit");
1058   TestError("table T { F:uint; } root_type T; { F:4294967296 }",
1059             "constant does not fit");
1060   TestError("table T { F:uint; } root_type T; { F:-4294967297 }",
1061             "constant does not fit");
1062 }
1063 
UnicodeTest()1064 void UnicodeTest() {
1065   flatbuffers::Parser parser;
1066   // Without setting allow_non_utf8 = true, we treat \x sequences as byte sequences
1067   // which are then validated as UTF-8.
1068   TEST_EQ(parser.Parse("table T { F:string; }"
1069                        "root_type T;"
1070                        "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
1071                        "\\u5225\\u30B5\\u30A4\\u30C8\\xE2\\x82\\xAC\\u0080\\uD83D\\uDE0E\" }"),
1072           true);
1073   std::string jsongen;
1074   parser.opts.indent_step = -1;
1075   auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
1076   TEST_EQ(result, true);
1077   TEST_EQ(jsongen,
1078           std::string(
1079             "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
1080             "\\u5225\\u30B5\\u30A4\\u30C8\\u20AC\\u0080\\uD83D\\uDE0E\"}"));
1081 }
1082 
UnicodeTestAllowNonUTF8()1083 void UnicodeTestAllowNonUTF8() {
1084   flatbuffers::Parser parser;
1085   parser.opts.allow_non_utf8 = true;
1086   TEST_EQ(parser.Parse("table T { F:string; }"
1087                        "root_type T;"
1088                        "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
1089                        "\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\\u0080\\uD83D\\uDE0E\" }"), true);
1090   std::string jsongen;
1091   parser.opts.indent_step = -1;
1092   auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
1093   TEST_EQ(result, true);
1094   TEST_EQ(jsongen,
1095           std::string(
1096             "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
1097             "\\u5225\\u30B5\\u30A4\\u30C8\\u0001\\x80\\u0080\\uD83D\\uDE0E\"}"));
1098 }
1099 
UnicodeTestGenerateTextFailsOnNonUTF8()1100 void UnicodeTestGenerateTextFailsOnNonUTF8() {
1101   flatbuffers::Parser parser;
1102   // Allow non-UTF-8 initially to model what happens when we load a binary flatbuffer from disk
1103   // which contains non-UTF-8 strings.
1104   parser.opts.allow_non_utf8 = true;
1105   TEST_EQ(parser.Parse("table T { F:string; }"
1106                        "root_type T;"
1107                        "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC"
1108                        "\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\\u0080\\uD83D\\uDE0E\" }"), true);
1109   std::string jsongen;
1110   parser.opts.indent_step = -1;
1111   // Now, disallow non-UTF-8 (the default behavior) so GenerateText indicates failure.
1112   parser.opts.allow_non_utf8 = false;
1113   auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
1114   TEST_EQ(result, false);
1115 }
1116 
UnicodeSurrogatesTest()1117 void UnicodeSurrogatesTest() {
1118   flatbuffers::Parser parser;
1119 
1120   TEST_EQ(
1121     parser.Parse(
1122       "table T { F:string (id: 0); }"
1123       "root_type T;"
1124       "{ F:\"\\uD83D\\uDCA9\"}"), true);
1125   auto root = flatbuffers::GetRoot<flatbuffers::Table>(
1126     parser.builder_.GetBufferPointer());
1127   auto string = root->GetPointer<flatbuffers::String *>(
1128     flatbuffers::FieldIndexToOffset(0));
1129   TEST_EQ(strcmp(string->c_str(), "\xF0\x9F\x92\xA9"), 0);
1130 }
1131 
UnicodeInvalidSurrogatesTest()1132 void UnicodeInvalidSurrogatesTest() {
1133   TestError(
1134     "table T { F:string; }"
1135     "root_type T;"
1136     "{ F:\"\\uD800\"}", "unpaired high surrogate");
1137   TestError(
1138     "table T { F:string; }"
1139     "root_type T;"
1140     "{ F:\"\\uD800abcd\"}", "unpaired high surrogate");
1141   TestError(
1142     "table T { F:string; }"
1143     "root_type T;"
1144     "{ F:\"\\uD800\\n\"}", "unpaired high surrogate");
1145   TestError(
1146     "table T { F:string; }"
1147     "root_type T;"
1148     "{ F:\"\\uD800\\uD800\"}", "multiple high surrogates");
1149   TestError(
1150     "table T { F:string; }"
1151     "root_type T;"
1152     "{ F:\"\\uDC00\"}", "unpaired low surrogate");
1153 }
1154 
InvalidUTF8Test()1155 void InvalidUTF8Test() {
1156   // "1 byte" pattern, under min length of 2 bytes
1157   TestError(
1158     "table T { F:string; }"
1159     "root_type T;"
1160     "{ F:\"\x80\"}", "illegal UTF-8 sequence");
1161   // 2 byte pattern, string too short
1162   TestError(
1163     "table T { F:string; }"
1164     "root_type T;"
1165     "{ F:\"\xDF\"}", "illegal UTF-8 sequence");
1166   // 3 byte pattern, string too short
1167   TestError(
1168     "table T { F:string; }"
1169     "root_type T;"
1170     "{ F:\"\xEF\xBF\"}", "illegal UTF-8 sequence");
1171   // 4 byte pattern, string too short
1172   TestError(
1173     "table T { F:string; }"
1174     "root_type T;"
1175     "{ F:\"\xF7\xBF\xBF\"}", "illegal UTF-8 sequence");
1176   // "5 byte" pattern, string too short
1177   TestError(
1178     "table T { F:string; }"
1179     "root_type T;"
1180     "{ F:\"\xFB\xBF\xBF\xBF\"}", "illegal UTF-8 sequence");
1181   // "6 byte" pattern, string too short
1182   TestError(
1183     "table T { F:string; }"
1184     "root_type T;"
1185     "{ F:\"\xFD\xBF\xBF\xBF\xBF\"}", "illegal UTF-8 sequence");
1186   // "7 byte" pattern, string too short
1187   TestError(
1188     "table T { F:string; }"
1189     "root_type T;"
1190     "{ F:\"\xFE\xBF\xBF\xBF\xBF\xBF\"}", "illegal UTF-8 sequence");
1191   // "5 byte" pattern, over max length of 4 bytes
1192   TestError(
1193     "table T { F:string; }"
1194     "root_type T;"
1195     "{ F:\"\xFB\xBF\xBF\xBF\xBF\"}", "illegal UTF-8 sequence");
1196   // "6 byte" pattern, over max length of 4 bytes
1197   TestError(
1198     "table T { F:string; }"
1199     "root_type T;"
1200     "{ F:\"\xFD\xBF\xBF\xBF\xBF\xBF\"}", "illegal UTF-8 sequence");
1201   // "7 byte" pattern, over max length of 4 bytes
1202   TestError(
1203     "table T { F:string; }"
1204     "root_type T;"
1205     "{ F:\"\xFE\xBF\xBF\xBF\xBF\xBF\xBF\"}", "illegal UTF-8 sequence");
1206 
1207   // Three invalid encodings for U+000A (\n, aka NEWLINE)
1208   TestError(
1209     "table T { F:string; }"
1210     "root_type T;"
1211     "{ F:\"\xC0\x8A\"}", "illegal UTF-8 sequence");
1212   TestError(
1213     "table T { F:string; }"
1214     "root_type T;"
1215     "{ F:\"\xE0\x80\x8A\"}", "illegal UTF-8 sequence");
1216   TestError(
1217     "table T { F:string; }"
1218     "root_type T;"
1219     "{ F:\"\xF0\x80\x80\x8A\"}", "illegal UTF-8 sequence");
1220 
1221   // Two invalid encodings for U+00A9 (COPYRIGHT SYMBOL)
1222   TestError(
1223     "table T { F:string; }"
1224     "root_type T;"
1225     "{ F:\"\xE0\x81\xA9\"}", "illegal UTF-8 sequence");
1226   TestError(
1227     "table T { F:string; }"
1228     "root_type T;"
1229     "{ F:\"\xF0\x80\x81\xA9\"}", "illegal UTF-8 sequence");
1230 
1231   // Invalid encoding for U+20AC (EURO SYMBOL)
1232   TestError(
1233     "table T { F:string; }"
1234     "root_type T;"
1235     "{ F:\"\xF0\x82\x82\xAC\"}", "illegal UTF-8 sequence");
1236 
1237   // UTF-16 surrogate values between U+D800 and U+DFFF cannot be encoded in UTF-8
1238   TestError(
1239     "table T { F:string; }"
1240     "root_type T;"
1241     // U+10400 "encoded" as U+D801 U+DC00
1242     "{ F:\"\xED\xA0\x81\xED\xB0\x80\"}", "illegal UTF-8 sequence");
1243 }
1244 
UnknownFieldsTest()1245 void UnknownFieldsTest() {
1246   flatbuffers::IDLOptions opts;
1247   opts.skip_unexpected_fields_in_json = true;
1248   flatbuffers::Parser parser(opts);
1249 
1250   TEST_EQ(parser.Parse("table T { str:string; i:int;}"
1251                        "root_type T;"
1252                        "{ str:\"test\","
1253                        "unknown_string:\"test\","
1254                        "\"unknown_string\":\"test\","
1255                        "unknown_int:10,"
1256                        "unknown_float:1.0,"
1257                        "unknown_array: [ 1, 2, 3, 4],"
1258                        "unknown_object: { i: 10 },"
1259                        "\"unknown_object\": { \"i\": 10 },"
1260                        "i:10}"), true);
1261 
1262   std::string jsongen;
1263   parser.opts.indent_step = -1;
1264   auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen);
1265   TEST_EQ(result, true);
1266   TEST_EQ(jsongen == "{str: \"test\",i: 10}", true);
1267 }
1268 
ParseUnionTest()1269 void ParseUnionTest() {
1270   // Unions must be parseable with the type field following the object.
1271   flatbuffers::Parser parser;
1272   TEST_EQ(parser.Parse("table T { A:int; }"
1273                        "union U { T }"
1274                        "table V { X:U; }"
1275                        "root_type V;"
1276                        "{ X:{ A:1 }, X_type: T }"), true);
1277   // Unions must be parsable with prefixed namespace.
1278   flatbuffers::Parser parser2;
1279   TEST_EQ(parser2.Parse("namespace N; table A {} namespace; union U { N.A }"
1280                         "table B { e:U; } root_type B;"
1281                         "{ e_type: N_A, e: {} }"), true);
1282 }
1283 
UnionVectorTest()1284 void UnionVectorTest() {
1285   // load FlatBuffer fbs schema.
1286   // TODO: load a JSON file with such a vector when JSON support is ready.
1287   std::string schemafile;
1288   TEST_EQ(flatbuffers::LoadFile(
1289     "tests/union_vector/union_vector.fbs", false, &schemafile), true);
1290 
1291   // parse schema.
1292   flatbuffers::IDLOptions idl_opts;
1293   idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kCpp;
1294   flatbuffers::Parser parser(idl_opts);
1295   const char *include_directories[] = { "tests/union_vector", nullptr };
1296   TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
1297 
1298   flatbuffers::FlatBufferBuilder fbb;
1299 
1300   // union types.
1301   std::vector<uint8_t> types;
1302   types.push_back(static_cast<uint8_t>(Character_Belle));
1303   types.push_back(static_cast<uint8_t>(Character_Rapunzel));
1304   types.push_back(static_cast<uint8_t>(Character_MuLan));
1305 
1306   // union values.
1307   std::vector<flatbuffers::Offset<void>> characters;
1308   characters.push_back(CreateBelle(fbb, /*books_read=*/7).Union());
1309   characters.push_back(CreateRapunzel(fbb, /*hair_length=*/6).Union());
1310   characters.push_back(CreateMuLan(fbb, /*sword_attack_damage=*/5).Union());
1311 
1312   // create Movie.
1313   const auto movie_offset =
1314       CreateMovie(fbb, fbb.CreateVector(types), fbb.CreateVector(characters));
1315   FinishMovieBuffer(fbb, movie_offset);
1316   uint8_t *buf = fbb.GetBufferPointer();
1317 
1318   flatbuffers::Verifier verifier(buf, fbb.GetSize());
1319   TEST_EQ(VerifyMovieBuffer(verifier), true);
1320 
1321   const Movie *movie = GetMovie(buf);
1322   TEST_EQ(movie->characters_type()->size(), 3);
1323   TEST_EQ(
1324       movie->characters_type()->GetEnum<Character>(0) == Character_Belle,
1325       true);
1326   TEST_EQ(
1327       movie->characters_type()->GetEnum<Character>(1) == Character_Rapunzel,
1328       true);
1329   TEST_EQ(
1330       movie->characters_type()->GetEnum<Character>(2) == Character_MuLan,
1331       true);
1332 
1333   TEST_EQ(movie->characters()->size(), 3);
1334   const Belle *belle =
1335       reinterpret_cast<const Belle*>(movie->characters()->Get(0));
1336   TEST_EQ(belle->books_read(), 7);
1337   const Rapunzel *rapunzel =
1338       reinterpret_cast<const Rapunzel*>(movie->characters()->Get(1));
1339   TEST_EQ(rapunzel->hair_length(), 6);
1340   const MuLan *mu_lan =
1341       reinterpret_cast<const MuLan*>(movie->characters()->Get(2));
1342   TEST_EQ(mu_lan->sword_attack_damage(), 5);
1343 }
1344 
ConformTest()1345 void ConformTest() {
1346   flatbuffers::Parser parser;
1347   TEST_EQ(parser.Parse("table T { A:int; } enum E:byte { A }"), true);
1348 
1349   auto test_conform = [&](const char *test, const char *expected_err) {
1350     flatbuffers::Parser parser2;
1351     TEST_EQ(parser2.Parse(test), true);
1352     auto err = parser2.ConformTo(parser);
1353     TEST_NOTNULL(strstr(err.c_str(), expected_err));
1354   };
1355 
1356   test_conform("table T { A:byte; }", "types differ for field");
1357   test_conform("table T { B:int; A:int; }", "offsets differ for field");
1358   test_conform("table T { A:int = 1; }", "defaults differ for field");
1359   test_conform("table T { B:float; }", "field renamed to different type");
1360   test_conform("enum E:byte { B, A }", "values differ for enum");
1361 }
1362 
FlexBuffersTest()1363 void FlexBuffersTest() {
1364   flexbuffers::Builder slb(512,
1365                            flexbuffers::BUILDER_FLAG_SHARE_KEYS_AND_STRINGS);
1366 
1367   // Write the equivalent of:
1368   // { vec: [ -100, "Fred", 4.0 ], bar: [ 1, 2, 3 ], foo: 100 }
1369   slb.Map([&]() {
1370      slb.Vector("vec", [&]() {
1371       slb += -100;  // Equivalent to slb.Add(-100) or slb.Int(-100);
1372       slb += "Fred";
1373       slb.IndirectFloat(4.0f);
1374     });
1375     int ints[] = { 1, 2, 3 };
1376     slb.Vector("bar", ints, 3);
1377     slb.FixedTypedVector("bar3", ints, 3);
1378     slb.Double("foo", 100);
1379     slb.Map("mymap", [&]() {
1380       slb.String("foo", "Fred");  // Testing key and string reuse.
1381     });
1382   });
1383   slb.Finish();
1384 
1385   for (size_t i = 0; i < slb.GetBuffer().size(); i++)
1386     printf("%d ", slb.GetBuffer().data()[i]);
1387   printf("\n");
1388 
1389   auto map = flexbuffers::GetRoot(slb.GetBuffer()).AsMap();
1390   TEST_EQ(map.size(), 5);
1391   auto vec = map["vec"].AsVector();
1392   TEST_EQ(vec.size(), 3);
1393   TEST_EQ(vec[0].AsInt64(), -100);
1394   TEST_EQ_STR(vec[1].AsString().c_str(), "Fred");
1395   TEST_EQ(vec[1].AsInt64(), 0);  // Number parsing failed.
1396   TEST_EQ(vec[2].AsDouble(), 4.0);
1397   TEST_EQ(vec[2].AsString().IsTheEmptyString(), true);  // Wrong Type.
1398   TEST_EQ_STR(vec[2].AsString().c_str(), "");  // This still works though.
1399   TEST_EQ_STR(vec[2].ToString().c_str(), "4");  // Or have it converted.
1400   auto tvec = map["bar"].AsTypedVector();
1401   TEST_EQ(tvec.size(), 3);
1402   TEST_EQ(tvec[2].AsInt8(), 3);
1403   auto tvec3 = map["bar3"].AsFixedTypedVector();
1404   TEST_EQ(tvec3.size(), 3);
1405   TEST_EQ(tvec3[2].AsInt8(), 3);
1406   TEST_EQ(map["foo"].AsUInt8(), 100);
1407   TEST_EQ(map["unknown"].IsNull(), true);
1408   auto mymap = map["mymap"].AsMap();
1409   // These should be equal by pointer equality, since key and value are shared.
1410   TEST_EQ(mymap.Keys()[0].AsKey(), map.Keys()[2].AsKey());
1411   TEST_EQ(mymap.Values()[0].AsString().c_str(), vec[1].AsString().c_str());
1412   // We can mutate values in the buffer.
1413   TEST_EQ(vec[0].MutateInt(-99), true);
1414   TEST_EQ(vec[0].AsInt64(), -99);
1415   TEST_EQ(vec[1].MutateString("John"), true);  // Size must match.
1416   TEST_EQ_STR(vec[1].AsString().c_str(), "John");
1417   TEST_EQ(vec[1].MutateString("Alfred"), false);  // Too long.
1418   TEST_EQ(vec[2].MutateFloat(2.0f), true);
1419   TEST_EQ(vec[2].AsFloat(), 2.0f);
1420   TEST_EQ(vec[2].MutateFloat(3.14159), false);  // Double does not fit in float.
1421 }
1422 
main(int,const char * [])1423 int main(int /*argc*/, const char * /*argv*/[]) {
1424   // Run our various test suites:
1425 
1426   std::string rawbuf;
1427   auto flatbuf = CreateFlatBufferTest(rawbuf);
1428   AccessFlatBufferTest(reinterpret_cast<const uint8_t *>(rawbuf.c_str()),
1429                        rawbuf.length());
1430   AccessFlatBufferTest(flatbuf.get(), rawbuf.length());
1431 
1432   MutateFlatBuffersTest(flatbuf.get(), rawbuf.length());
1433 
1434   ObjectFlatBuffersTest(flatbuf.get());
1435 
1436   SizePrefixedTest();
1437 
1438   #ifndef FLATBUFFERS_NO_FILE_TESTS
1439   ParseAndGenerateTextTest();
1440   ReflectionTest(flatbuf.get(), rawbuf.length());
1441   ParseProtoTest();
1442   UnionVectorTest();
1443   #endif
1444 
1445   FuzzTest1();
1446   FuzzTest2();
1447 
1448   ErrorTest();
1449   ValueTest();
1450   EnumStringsTest();
1451   IntegerOutOfRangeTest();
1452   UnicodeTest();
1453   UnicodeTestAllowNonUTF8();
1454   UnicodeTestGenerateTextFailsOnNonUTF8();
1455   UnicodeSurrogatesTest();
1456   UnicodeInvalidSurrogatesTest();
1457   InvalidUTF8Test();
1458   UnknownFieldsTest();
1459   ParseUnionTest();
1460   ConformTest();
1461 
1462   FlexBuffersTest();
1463 
1464   if (!testing_fails) {
1465     TEST_OUTPUT_LINE("ALL TESTS PASSED");
1466     return 0;
1467   } else {
1468     TEST_OUTPUT_LINE("%d FAILED TESTS", testing_fails);
1469     return 1;
1470   }
1471 }
1472 
1473