...

Text file src/github.com/google/flatbuffers/tests/monster_test.cpp

Documentation: github.com/google/flatbuffers/tests

     1#include "monster_test.h"
     2
     3#include <limits>
     4#include <vector>
     5
     6#include "flatbuffers/base.h"
     7#include "flatbuffers/flatbuffer_builder.h"
     8#include "flatbuffers/flatbuffers.h"
     9#include "flatbuffers/idl.h"
    10#include "flatbuffers/registry.h"
    11#include "flatbuffers/verifier.h"
    12#include "is_quiet_nan.h"
    13#include "monster_extra_generated.h"
    14#include "monster_test_generated.h"
    15#include "test_assert.h"
    16
    17namespace flatbuffers {
    18namespace tests {
    19
    20// Shortcuts for the infinity.
    21static const auto infinity_f = std::numeric_limits<float>::infinity();
    22static const auto infinity_d = std::numeric_limits<double>::infinity();
    23
    24using namespace MyGame::Example;
    25
    26// example of how to build up a serialized buffer algorithmically:
    27flatbuffers::DetachedBuffer CreateFlatBufferTest(std::string &buffer) {
    28  flatbuffers::FlatBufferBuilder builder;
    29
    30  auto vec = Vec3(1, 2, 3, 0, Color_Red, Test(10, 20));
    31
    32  auto name = builder.CreateString("MyMonster");
    33
    34  // Use the initializer_list specialization of CreateVector.
    35  auto inventory =
    36      builder.CreateVector<uint8_t>({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
    37
    38  // Alternatively, create the vector first, and fill in data later:
    39  // unsigned char *inv_buf = nullptr;
    40  // auto inventory = builder.CreateUninitializedVector<unsigned char>(
    41  //                                                              10, &inv_buf);
    42  // memcpy(inv_buf, inv_data, 10);
    43
    44  Test tests[] = { Test(10, 20), Test(30, 40) };
    45  auto testv = builder.CreateVectorOfStructs(tests, 2);
    46
    47  // Create a vector of structures from a lambda.
    48  auto testv2 = builder.CreateVectorOfStructs<Test>(
    49      2, [&](size_t i, Test *s) -> void { *s = tests[i]; });
    50
    51  // create monster with very few fields set:
    52  // (same functionality as CreateMonster below, but sets fields manually)
    53  flatbuffers::Offset<Monster> mlocs[3];
    54  auto fred = builder.CreateString("Fred");
    55  auto barney = builder.CreateString("Barney");
    56  auto wilma = builder.CreateString("Wilma");
    57  MonsterBuilder mb1(builder);
    58  mb1.add_name(fred);
    59  mlocs[0] = mb1.Finish();
    60  MonsterBuilder mb2(builder);
    61  mb2.add_name(barney);
    62  mb2.add_hp(1000);
    63  mlocs[1] = mb2.Finish();
    64  MonsterBuilder mb3(builder);
    65  mb3.add_name(wilma);
    66  mlocs[2] = mb3.Finish();
    67
    68  // Create an array of strings. Also test string pooling, and lambdas.
    69  auto vecofstrings =
    70      builder.CreateVector<flatbuffers::Offset<flatbuffers::String>>(
    71          4,
    72          [](size_t i, flatbuffers::FlatBufferBuilder *b)
    73              -> flatbuffers::Offset<flatbuffers::String> {
    74            static const char *names[] = { "bob", "fred", "bob", "fred" };
    75            return b->CreateSharedString(names[i]);
    76          },
    77          &builder);
    78
    79  // Creating vectors of strings in one convenient call.
    80  std::vector<std::string> names2;
    81  names2.push_back("jane");
    82  names2.push_back("mary");
    83  auto vecofstrings2 = builder.CreateVectorOfStrings(names2);
    84
    85  // Creating vectors from types that are different from std::string
    86  std::vector<const char *> names3;
    87  names3.push_back("foo");
    88  names3.push_back("bar");
    89  builder.CreateVectorOfStrings(names3);  // Also an accepted type
    90
    91#ifdef FLATBUFFERS_HAS_STRING_VIEW
    92  std::vector<flatbuffers::string_view> names4;
    93  names3.push_back("baz");
    94  names3.push_back("quux");
    95  builder.CreateVectorOfStrings(names4);  // Also an accepted type
    96#endif
    97
    98  // Make sure the template deduces an initializer as std::vector<std::string>
    99  builder.CreateVectorOfStrings({ "hello", "world" });
   100
   101  // Create many vectors of strings
   102  std::vector<std::string> manyNames;
   103  for (auto i = 0; i < 100; i++) { manyNames.push_back("john_doe"); }
   104  auto manyNamesVec = builder.CreateVectorOfStrings(manyNames);
   105  TEST_EQ(false, manyNamesVec.IsNull());
   106  auto manyNamesVec2 =
   107      builder.CreateVectorOfStrings(manyNames.cbegin(), manyNames.cend());
   108  TEST_EQ(false, manyNamesVec2.IsNull());
   109
   110  // Create an array of sorted tables, can be used with binary search when read:
   111  auto vecoftables = builder.CreateVectorOfSortedTables(mlocs, 3);
   112
   113  // Create an array of sorted structs,
   114  // can be used with binary search when read:
   115  std::vector<Ability> abilities;
   116  abilities.push_back(Ability(4, 40));
   117  abilities.push_back(Ability(3, 30));
   118  abilities.push_back(Ability(2, 20));
   119  abilities.push_back(Ability(0, 0));
   120  auto vecofstructs = builder.CreateVectorOfSortedStructs(&abilities);
   121
   122  flatbuffers::Offset<Stat> mlocs_stats[1];
   123  auto miss = builder.CreateString("miss");
   124  StatBuilder mb_miss(builder);
   125  mb_miss.add_id(miss);
   126  mb_miss.add_val(0);
   127  mb_miss.add_count(0);  // key
   128  mlocs_stats[0] = mb_miss.Finish();
   129  auto vec_of_stats = builder.CreateVectorOfSortedTables(mlocs_stats, 1);
   130
   131  // Create a nested FlatBuffer.
   132  // Nested FlatBuffers are stored in a ubyte vector, which can be convenient
   133  // since they can be memcpy'd around much easier than other FlatBuffer
   134  // values. They have little overhead compared to storing the table directly.
   135  // As a test, create a mostly empty Monster buffer:
   136  flatbuffers::FlatBufferBuilder nested_builder;
   137  auto nmloc = CreateMonster(nested_builder, nullptr, 0, 0,
   138                             nested_builder.CreateString("NestedMonster"));
   139  FinishMonsterBuffer(nested_builder, nmloc);
   140  // Now we can store the buffer in the parent. Note that by default, vectors
   141  // are only aligned to their elements or size field, so in this case if the
   142  // buffer contains 64-bit elements, they may not be correctly aligned. We fix
   143  // that with:
   144  builder.ForceVectorAlignment(nested_builder.GetSize(), sizeof(uint8_t),
   145                               nested_builder.GetBufferMinAlignment());
   146  // If for whatever reason you don't have the nested_builder available, you
   147  // can substitute flatbuffers::largest_scalar_t (64-bit) for the alignment, or
   148  // the largest force_align value in your schema if you're using it.
   149  auto nested_flatbuffer_vector = builder.CreateVector(
   150      nested_builder.GetBufferPointer(), nested_builder.GetSize());
   151
   152  // Test a nested FlexBuffer:
   153  flexbuffers::Builder flexbuild;
   154  flexbuild.Int(1234);
   155  flexbuild.Finish();
   156  auto flex = builder.CreateVector(flexbuild.GetBuffer());
   157  // Test vector of enums.
   158  Color colors[] = { Color_Blue, Color_Green };
   159  // We use this special creation function because we have an array of
   160  // pre-C++11 (enum class) enums whose size likely is int, yet its declared
   161  // type in the schema is byte.
   162  auto vecofcolors = builder.CreateVectorScalarCast<uint8_t, Color>(colors, 2);
   163
   164  // shortcut for creating monster with all fields set:
   165  auto mloc = CreateMonster(
   166      builder, &vec, 150, 80, name, inventory, Color_Blue, Any_Monster,
   167      mlocs[1].Union(),  // Store a union.
   168      testv, vecofstrings, vecoftables, 0, nested_flatbuffer_vector, 0, false,
   169      0, 0, 0, 0, 0, 0, 0, 0, 0, 3.14159f, 3.0f, 0.0f, vecofstrings2,
   170      vecofstructs, flex, testv2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   171      AnyUniqueAliases_NONE, 0, AnyAmbiguousAliases_NONE, 0, vecofcolors,
   172      MyGame::Example::Race_None, 0, vec_of_stats);
   173
   174  FinishMonsterBuffer(builder, mloc);
   175
   176  // clang-format off
   177  #ifdef FLATBUFFERS_TEST_VERBOSE
   178  // print byte data for debugging:
   179  auto p = builder.GetBufferPointer();
   180  for (flatbuffers::uoffset_t i = 0; i < builder.GetSize(); i++)
   181    printf("%d ", p[i]);
   182  #endif
   183  // clang-format on
   184
   185  // return the buffer for the caller to use.
   186  auto bufferpointer =
   187      reinterpret_cast<const char *>(builder.GetBufferPointer());
   188  buffer.assign(bufferpointer, bufferpointer + builder.GetSize());
   189
   190  return builder.Release();
   191}
   192
   193//  example of accessing a buffer loaded in memory:
   194void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length, bool pooled) {
   195  // First, verify the buffers integrity (optional)
   196  flatbuffers::Verifier verifier(flatbuf, length);
   197  std::vector<uint8_t> flex_reuse_tracker;
   198  verifier.SetFlexReuseTracker(&flex_reuse_tracker);
   199  TEST_EQ(VerifyMonsterBuffer(verifier), true);
   200
   201  // clang-format off
   202  #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
   203    std::vector<uint8_t> test_buff;
   204    test_buff.resize(length * 2);
   205    std::memcpy(&test_buff[0], flatbuf, length);
   206    std::memcpy(&test_buff[length], flatbuf, length);
   207
   208    flatbuffers::Verifier verifier1(&test_buff[0], length);
   209    TEST_EQ(VerifyMonsterBuffer(verifier1), true);
   210    TEST_EQ(verifier1.GetComputedSize(), length);
   211
   212    flatbuffers::Verifier verifier2(&test_buff[length], length);
   213    TEST_EQ(VerifyMonsterBuffer(verifier2), true);
   214    TEST_EQ(verifier2.GetComputedSize(), length);
   215  #endif
   216  // clang-format on
   217
   218  TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0);
   219  TEST_EQ(MonsterBufferHasIdentifier(flatbuf), true);
   220  TEST_EQ(strcmp(MonsterExtension(), "mon"), 0);
   221
   222  // Access the buffer from the root.
   223  auto monster = GetMonster(flatbuf);
   224
   225  TEST_EQ(monster->hp(), 80);
   226  TEST_EQ(monster->mana(), 150);  // default
   227  TEST_EQ_STR(monster->name()->c_str(), "MyMonster");
   228  // Can't access the following field, it is deprecated in the schema,
   229  // which means accessors are not generated:
   230  // monster.friendly()
   231
   232  auto pos = monster->pos();
   233  TEST_NOTNULL(pos);
   234  TEST_EQ(pos->z(), 3);
   235  TEST_EQ(pos->test3().a(), 10);
   236  TEST_EQ(pos->test3().b(), 20);
   237
   238  auto inventory = monster->inventory();
   239  TEST_EQ(VectorLength(inventory), 10UL);  // Works even if inventory is null.
   240  TEST_NOTNULL(inventory);
   241  unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
   242  // Check compatibilty of iterators with STL.
   243  std::vector<unsigned char> inv_vec(inventory->begin(), inventory->end());
   244  size_t n = 0;
   245  for (auto it = inventory->begin(); it != inventory->end(); ++it, ++n) {
   246    auto indx = it - inventory->begin();
   247    TEST_EQ(*it, inv_vec.at(indx));  // Use bounds-check.
   248    TEST_EQ(*it, inv_data[indx]);
   249  }
   250  TEST_EQ(n, inv_vec.size());
   251
   252  n = 0;
   253  for (auto it = inventory->cbegin(); it != inventory->cend(); ++it, ++n) {
   254    auto indx = it - inventory->cbegin();
   255    TEST_EQ(*it, inv_vec.at(indx));  // Use bounds-check.
   256    TEST_EQ(*it, inv_data[indx]);
   257  }
   258  TEST_EQ(n, inv_vec.size());
   259
   260  n = 0;
   261  for (auto it = inventory->rbegin(); it != inventory->rend(); ++it, ++n) {
   262    auto indx = inventory->rend() - it - 1;
   263    TEST_EQ(*it, inv_vec.at(indx));  // Use bounds-check.
   264    TEST_EQ(*it, inv_data[indx]);
   265  }
   266  TEST_EQ(n, inv_vec.size());
   267
   268  n = 0;
   269  for (auto it = inventory->crbegin(); it != inventory->crend(); ++it, ++n) {
   270    auto indx = inventory->crend() - it - 1;
   271    TEST_EQ(*it, inv_vec.at(indx));  // Use bounds-check.
   272    TEST_EQ(*it, inv_data[indx]);
   273  }
   274  TEST_EQ(n, inv_vec.size());
   275
   276  TEST_EQ(monster->color(), Color_Blue);
   277
   278  // Example of accessing a union:
   279  TEST_EQ(monster->test_type(), Any_Monster);  // First make sure which it is.
   280  auto monster2 = reinterpret_cast<const Monster *>(monster->test());
   281  TEST_NOTNULL(monster2);
   282  TEST_EQ_STR(monster2->name()->c_str(), "Fred");
   283
   284  // Example of accessing a vector of strings:
   285  auto vecofstrings = monster->testarrayofstring();
   286  TEST_EQ(vecofstrings->size(), 4U);
   287  TEST_EQ_STR(vecofstrings->Get(0)->c_str(), "bob");
   288  TEST_EQ_STR(vecofstrings->Get(1)->c_str(), "fred");
   289  if (pooled) {
   290    // These should have pointer equality because of string pooling.
   291    TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str());
   292    TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str());
   293  }
   294
   295  auto vecofstrings2 = monster->testarrayofstring2();
   296  if (vecofstrings2) {
   297    TEST_EQ(vecofstrings2->size(), 2U);
   298    TEST_EQ_STR(vecofstrings2->Get(0)->c_str(), "jane");
   299    TEST_EQ_STR(vecofstrings2->Get(1)->c_str(), "mary");
   300  }
   301
   302  // Example of accessing a vector of tables:
   303  auto vecoftables = monster->testarrayoftables();
   304  TEST_EQ(vecoftables->size(), 3U);
   305  for (auto it = vecoftables->begin(); it != vecoftables->end(); ++it) {
   306    TEST_EQ(strlen(it->name()->c_str()) >= 4, true);
   307  }
   308  TEST_EQ_STR(vecoftables->Get(0)->name()->c_str(), "Barney");
   309  TEST_EQ(vecoftables->Get(0)->hp(), 1000);
   310  TEST_EQ_STR(vecoftables->Get(1)->name()->c_str(), "Fred");
   311  TEST_EQ_STR(vecoftables->Get(2)->name()->c_str(), "Wilma");
   312  TEST_NOTNULL(vecoftables->LookupByKey("Barney"));
   313  TEST_NOTNULL(vecoftables->LookupByKey("Fred"));
   314  TEST_NOTNULL(vecoftables->LookupByKey("Wilma"));
   315
   316  // Test accessing a vector of sorted structs
   317  auto vecofstructs = monster->testarrayofsortedstruct();
   318  if (vecofstructs) {  // not filled in monster_test.bfbs
   319    for (flatbuffers::uoffset_t i = 0; i < vecofstructs->size() - 1; i++) {
   320      auto left = vecofstructs->Get(i);
   321      auto right = vecofstructs->Get(i + 1);
   322      TEST_EQ(true, (left->KeyCompareLessThan(right)));
   323    }
   324    TEST_NOTNULL(vecofstructs->LookupByKey(0));  // test default value
   325    TEST_NOTNULL(vecofstructs->LookupByKey(3));
   326    TEST_EQ(static_cast<const Ability *>(nullptr),
   327            vecofstructs->LookupByKey(5));
   328  }
   329
   330  if (auto vec_of_stat = monster->scalar_key_sorted_tables()) {
   331    auto stat_0 = vec_of_stat->LookupByKey(static_cast<uint16_t>(0u));
   332    TEST_NOTNULL(stat_0);
   333    TEST_NOTNULL(stat_0->id());
   334    TEST_EQ(0, stat_0->count());
   335    TEST_EQ_STR("miss", stat_0->id()->c_str());
   336  }
   337
   338  // Test nested FlatBuffers if available:
   339  auto nested_buffer = monster->testnestedflatbuffer();
   340  if (nested_buffer) {
   341    // nested_buffer is a vector of bytes you can memcpy. However, if you
   342    // actually want to access the nested data, this is a convenient
   343    // accessor that directly gives you the root table:
   344    auto nested_monster = monster->testnestedflatbuffer_nested_root();
   345    TEST_EQ_STR(nested_monster->name()->c_str(), "NestedMonster");
   346  }
   347
   348  // Test flexbuffer if available:
   349  auto flex = monster->flex();
   350  // flex is a vector of bytes you can memcpy etc.
   351  TEST_EQ(flex->size(), 4);  // Encoded FlexBuffer bytes.
   352  // However, if you actually want to access the nested data, this is a
   353  // convenient accessor that directly gives you the root value:
   354  TEST_EQ(monster->flex_flexbuffer_root().AsInt16(), 1234);
   355
   356  // Test vector of enums:
   357  auto colors = monster->vector_of_enums();
   358  if (colors) {
   359    TEST_EQ(colors->size(), 2);
   360    TEST_EQ(colors->Get(0), Color_Blue);
   361    TEST_EQ(colors->Get(1), Color_Green);
   362  }
   363
   364  // Since Flatbuffers uses explicit mechanisms to override the default
   365  // compiler alignment, double check that the compiler indeed obeys them:
   366  // (Test consists of a short and byte):
   367  TEST_EQ(flatbuffers::AlignOf<Test>(), 2UL);
   368  TEST_EQ(sizeof(Test), 4UL);
   369
   370  const flatbuffers::Vector<const Test *> *tests_array[] = {
   371    monster->test4(),
   372    monster->test5(),
   373  };
   374  for (size_t i = 0; i < sizeof(tests_array) / sizeof(tests_array[0]); ++i) {
   375    auto tests = tests_array[i];
   376    TEST_NOTNULL(tests);
   377    auto test_0 = tests->Get(0);
   378    auto test_1 = tests->Get(1);
   379    TEST_EQ(test_0->a(), 10);
   380    TEST_EQ(test_0->b(), 20);
   381    TEST_EQ(test_1->a(), 30);
   382    TEST_EQ(test_1->b(), 40);
   383    for (auto it = tests->begin(); it != tests->end(); ++it) {
   384      TEST_EQ(it->a() == 10 || it->a() == 30, true);  // Just testing iterators.
   385    }
   386  }
   387
   388  // Checking for presence of fields:
   389  TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_HP), true);
   390  TEST_EQ(flatbuffers::IsFieldPresent(monster, Monster::VT_MANA), false);
   391
   392  // Obtaining a buffer from a root:
   393  TEST_EQ(GetBufferStartFromRootPointer(monster), flatbuf);
   394}
   395
   396// Change a FlatBuffer in-place, after it has been constructed.
   397void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
   398  // Get non-const pointer to root.
   399  auto monster = GetMutableMonster(flatbuf);
   400
   401  // Each of these tests mutates, then tests, then set back to the original,
   402  // so we can test that the buffer in the end still passes our original test.
   403  auto hp_ok = monster->mutate_hp(10);
   404  TEST_EQ(hp_ok, true);  // Field was present.
   405  TEST_EQ(monster->hp(), 10);
   406  // Mutate to default value
   407  auto hp_ok_default = monster->mutate_hp(100);
   408  TEST_EQ(hp_ok_default, true);  // Field was present.
   409  TEST_EQ(monster->hp(), 100);
   410  // Test that mutate to default above keeps field valid for further mutations
   411  auto hp_ok_2 = monster->mutate_hp(20);
   412  TEST_EQ(hp_ok_2, true);
   413  TEST_EQ(monster->hp(), 20);
   414  monster->mutate_hp(80);
   415
   416  // Monster originally at 150 mana (default value)
   417  auto mana_default_ok = monster->mutate_mana(150);  // Mutate to default value.
   418  TEST_EQ(mana_default_ok,
   419          true);  // Mutation should succeed, because default value.
   420  TEST_EQ(monster->mana(), 150);
   421  auto mana_ok = monster->mutate_mana(10);
   422  TEST_EQ(mana_ok, false);  // Field was NOT present, because default value.
   423  TEST_EQ(monster->mana(), 150);
   424
   425  // Mutate structs.
   426  auto pos = monster->mutable_pos();
   427  auto &test3 = pos->mutable_test3();  // Struct inside a struct.
   428  test3.mutate_a(50);                  // Struct fields never fail.
   429  TEST_EQ(test3.a(), 50);
   430  test3.mutate_a(10);
   431
   432  // Mutate vectors.
   433  auto inventory = monster->mutable_inventory();
   434  inventory->Mutate(9, 100);
   435  TEST_EQ(inventory->Get(9), 100);
   436  inventory->Mutate(9, 9);
   437
   438  auto tables = monster->mutable_testarrayoftables();
   439  auto first = tables->GetMutableObject(0);
   440  TEST_EQ(first->hp(), 1000);
   441  first->mutate_hp(0);
   442  TEST_EQ(first->hp(), 0);
   443  first->mutate_hp(1000);
   444
   445  // Test for each loop over mutable entries
   446  for (auto item : *tables) {
   447    TEST_EQ(item->hp(), 1000);
   448    item->mutate_hp(0);
   449    TEST_EQ(item->hp(), 0);
   450    item->mutate_hp(1000);
   451    break;  // one iteration is enough, just testing compilation
   452  }
   453
   454  // Mutate via LookupByKey
   455  TEST_NOTNULL(tables->MutableLookupByKey("Barney"));
   456  TEST_EQ(static_cast<Monster *>(nullptr),
   457          tables->MutableLookupByKey("DoesntExist"));
   458  TEST_EQ(tables->MutableLookupByKey("Barney")->hp(), 1000);
   459  TEST_EQ(tables->MutableLookupByKey("Barney")->mutate_hp(0), true);
   460  TEST_EQ(tables->LookupByKey("Barney")->hp(), 0);
   461  TEST_EQ(tables->MutableLookupByKey("Barney")->mutate_hp(1000), true);
   462
   463  // Run the verifier and the regular test to make sure we didn't trample on
   464  // anything.
   465  AccessFlatBufferTest(flatbuf, length);
   466}
   467
   468// Unpack a FlatBuffer into objects.
   469void ObjectFlatBuffersTest(uint8_t *flatbuf) {
   470  // Optional: we can specify resolver and rehasher functions to turn hashed
   471  // strings into object pointers and back, to implement remote references
   472  // and such.
   473  auto resolver = flatbuffers::resolver_function_t(
   474      [](void **pointer_adr, flatbuffers::hash_value_t hash) {
   475        (void)pointer_adr;
   476        (void)hash;
   477        // Don't actually do anything, leave variable null.
   478      });
   479  auto rehasher = flatbuffers::rehasher_function_t(
   480      [](void *pointer) -> flatbuffers::hash_value_t {
   481        (void)pointer;
   482        return 0;
   483      });
   484
   485  // Turn a buffer into C++ objects.
   486  auto monster1 = UnPackMonster(flatbuf, &resolver);
   487
   488  // Re-serialize the data.
   489  flatbuffers::FlatBufferBuilder fbb1;
   490  fbb1.Finish(CreateMonster(fbb1, monster1.get(), &rehasher),
   491              MonsterIdentifier());
   492
   493  // Unpack again, and re-serialize again.
   494  auto monster2 = UnPackMonster(fbb1.GetBufferPointer(), &resolver);
   495  flatbuffers::FlatBufferBuilder fbb2;
   496  fbb2.Finish(CreateMonster(fbb2, monster2.get(), &rehasher),
   497              MonsterIdentifier());
   498
   499  // Now we've gone full round-trip, the two buffers should match.
   500  const auto len1 = fbb1.GetSize();
   501  const auto len2 = fbb2.GetSize();
   502  TEST_EQ(len1, len2);
   503  TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(), len1), 0);
   504
   505  // Test it with the original buffer test to make sure all data survived.
   506  AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false);
   507
   508  // Test accessing fields, similar to AccessFlatBufferTest above.
   509  CheckMonsterObject(monster2.get());
   510
   511  // Test object copy.
   512  MonsterT monster3 = *monster2;
   513  flatbuffers::FlatBufferBuilder fbb3;
   514  fbb3.Finish(CreateMonster(fbb3, &monster3, &rehasher), MonsterIdentifier());
   515  const auto len3 = fbb3.GetSize();
   516  TEST_EQ(len2, len3);
   517  TEST_EQ(memcmp(fbb2.GetBufferPointer(), fbb3.GetBufferPointer(), len2), 0);
   518  // Delete monster1 and monster2, then test accessing fields in monster3.
   519  monster1.reset();
   520  monster2.reset();
   521  CheckMonsterObject(&monster3);
   522}
   523
   524// Utility function to check a Monster object.
   525void CheckMonsterObject(MonsterT *monster2) {
   526  TEST_EQ(monster2->hp, 80);
   527  TEST_EQ(monster2->mana, 150);  // default
   528  TEST_EQ_STR(monster2->name.c_str(), "MyMonster");
   529
   530  auto &pos = monster2->pos;
   531  TEST_NOTNULL(pos);
   532  TEST_EQ(pos->z(), 3);
   533  TEST_EQ(pos->test3().a(), 10);
   534  TEST_EQ(pos->test3().b(), 20);
   535
   536  auto &inventory = monster2->inventory;
   537  TEST_EQ(inventory.size(), 10UL);
   538  unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
   539  for (auto it = inventory.begin(); it != inventory.end(); ++it)
   540    TEST_EQ(*it, inv_data[it - inventory.begin()]);
   541
   542  TEST_EQ(monster2->color, Color_Blue);
   543
   544  auto monster3 = monster2->test.AsMonster();
   545  TEST_NOTNULL(monster3);
   546  TEST_EQ_STR(monster3->name.c_str(), "Fred");
   547
   548  auto &vecofstrings = monster2->testarrayofstring;
   549  TEST_EQ(vecofstrings.size(), 4U);
   550  TEST_EQ_STR(vecofstrings[0].c_str(), "bob");
   551  TEST_EQ_STR(vecofstrings[1].c_str(), "fred");
   552
   553  auto &vecofstrings2 = monster2->testarrayofstring2;
   554  TEST_EQ(vecofstrings2.size(), 2U);
   555  TEST_EQ_STR(vecofstrings2[0].c_str(), "jane");
   556  TEST_EQ_STR(vecofstrings2[1].c_str(), "mary");
   557
   558  auto &vecoftables = monster2->testarrayoftables;
   559  TEST_EQ(vecoftables.size(), 3U);
   560  TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney");
   561  TEST_EQ(vecoftables[0]->hp, 1000);
   562  TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred");
   563  TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma");
   564
   565  auto &tests = monster2->test4;
   566  TEST_EQ(tests[0].a(), 10);
   567  TEST_EQ(tests[0].b(), 20);
   568  TEST_EQ(tests[1].a(), 30);
   569  TEST_EQ(tests[1].b(), 40);
   570}
   571
   572// Prefix a FlatBuffer with a size field.
   573void SizePrefixedTest() {
   574  // Create size prefixed buffer.
   575  flatbuffers::FlatBufferBuilder fbb;
   576  FinishSizePrefixedMonsterBuffer(
   577      fbb, CreateMonster(fbb, nullptr, 200, 300, fbb.CreateString("bob")));
   578
   579  // Verify it.
   580  flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
   581  TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier), true);
   582
   583  // The prefixed size doesn't include itself, so substract the size of the
   584  // prefix
   585  TEST_EQ(GetPrefixedSize(fbb.GetBufferPointer()),
   586          fbb.GetSize() - sizeof(uoffset_t));
   587
   588  // Getting the buffer length does include the prefix size, so it should be the
   589  // full lenght.
   590  TEST_EQ(GetSizePrefixedBufferLength(fbb.GetBufferPointer()), fbb.GetSize());
   591
   592  // Access it.
   593  auto m = GetSizePrefixedMonster(fbb.GetBufferPointer());
   594  TEST_EQ(m->mana(), 200);
   595  TEST_EQ(m->hp(), 300);
   596  TEST_EQ_STR(m->name()->c_str(), "bob");
   597
   598  {
   599    // Verify that passing a larger size is OK, but not a smaller
   600    flatbuffers::Verifier verifier_larger(fbb.GetBufferPointer(),
   601                                          fbb.GetSize() + 10);
   602    TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_larger), true);
   603
   604    flatbuffers::Verifier verifier_smaller(fbb.GetBufferPointer(),
   605                                           fbb.GetSize() - 10);
   606    TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_smaller), false);
   607  }
   608}
   609
   610void TestMonsterExtraFloats(const std::string &tests_data_path) {
   611#if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
   612  TEST_EQ(is_quiet_nan(1.0), false);
   613  TEST_EQ(is_quiet_nan(infinity_d), false);
   614  TEST_EQ(is_quiet_nan(-infinity_f), false);
   615  TEST_EQ(is_quiet_nan(std::numeric_limits<float>::quiet_NaN()), true);
   616  TEST_EQ(is_quiet_nan(std::numeric_limits<double>::quiet_NaN()), true);
   617
   618  using namespace flatbuffers;
   619  using namespace MyGame;
   620  // Load FlatBuffer schema (.fbs) from disk.
   621  std::string schemafile;
   622  TEST_EQ(LoadFile((tests_data_path + "monster_extra.fbs").c_str(), false,
   623                   &schemafile),
   624          true);
   625  // Parse schema first, so we can use it to parse the data after.
   626  Parser parser;
   627  auto include_test_path = ConCatPathFileName(tests_data_path, "include_test");
   628  const char *include_directories[] = { tests_data_path.c_str(),
   629                                        include_test_path.c_str(), nullptr };
   630  TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
   631  // Create empty extra and store to json.
   632  parser.opts.output_default_scalars_in_json = true;
   633  parser.opts.output_enum_identifiers = true;
   634  FlatBufferBuilder builder;
   635  const auto def_root = MonsterExtraBuilder(builder).Finish();
   636  FinishMonsterExtraBuffer(builder, def_root);
   637  const auto def_obj = builder.GetBufferPointer();
   638  const auto def_extra = GetMonsterExtra(def_obj);
   639  TEST_NOTNULL(def_extra);
   640  TEST_EQ(is_quiet_nan(def_extra->f0()), true);
   641  TEST_EQ(is_quiet_nan(def_extra->f1()), true);
   642  TEST_EQ(def_extra->f2(), +infinity_f);
   643  TEST_EQ(def_extra->f3(), -infinity_f);
   644  TEST_EQ(is_quiet_nan(def_extra->d0()), true);
   645  TEST_EQ(is_quiet_nan(def_extra->d1()), true);
   646  TEST_EQ(def_extra->d2(), +infinity_d);
   647  TEST_EQ(def_extra->d3(), -infinity_d);
   648  std::string jsongen;
   649  auto result = GenText(parser, def_obj, &jsongen);
   650  TEST_NULL(result);
   651  // Check expected default values.
   652  TEST_EQ(std::string::npos != jsongen.find("f0: nan"), true);
   653  TEST_EQ(std::string::npos != jsongen.find("f1: nan"), true);
   654  TEST_EQ(std::string::npos != jsongen.find("f2: inf"), true);
   655  TEST_EQ(std::string::npos != jsongen.find("f3: -inf"), true);
   656  TEST_EQ(std::string::npos != jsongen.find("d0: nan"), true);
   657  TEST_EQ(std::string::npos != jsongen.find("d1: nan"), true);
   658  TEST_EQ(std::string::npos != jsongen.find("d2: inf"), true);
   659  TEST_EQ(std::string::npos != jsongen.find("d3: -inf"), true);
   660  // Parse 'mosterdata_extra.json'.
   661  const auto extra_base = tests_data_path + "monsterdata_extra";
   662  jsongen = "";
   663  TEST_EQ(LoadFile((extra_base + ".json").c_str(), false, &jsongen), true);
   664  TEST_EQ(parser.Parse(jsongen.c_str()), true);
   665  const auto test_file = parser.builder_.GetBufferPointer();
   666  const auto test_size = parser.builder_.GetSize();
   667  Verifier verifier(test_file, test_size);
   668  TEST_ASSERT(VerifyMonsterExtraBuffer(verifier));
   669  const auto extra = GetMonsterExtra(test_file);
   670  TEST_NOTNULL(extra);
   671  TEST_EQ(is_quiet_nan(extra->f0()), true);
   672  TEST_EQ(is_quiet_nan(extra->f1()), true);
   673  TEST_EQ(extra->f2(), +infinity_f);
   674  TEST_EQ(extra->f3(), -infinity_f);
   675  TEST_EQ(is_quiet_nan(extra->d0()), true);
   676  TEST_EQ(extra->d1(), +infinity_d);
   677  TEST_EQ(extra->d2(), -infinity_d);
   678  TEST_EQ(is_quiet_nan(extra->d3()), true);
   679  TEST_NOTNULL(extra->fvec());
   680  TEST_EQ(extra->fvec()->size(), 4);
   681  TEST_EQ(extra->fvec()->Get(0), 1.0f);
   682  TEST_EQ(extra->fvec()->Get(1), -infinity_f);
   683  TEST_EQ(extra->fvec()->Get(2), +infinity_f);
   684  TEST_EQ(is_quiet_nan(extra->fvec()->Get(3)), true);
   685  TEST_NOTNULL(extra->dvec());
   686  TEST_EQ(extra->dvec()->size(), 4);
   687  TEST_EQ(extra->dvec()->Get(0), 2.0);
   688  TEST_EQ(extra->dvec()->Get(1), +infinity_d);
   689  TEST_EQ(extra->dvec()->Get(2), -infinity_d);
   690  TEST_EQ(is_quiet_nan(extra->dvec()->Get(3)), true);
   691#endif
   692}
   693
   694void EnumNamesTest() {
   695  TEST_EQ_STR("Red", EnumNameColor(Color_Red));
   696  TEST_EQ_STR("Green", EnumNameColor(Color_Green));
   697  TEST_EQ_STR("Blue", EnumNameColor(Color_Blue));
   698  // Check that Color to string don't crash while decode a mixture of Colors.
   699  // 1) Example::Color enum is enum with unfixed underlying type.
   700  // 2) Valid enum range: [0; 2^(ceil(log2(Color_ANY))) - 1].
   701  // Consequence: A value is out of this range will lead to UB (since C++17).
   702  // For details see C++17 standard or explanation on the SO:
   703  // stackoverflow.com/questions/18195312/what-happens-if-you-static-cast-invalid-value-to-enum-class
   704  TEST_EQ_STR("", EnumNameColor(static_cast<Color>(0)));
   705  TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY - 1)));
   706  TEST_EQ_STR("", EnumNameColor(static_cast<Color>(Color_ANY + 1)));
   707}
   708
   709void TypeAliasesTest() {
   710  flatbuffers::FlatBufferBuilder builder;
   711
   712  builder.Finish(CreateTypeAliases(
   713      builder, flatbuffers::numeric_limits<int8_t>::min(),
   714      flatbuffers::numeric_limits<uint8_t>::max(),
   715      flatbuffers::numeric_limits<int16_t>::min(),
   716      flatbuffers::numeric_limits<uint16_t>::max(),
   717      flatbuffers::numeric_limits<int32_t>::min(),
   718      flatbuffers::numeric_limits<uint32_t>::max(),
   719      flatbuffers::numeric_limits<int64_t>::min(),
   720      flatbuffers::numeric_limits<uint64_t>::max(), 2.3f, 2.3));
   721
   722  auto p = builder.GetBufferPointer();
   723  auto ta = flatbuffers::GetRoot<TypeAliases>(p);
   724
   725  TEST_EQ(ta->i8(), flatbuffers::numeric_limits<int8_t>::min());
   726  TEST_EQ(ta->u8(), flatbuffers::numeric_limits<uint8_t>::max());
   727  TEST_EQ(ta->i16(), flatbuffers::numeric_limits<int16_t>::min());
   728  TEST_EQ(ta->u16(), flatbuffers::numeric_limits<uint16_t>::max());
   729  TEST_EQ(ta->i32(), flatbuffers::numeric_limits<int32_t>::min());
   730  TEST_EQ(ta->u32(), flatbuffers::numeric_limits<uint32_t>::max());
   731  TEST_EQ(ta->i64(), flatbuffers::numeric_limits<int64_t>::min());
   732  TEST_EQ(ta->u64(), flatbuffers::numeric_limits<uint64_t>::max());
   733  TEST_EQ(ta->f32(), 2.3f);
   734  TEST_EQ(ta->f64(), 2.3);
   735  using namespace flatbuffers;  // is_same
   736  static_assert(is_same<decltype(ta->i8()), int8_t>::value, "invalid type");
   737  static_assert(is_same<decltype(ta->i16()), int16_t>::value, "invalid type");
   738  static_assert(is_same<decltype(ta->i32()), int32_t>::value, "invalid type");
   739  static_assert(is_same<decltype(ta->i64()), int64_t>::value, "invalid type");
   740  static_assert(is_same<decltype(ta->u8()), uint8_t>::value, "invalid type");
   741  static_assert(is_same<decltype(ta->u16()), uint16_t>::value, "invalid type");
   742  static_assert(is_same<decltype(ta->u32()), uint32_t>::value, "invalid type");
   743  static_assert(is_same<decltype(ta->u64()), uint64_t>::value, "invalid type");
   744  static_assert(is_same<decltype(ta->f32()), float>::value, "invalid type");
   745  static_assert(is_same<decltype(ta->f64()), double>::value, "invalid type");
   746}
   747
   748// example of parsing text straight into a buffer, and generating
   749// text back from it:
   750void ParseAndGenerateTextTest(const std::string &tests_data_path, bool binary) {
   751  // load FlatBuffer schema (.fbs) and JSON from disk
   752  std::string schemafile;
   753  std::string jsonfile;
   754  TEST_EQ(flatbuffers::LoadFile(
   755              (tests_data_path + "monster_test." + (binary ? "bfbs" : "fbs"))
   756                  .c_str(),
   757              binary, &schemafile),
   758          true);
   759  TEST_EQ(flatbuffers::LoadFile(
   760              (tests_data_path + "monsterdata_test.golden").c_str(), false,
   761              &jsonfile),
   762          true);
   763
   764  auto include_test_path =
   765      flatbuffers::ConCatPathFileName(tests_data_path, "include_test");
   766  const char *include_directories[] = { tests_data_path.c_str(),
   767                                        include_test_path.c_str(), nullptr };
   768
   769  // parse schema first, so we can use it to parse the data after
   770  flatbuffers::Parser parser;
   771  if (binary) {
   772    flatbuffers::Verifier verifier(
   773        reinterpret_cast<const uint8_t *>(schemafile.c_str()),
   774        schemafile.size());
   775    TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
   776    // auto schema = reflection::GetSchema(schemafile.c_str());
   777    TEST_EQ(parser.Deserialize(
   778                reinterpret_cast<const uint8_t *>(schemafile.c_str()),
   779                schemafile.size()),
   780            true);
   781  } else {
   782    TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true);
   783  }
   784  TEST_EQ(parser.ParseJson(jsonfile.c_str()), true);
   785
   786  // here, parser.builder_ contains a binary buffer that is the parsed data.
   787
   788  // First, verify it, just in case:
   789  flatbuffers::Verifier verifier(parser.builder_.GetBufferPointer(),
   790                                 parser.builder_.GetSize());
   791  TEST_EQ(VerifyMonsterBuffer(verifier), true);
   792
   793  AccessFlatBufferTest(parser.builder_.GetBufferPointer(),
   794                       parser.builder_.GetSize(), false);
   795
   796  // to ensure it is correct, we now generate text back from the binary,
   797  // and compare the two:
   798  std::string jsongen;
   799  auto result = GenText(parser, parser.builder_.GetBufferPointer(), &jsongen);
   800  TEST_NULL(result);
   801  TEST_EQ_STR(jsongen.c_str(), jsonfile.c_str());
   802
   803  // We can also do the above using the convenient Registry that knows about
   804  // a set of file_identifiers mapped to schemas.
   805  flatbuffers::Registry registry;
   806  // Make sure schemas can find their includes.
   807  registry.AddIncludeDirectory(tests_data_path.c_str());
   808  registry.AddIncludeDirectory(include_test_path.c_str());
   809  // Call this with many schemas if possible.
   810  registry.Register(MonsterIdentifier(),
   811                    (tests_data_path + "monster_test.fbs").c_str());
   812  // Now we got this set up, we can parse by just specifying the identifier,
   813  // the correct schema will be loaded on the fly:
   814  auto buf = registry.TextToFlatBuffer(jsonfile.c_str(), MonsterIdentifier());
   815  // If this fails, check registry.lasterror_.
   816  TEST_NOTNULL(buf.data());
   817  // Test the buffer, to be sure:
   818  AccessFlatBufferTest(buf.data(), buf.size(), false);
   819  // We can use the registry to turn this back into text, in this case it
   820  // will get the file_identifier from the binary:
   821  std::string text;
   822  auto ok = registry.FlatBufferToText(buf.data(), buf.size(), &text);
   823  // If this fails, check registry.lasterror_.
   824  TEST_EQ(ok, true);
   825  TEST_EQ_STR(text.c_str(), jsonfile.c_str());
   826
   827  // Generate text for UTF-8 strings without escapes.
   828  std::string jsonfile_utf8;
   829  TEST_EQ(flatbuffers::LoadFile((tests_data_path + "unicode_test.json").c_str(),
   830                                false, &jsonfile_utf8),
   831          true);
   832  TEST_EQ(parser.Parse(jsonfile_utf8.c_str(), include_directories), true);
   833  // To ensure it is correct, generate utf-8 text back from the binary.
   834  std::string jsongen_utf8;
   835  // request natural printing for utf-8 strings
   836  parser.opts.natural_utf8 = true;
   837  parser.opts.strict_json = true;
   838  TEST_NULL(GenText(parser, parser.builder_.GetBufferPointer(), &jsongen_utf8));
   839  TEST_EQ_STR(jsongen_utf8.c_str(), jsonfile_utf8.c_str());
   840}
   841
   842void UnPackTo(const uint8_t *flatbuf) {
   843  // Get a monster that has a name and no enemy
   844  auto orig_monster = GetMonster(flatbuf);
   845  TEST_EQ_STR(orig_monster->name()->c_str(), "MyMonster");
   846  TEST_ASSERT(orig_monster->enemy() == nullptr);
   847
   848  // Create an enemy
   849  MonsterT *enemy = new MonsterT();
   850  enemy->name = "Enemy";
   851
   852  // And create another monster owning the enemy,
   853  MonsterT mon;
   854  mon.name = "I'm monster 1";
   855  mon.enemy.reset(enemy);
   856  TEST_ASSERT(mon.enemy != nullptr);
   857
   858  // Assert that all the Monster objects are correct.
   859  TEST_EQ_STR(mon.name.c_str(), "I'm monster 1");
   860  TEST_EQ_STR(enemy->name.c_str(), "Enemy");
   861  TEST_EQ_STR(mon.enemy->name.c_str(), "Enemy");
   862
   863  // Now unpack monster ("MyMonster") into monster
   864  orig_monster->UnPackTo(&mon);
   865
   866  // Monster name should be from monster
   867  TEST_EQ_STR(mon.name.c_str(), "MyMonster");
   868
   869  // The monster shouldn't have any enemies, because monster didn't.
   870  TEST_ASSERT(mon.enemy == nullptr);
   871}
   872
   873}  // namespace tests
   874}  // namespace flatbuffers

View as plain text