1713 lines
60 KiB
C++
1713 lines
60 KiB
C++
/*
|
|
* Copyright 2014 Google Inc. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include <stdint.h>
|
|
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "alignment_test.h"
|
|
#include "evolution_test.h"
|
|
#include "flatbuffers/flatbuffers.h"
|
|
#include "flatbuffers/idl.h"
|
|
#include "flatbuffers/minireflect.h"
|
|
#include "flatbuffers/reflection_generated.h"
|
|
#include "flatbuffers/registry.h"
|
|
#include "flatbuffers/util.h"
|
|
#include "fuzz_test.h"
|
|
#include "json_test.h"
|
|
#include "key_field_test.h"
|
|
#include "monster_test.h"
|
|
#include "monster_test_generated.h"
|
|
#include "native_inline_table_test_generated.h"
|
|
#include "optional_scalars_test.h"
|
|
#include "parser_test.h"
|
|
#include "proto_test.h"
|
|
#include "reflection_test.h"
|
|
#include "union_vector/union_vector_generated.h"
|
|
#if !defined(_MSC_VER) || _MSC_VER >= 1700
|
|
# include "arrays_test_generated.h"
|
|
#endif
|
|
|
|
#include "flexbuffers_test.h"
|
|
#include "is_quiet_nan.h"
|
|
#include "monster_test_bfbs_generated.h" // Generated using --bfbs-comments --bfbs-builtins --cpp --bfbs-gen-embed
|
|
#include "native_type_test_generated.h"
|
|
#include "test_assert.h"
|
|
#include "util_test.h"
|
|
|
|
void FlatBufferBuilderTest();
|
|
|
|
namespace flatbuffers {
|
|
namespace tests {
|
|
namespace {
|
|
|
|
// clang-format off
|
|
// Check that char* and uint8_t* are interoperable types.
|
|
// The reinterpret_cast<> between the pointers are used to simplify data loading.
|
|
static_assert(flatbuffers::is_same<uint8_t, char>::value ||
|
|
flatbuffers::is_same<uint8_t, unsigned char>::value,
|
|
"unexpected uint8_t type");
|
|
|
|
#if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
|
|
// Ensure IEEE-754 support if tests of floats with NaN/Inf will run.
|
|
static_assert(std::numeric_limits<float>::is_iec559 &&
|
|
std::numeric_limits<double>::is_iec559,
|
|
"IEC-559 (IEEE-754) standard required");
|
|
#endif
|
|
// clang-format on
|
|
|
|
using namespace MyGame::Example;
|
|
|
|
void TriviallyCopyableTest() {
|
|
// clang-format off
|
|
#if __GNUG__ && __GNUC__ < 5 && \
|
|
!(defined(__clang__) && __clang_major__ >= 16)
|
|
TEST_EQ(__has_trivial_copy(Vec3), true);
|
|
#else
|
|
#if __cplusplus >= 201103L
|
|
TEST_EQ(std::is_trivially_copyable<Vec3>::value, true);
|
|
#endif
|
|
#endif
|
|
// clang-format on
|
|
}
|
|
|
|
// Guard against -Wunused-function on platforms without file tests.
|
|
#ifndef FLATBUFFERS_NO_FILE_TESTS
|
|
void GenerateTableTextTest(const std::string &tests_data_path) {
|
|
std::string schemafile;
|
|
std::string jsonfile;
|
|
bool ok =
|
|
flatbuffers::LoadFile((tests_data_path + "monster_test.fbs").c_str(),
|
|
false, &schemafile) &&
|
|
flatbuffers::LoadFile((tests_data_path + "monsterdata_test.json").c_str(),
|
|
false, &jsonfile);
|
|
TEST_EQ(ok, true);
|
|
auto include_test_path =
|
|
flatbuffers::ConCatPathFileName(tests_data_path, "include_test");
|
|
const char *include_directories[] = { tests_data_path.c_str(),
|
|
include_test_path.c_str(), nullptr };
|
|
flatbuffers::IDLOptions opt;
|
|
opt.indent_step = -1;
|
|
flatbuffers::Parser parser(opt);
|
|
ok = parser.Parse(schemafile.c_str(), include_directories) &&
|
|
parser.Parse(jsonfile.c_str(), include_directories);
|
|
TEST_EQ(ok, true);
|
|
// Test root table
|
|
const Monster *monster = GetMonster(parser.builder_.GetBufferPointer());
|
|
const auto abilities = monster->testarrayofsortedstruct();
|
|
TEST_EQ(abilities->size(), 3);
|
|
TEST_EQ(abilities->Get(0)->id(), 0);
|
|
TEST_EQ(abilities->Get(0)->distance(), 45);
|
|
TEST_EQ(abilities->Get(1)->id(), 1);
|
|
TEST_EQ(abilities->Get(1)->distance(), 21);
|
|
TEST_EQ(abilities->Get(2)->id(), 5);
|
|
TEST_EQ(abilities->Get(2)->distance(), 12);
|
|
|
|
std::string jsongen;
|
|
auto result = GenerateTextFromTable(parser, monster, "MyGame.Example.Monster",
|
|
&jsongen);
|
|
TEST_NULL(result);
|
|
// Test sub table
|
|
const Vec3 *pos = monster->pos();
|
|
jsongen.clear();
|
|
result = GenerateTextFromTable(parser, pos, "MyGame.Example.Vec3", &jsongen);
|
|
TEST_NULL(result);
|
|
TEST_EQ_STR(
|
|
jsongen.c_str(),
|
|
"{x: 1.0,y: 2.0,z: 3.0,test1: 3.0,test2: \"Green\",test3: {a: 5,b: 6}}");
|
|
const Test &test3 = pos->test3();
|
|
jsongen.clear();
|
|
result =
|
|
GenerateTextFromTable(parser, &test3, "MyGame.Example.Test", &jsongen);
|
|
TEST_NULL(result);
|
|
TEST_EQ_STR(jsongen.c_str(), "{a: 5,b: 6}");
|
|
const Test *test4 = monster->test4()->Get(0);
|
|
jsongen.clear();
|
|
result =
|
|
GenerateTextFromTable(parser, test4, "MyGame.Example.Test", &jsongen);
|
|
TEST_NULL(result);
|
|
TEST_EQ_STR(jsongen.c_str(), "{a: 10,b: 20}");
|
|
}
|
|
|
|
void MultiFileNameClashTest(const std::string &tests_data_path) {
|
|
const auto name_clash_path =
|
|
flatbuffers::ConCatPathFileName(tests_data_path, "name_clash_test");
|
|
const char *include_directories[] = { name_clash_path.c_str() };
|
|
|
|
// Load valid 2 file Flatbuffer schema
|
|
const auto valid_path =
|
|
flatbuffers::ConCatPathFileName(name_clash_path, "valid_test1.fbs");
|
|
std::string valid_schema;
|
|
TEST_ASSERT(flatbuffers::LoadFile(valid_path.c_str(), false, &valid_schema));
|
|
// Clashing table and union names in different namespaces must be parsable
|
|
TEST_ASSERT(
|
|
flatbuffers::Parser().Parse(valid_schema.c_str(), include_directories));
|
|
|
|
flatbuffers::Parser p;
|
|
TEST_ASSERT(p.Parse(valid_schema.c_str(), include_directories));
|
|
|
|
// Load invalid 2 file Flatbuffer schema
|
|
const auto invalid_path =
|
|
flatbuffers::ConCatPathFileName(name_clash_path, "invalid_test1.fbs");
|
|
std::string invalid_schema;
|
|
TEST_ASSERT(
|
|
flatbuffers::LoadFile(invalid_path.c_str(), false, &invalid_schema));
|
|
// Clashing table and union names in same namespace must fail to parse
|
|
TEST_EQ(
|
|
flatbuffers::Parser().Parse(invalid_schema.c_str(), include_directories),
|
|
false);
|
|
}
|
|
|
|
void InvalidNestedFlatbufferTest(const std::string &tests_data_path) {
|
|
// First, load and parse FlatBuffer schema (.fbs)
|
|
std::string schemafile;
|
|
TEST_EQ(flatbuffers::LoadFile((tests_data_path + "monster_test.fbs").c_str(),
|
|
false, &schemafile),
|
|
true);
|
|
auto include_test_path =
|
|
flatbuffers::ConCatPathFileName(tests_data_path, "include_test");
|
|
const char *include_directories[] = { tests_data_path.c_str(),
|
|
include_test_path.c_str(), nullptr };
|
|
flatbuffers::Parser parser1;
|
|
TEST_EQ(parser1.Parse(schemafile.c_str(), include_directories), true);
|
|
|
|
// "color" inside nested flatbuffer contains invalid enum value
|
|
TEST_EQ(parser1.Parse("{ name: \"Bender\", testnestedflatbuffer: { name: "
|
|
"\"Leela\", color: \"nonexistent\"}}"),
|
|
false);
|
|
}
|
|
|
|
void UnionVectorTest(const std::string &tests_data_path) {
|
|
// load FlatBuffer fbs schema and json.
|
|
std::string schemafile, jsonfile;
|
|
TEST_EQ(flatbuffers::LoadFile(
|
|
(tests_data_path + "union_vector/union_vector.fbs").c_str(),
|
|
false, &schemafile),
|
|
true);
|
|
TEST_EQ(flatbuffers::LoadFile(
|
|
(tests_data_path + "union_vector/union_vector.json").c_str(),
|
|
false, &jsonfile),
|
|
true);
|
|
|
|
// parse schema.
|
|
flatbuffers::IDLOptions idl_opts;
|
|
idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
|
|
flatbuffers::Parser parser(idl_opts);
|
|
TEST_EQ(parser.Parse(schemafile.c_str()), true);
|
|
|
|
flatbuffers::FlatBufferBuilder fbb;
|
|
|
|
// union types.
|
|
std::vector<uint8_t> types;
|
|
types.push_back(static_cast<uint8_t>(Character_Belle));
|
|
types.push_back(static_cast<uint8_t>(Character_MuLan));
|
|
types.push_back(static_cast<uint8_t>(Character_BookFan));
|
|
types.push_back(static_cast<uint8_t>(Character_Other));
|
|
types.push_back(static_cast<uint8_t>(Character_Unused));
|
|
|
|
// union values.
|
|
std::vector<flatbuffers::Offset<void>> characters;
|
|
characters.push_back(fbb.CreateStruct(BookReader(/*books_read=*/7)).Union());
|
|
characters.push_back(CreateAttacker(fbb, /*sword_attack_damage=*/5).Union());
|
|
characters.push_back(fbb.CreateStruct(BookReader(/*books_read=*/2)).Union());
|
|
characters.push_back(fbb.CreateString("Other").Union());
|
|
characters.push_back(fbb.CreateString("Unused").Union());
|
|
|
|
// create Movie.
|
|
const auto movie_offset =
|
|
CreateMovie(fbb, Character_Rapunzel,
|
|
fbb.CreateStruct(Rapunzel(/*hair_length=*/6)).Union(),
|
|
fbb.CreateVector(types), fbb.CreateVector(characters));
|
|
FinishMovieBuffer(fbb, movie_offset);
|
|
|
|
flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
|
|
TEST_EQ(VerifyMovieBuffer(verifier), true);
|
|
|
|
auto flat_movie = GetMovie(fbb.GetBufferPointer());
|
|
|
|
auto TestMovie = [](const Movie *movie) {
|
|
TEST_EQ(movie->main_character_type() == Character_Rapunzel, true);
|
|
|
|
auto cts = movie->characters_type();
|
|
TEST_EQ(movie->characters_type()->size(), 5);
|
|
TEST_EQ(cts->GetEnum<Character>(0) == Character_Belle, true);
|
|
TEST_EQ(cts->GetEnum<Character>(1) == Character_MuLan, true);
|
|
TEST_EQ(cts->GetEnum<Character>(2) == Character_BookFan, true);
|
|
TEST_EQ(cts->GetEnum<Character>(3) == Character_Other, true);
|
|
TEST_EQ(cts->GetEnum<Character>(4) == Character_Unused, true);
|
|
|
|
auto rapunzel = movie->main_character_as_Rapunzel();
|
|
TEST_NOTNULL(rapunzel);
|
|
TEST_EQ(rapunzel->hair_length(), 6);
|
|
|
|
auto cs = movie->characters();
|
|
TEST_EQ(cs->size(), 5);
|
|
auto belle = cs->GetAs<BookReader>(0);
|
|
TEST_EQ(belle->books_read(), 7);
|
|
auto mu_lan = cs->GetAs<Attacker>(1);
|
|
TEST_EQ(mu_lan->sword_attack_damage(), 5);
|
|
auto book_fan = cs->GetAs<BookReader>(2);
|
|
TEST_EQ(book_fan->books_read(), 2);
|
|
auto other = cs->GetAsString(3);
|
|
TEST_EQ_STR(other->c_str(), "Other");
|
|
auto unused = cs->GetAsString(4);
|
|
TEST_EQ_STR(unused->c_str(), "Unused");
|
|
};
|
|
|
|
TestMovie(flat_movie);
|
|
|
|
// Also test the JSON we loaded above.
|
|
TEST_EQ(parser.Parse(jsonfile.c_str()), true);
|
|
auto jbuf = parser.builder_.GetBufferPointer();
|
|
flatbuffers::Verifier jverifier(jbuf, parser.builder_.GetSize());
|
|
TEST_EQ(VerifyMovieBuffer(jverifier), true);
|
|
TestMovie(GetMovie(jbuf));
|
|
|
|
auto movie_object = flat_movie->UnPack();
|
|
TEST_EQ(movie_object->main_character.AsRapunzel()->hair_length(), 6);
|
|
TEST_EQ(movie_object->characters[0].AsBelle()->books_read(), 7);
|
|
TEST_EQ(movie_object->characters[1].AsMuLan()->sword_attack_damage, 5);
|
|
TEST_EQ(movie_object->characters[2].AsBookFan()->books_read(), 2);
|
|
TEST_EQ_STR(movie_object->characters[3].AsOther()->c_str(), "Other");
|
|
TEST_EQ_STR(movie_object->characters[4].AsUnused()->c_str(), "Unused");
|
|
|
|
fbb.Clear();
|
|
fbb.Finish(Movie::Pack(fbb, movie_object));
|
|
|
|
delete movie_object;
|
|
|
|
auto repacked_movie = GetMovie(fbb.GetBufferPointer());
|
|
|
|
TestMovie(repacked_movie);
|
|
|
|
// Generate text using mini-reflection.
|
|
auto s =
|
|
flatbuffers::FlatBufferToString(fbb.GetBufferPointer(), MovieTypeTable());
|
|
TEST_EQ_STR(
|
|
s.c_str(),
|
|
"{ main_character_type: Rapunzel, main_character: { hair_length: 6 }, "
|
|
"characters_type: [ Belle, MuLan, BookFan, Other, Unused ], "
|
|
"characters: [ { books_read: 7 }, { sword_attack_damage: 5 }, "
|
|
"{ books_read: 2 }, \"Other\", \"Unused\" ] }");
|
|
|
|
flatbuffers::ToStringVisitor visitor("\n", true, " ");
|
|
IterateFlatBuffer(fbb.GetBufferPointer(), MovieTypeTable(), &visitor);
|
|
TEST_EQ_STR(visitor.s.c_str(),
|
|
"{\n"
|
|
" \"main_character_type\": \"Rapunzel\",\n"
|
|
" \"main_character\": {\n"
|
|
" \"hair_length\": 6\n"
|
|
" },\n"
|
|
" \"characters_type\": [\n"
|
|
" \"Belle\",\n"
|
|
" \"MuLan\",\n"
|
|
" \"BookFan\",\n"
|
|
" \"Other\",\n"
|
|
" \"Unused\"\n"
|
|
" ],\n"
|
|
" \"characters\": [\n"
|
|
" {\n"
|
|
" \"books_read\": 7\n"
|
|
" },\n"
|
|
" {\n"
|
|
" \"sword_attack_damage\": 5\n"
|
|
" },\n"
|
|
" {\n"
|
|
" \"books_read\": 2\n"
|
|
" },\n"
|
|
" \"Other\",\n"
|
|
" \"Unused\"\n"
|
|
" ]\n"
|
|
"}");
|
|
|
|
// Generate text using parsed schema.
|
|
std::string jsongen;
|
|
auto result = GenerateText(parser, fbb.GetBufferPointer(), &jsongen);
|
|
TEST_NULL(result);
|
|
TEST_EQ_STR(jsongen.c_str(),
|
|
"{\n"
|
|
" main_character_type: \"Rapunzel\",\n"
|
|
" main_character: {\n"
|
|
" hair_length: 6\n"
|
|
" },\n"
|
|
" characters_type: [\n"
|
|
" \"Belle\",\n"
|
|
" \"MuLan\",\n"
|
|
" \"BookFan\",\n"
|
|
" \"Other\",\n"
|
|
" \"Unused\"\n"
|
|
" ],\n"
|
|
" characters: [\n"
|
|
" {\n"
|
|
" books_read: 7\n"
|
|
" },\n"
|
|
" {\n"
|
|
" sword_attack_damage: 5\n"
|
|
" },\n"
|
|
" {\n"
|
|
" books_read: 2\n"
|
|
" },\n"
|
|
" \"Other\",\n"
|
|
" \"Unused\"\n"
|
|
" ]\n"
|
|
"}\n");
|
|
|
|
// Simple test with reflection.
|
|
parser.Serialize();
|
|
auto schema = reflection::GetSchema(parser.builder_.GetBufferPointer());
|
|
auto ok = flatbuffers::Verify(*schema, *schema->root_table(),
|
|
fbb.GetBufferPointer(), fbb.GetSize());
|
|
TEST_EQ(ok, true);
|
|
|
|
flatbuffers::Parser parser2(idl_opts);
|
|
TEST_EQ(parser2.Parse("struct Bool { b:bool; }"
|
|
"union Any { Bool }"
|
|
"table Root { a:Any; }"
|
|
"root_type Root;"),
|
|
true);
|
|
TEST_EQ(parser2.Parse("{a_type:Bool,a:{b:true}}"), true);
|
|
}
|
|
#endif
|
|
|
|
void EndianSwapTest() {
|
|
TEST_EQ(flatbuffers::EndianSwap(static_cast<int16_t>(0x1234)), 0x3412);
|
|
TEST_EQ(flatbuffers::EndianSwap(static_cast<int32_t>(0x12345678)),
|
|
0x78563412);
|
|
TEST_EQ(flatbuffers::EndianSwap(static_cast<int64_t>(0x1234567890ABCDEF)),
|
|
0xEFCDAB9078563412);
|
|
TEST_EQ(flatbuffers::EndianSwap(flatbuffers::EndianSwap(3.14f)), 3.14f);
|
|
}
|
|
|
|
void UninitializedVectorTest() {
|
|
flatbuffers::FlatBufferBuilder builder;
|
|
|
|
Test *buf = nullptr;
|
|
auto vector_offset =
|
|
builder.CreateUninitializedVectorOfStructs<Test>(2, &buf);
|
|
TEST_NOTNULL(buf);
|
|
buf[0] = Test(10, 20);
|
|
buf[1] = Test(30, 40);
|
|
|
|
auto required_name = builder.CreateString("myMonster");
|
|
auto monster_builder = MonsterBuilder(builder);
|
|
monster_builder.add_name(
|
|
required_name); // required field mandated for monster.
|
|
monster_builder.add_test4(vector_offset);
|
|
builder.Finish(monster_builder.Finish());
|
|
|
|
auto p = builder.GetBufferPointer();
|
|
auto uvt = flatbuffers::GetRoot<Monster>(p);
|
|
TEST_NOTNULL(uvt);
|
|
auto vec = uvt->test4();
|
|
TEST_NOTNULL(vec);
|
|
auto test_0 = vec->Get(0);
|
|
auto test_1 = vec->Get(1);
|
|
TEST_EQ(test_0->a(), 10);
|
|
TEST_EQ(test_0->b(), 20);
|
|
TEST_EQ(test_1->a(), 30);
|
|
TEST_EQ(test_1->b(), 40);
|
|
}
|
|
|
|
void EqualOperatorTest() {
|
|
MonsterT a;
|
|
MonsterT b;
|
|
// We have to reset the fields that are NaN to zero to allow the equality
|
|
// to evaluate to true.
|
|
TEST_EQ(std::isnan(a.nan_default), true);
|
|
TEST_EQ(std::isnan(b.nan_default), true);
|
|
a.nan_default = 0;
|
|
b.nan_default = 0;
|
|
TEST_EQ(b == a, true);
|
|
TEST_EQ(b != a, false);
|
|
|
|
b.mana = 33;
|
|
TEST_EQ(b == a, false);
|
|
TEST_EQ(b != a, true);
|
|
b.mana = 150;
|
|
TEST_EQ(b == a, true);
|
|
TEST_EQ(b != a, false);
|
|
|
|
b.inventory.push_back(3);
|
|
TEST_EQ(b == a, false);
|
|
TEST_EQ(b != a, true);
|
|
b.inventory.clear();
|
|
TEST_EQ(b == a, true);
|
|
TEST_EQ(b != a, false);
|
|
|
|
a.enemy.reset(new MonsterT());
|
|
a.enemy->nan_default = 0;
|
|
TEST_EQ(b != a, true);
|
|
a.enemy->mana = 33;
|
|
TEST_EQ(b == a, false);
|
|
TEST_EQ(b != a, true);
|
|
|
|
b.enemy.reset(new MonsterT());
|
|
b.enemy->nan_default = 0;
|
|
TEST_EQ(b == a, false);
|
|
TEST_EQ(b != a, true);
|
|
b.enemy->mana = 33;
|
|
TEST_EQ(b == a, true);
|
|
TEST_EQ(b != a, false);
|
|
|
|
a.enemy.reset(nullptr);
|
|
TEST_EQ(b == a, false);
|
|
TEST_EQ(b != a, true);
|
|
b.enemy->mana = 150;
|
|
TEST_EQ(b == a, false);
|
|
TEST_EQ(b != a, true);
|
|
a.enemy.reset(new MonsterT());
|
|
a.enemy->nan_default = 0;
|
|
TEST_EQ(b == a, true);
|
|
TEST_EQ(b != a, false);
|
|
|
|
b.enemy.reset(nullptr);
|
|
|
|
b.test.type = Any_Monster;
|
|
TEST_EQ(b == a, false);
|
|
TEST_EQ(b != a, true);
|
|
|
|
// Test that vector of tables are compared by value and not by reference.
|
|
{
|
|
// Two tables are equal by default.
|
|
MonsterT a, b;
|
|
a.nan_default = 0;
|
|
b.nan_default = 0;
|
|
TEST_EQ(a == b, true);
|
|
|
|
// Adding only a table to one of the monster vectors should make it not
|
|
// equal (due to size mistmatch).
|
|
a.testarrayoftables.push_back(
|
|
flatbuffers::unique_ptr<MonsterT>(new MonsterT));
|
|
a.testarrayoftables.back()->nan_default = 0;
|
|
TEST_EQ(a == b, false);
|
|
|
|
// Adding an equalivant table to the other monster vector should make it
|
|
// equal again.
|
|
b.testarrayoftables.push_back(
|
|
flatbuffers::unique_ptr<MonsterT>(new MonsterT));
|
|
b.testarrayoftables.back()->nan_default = 0;
|
|
TEST_EQ(a == b, true);
|
|
|
|
// Create two new monsters that are different.
|
|
auto c = flatbuffers::unique_ptr<MonsterT>(new MonsterT);
|
|
auto d = flatbuffers::unique_ptr<MonsterT>(new MonsterT);
|
|
c->nan_default = 0;
|
|
d->nan_default = 0;
|
|
c->hp = 1;
|
|
d->hp = 2;
|
|
TEST_EQ(c == d, false);
|
|
|
|
// Adding them to the original monsters should also make them different.
|
|
a.testarrayoftables.push_back(std::move(c));
|
|
b.testarrayoftables.push_back(std::move(d));
|
|
TEST_EQ(a == b, false);
|
|
|
|
// Remove the mismatching monsters to get back to equality
|
|
a.testarrayoftables.pop_back();
|
|
b.testarrayoftables.pop_back();
|
|
TEST_EQ(a == b, true);
|
|
|
|
// Check that nullptr are OK.
|
|
a.testarrayoftables.push_back(nullptr);
|
|
b.testarrayoftables.push_back(
|
|
flatbuffers::unique_ptr<MonsterT>(new MonsterT));
|
|
TEST_EQ(a == b, false);
|
|
}
|
|
}
|
|
|
|
void CreateSharedStringTest() {
|
|
flatbuffers::FlatBufferBuilder builder;
|
|
const auto one1 = builder.CreateSharedString("one");
|
|
const auto two = builder.CreateSharedString("two");
|
|
const auto one2 = builder.CreateSharedString("one");
|
|
TEST_EQ(one1.o, one2.o);
|
|
const auto onetwo = builder.CreateSharedString("onetwo");
|
|
TEST_EQ(onetwo.o != one1.o, true);
|
|
TEST_EQ(onetwo.o != two.o, true);
|
|
|
|
// Support for embedded nulls
|
|
const char chars_b[] = { 'a', '\0', 'b' };
|
|
const char chars_c[] = { 'a', '\0', 'c' };
|
|
const auto null_b1 = builder.CreateSharedString(chars_b, sizeof(chars_b));
|
|
const auto null_c = builder.CreateSharedString(chars_c, sizeof(chars_c));
|
|
const auto null_b2 = builder.CreateSharedString(chars_b, sizeof(chars_b));
|
|
TEST_EQ(null_b1.o != null_c.o, true); // Issue#5058 repro
|
|
TEST_EQ(null_b1.o, null_b2.o);
|
|
|
|
// Put the strings into an array for round trip verification.
|
|
std::array<flatbuffers::Offset<flatbuffers::String>, 7> array = {
|
|
one1, two, one2, onetwo, null_b1, null_c, null_b2
|
|
};
|
|
const auto vector_offset =
|
|
builder.CreateVector<flatbuffers::Offset<flatbuffers::String>>(array);
|
|
MonsterBuilder monster_builder(builder);
|
|
monster_builder.add_name(two);
|
|
monster_builder.add_testarrayofstring(vector_offset);
|
|
builder.Finish(monster_builder.Finish());
|
|
|
|
// Read the Monster back.
|
|
const auto *monster =
|
|
flatbuffers::GetRoot<Monster>(builder.GetBufferPointer());
|
|
TEST_EQ_STR(monster->name()->c_str(), "two");
|
|
const auto *testarrayofstring = monster->testarrayofstring();
|
|
TEST_EQ(testarrayofstring->size(), flatbuffers::uoffset_t(7));
|
|
const auto &a = *testarrayofstring;
|
|
TEST_EQ_STR(a[0]->c_str(), "one");
|
|
TEST_EQ_STR(a[1]->c_str(), "two");
|
|
TEST_EQ_STR(a[2]->c_str(), "one");
|
|
TEST_EQ_STR(a[3]->c_str(), "onetwo");
|
|
TEST_EQ(a[4]->str(), (std::string(chars_b, sizeof(chars_b))));
|
|
TEST_EQ(a[5]->str(), (std::string(chars_c, sizeof(chars_c))));
|
|
TEST_EQ(a[6]->str(), (std::string(chars_b, sizeof(chars_b))));
|
|
|
|
// Make sure String::operator< works, too, since it is related to
|
|
// StringOffsetCompare.
|
|
TEST_EQ((*a[0]) < (*a[1]), true);
|
|
TEST_EQ((*a[1]) < (*a[0]), false);
|
|
TEST_EQ((*a[1]) < (*a[2]), false);
|
|
TEST_EQ((*a[2]) < (*a[1]), true);
|
|
TEST_EQ((*a[4]) < (*a[3]), true);
|
|
TEST_EQ((*a[5]) < (*a[4]), false);
|
|
TEST_EQ((*a[5]) < (*a[4]), false);
|
|
TEST_EQ((*a[6]) < (*a[5]), true);
|
|
}
|
|
|
|
#if !defined(FLATBUFFERS_USE_STD_SPAN) && !defined(FLATBUFFERS_SPAN_MINIMAL)
|
|
void FlatbuffersSpanTest() {
|
|
// Compile-time checking of non-const [] to const [] conversions.
|
|
using flatbuffers::internal::is_span_convertible;
|
|
(void)is_span_convertible<int, 1, int, 1>::type(123);
|
|
(void)is_span_convertible<const int, 1, int, 1>::type(123);
|
|
(void)is_span_convertible<const int64_t, 1, int64_t, 1>::type(123);
|
|
(void)is_span_convertible<const uint64_t, 1, uint64_t, 1>::type(123);
|
|
(void)is_span_convertible<const int, 1, const int, 1>::type(123);
|
|
(void)is_span_convertible<const int64_t, 1, const int64_t, 1>::type(123);
|
|
(void)is_span_convertible<const uint64_t, 1, const uint64_t, 1>::type(123);
|
|
|
|
using flatbuffers::span;
|
|
span<char, 0> c1;
|
|
TEST_EQ(c1.size(), 0);
|
|
span<char, flatbuffers::dynamic_extent> c2;
|
|
TEST_EQ(c2.size(), 0);
|
|
span<char> c3;
|
|
TEST_EQ(c3.size(), 0);
|
|
TEST_ASSERT(c1.empty() && c2.empty() && c3.empty());
|
|
|
|
int i_data7[7] = { 0, 1, 2, 3, 4, 5, 6 };
|
|
span<int, 7> i1(&i_data7[0], 7);
|
|
span<int> i2(i1); // make dynamic from static
|
|
TEST_EQ(i1.size(), 7);
|
|
TEST_EQ(i1.empty(), false);
|
|
TEST_EQ(i1.size(), i2.size());
|
|
TEST_EQ(i1.data(), i_data7);
|
|
TEST_EQ(i1[2], 2);
|
|
// Make const span from a non-const one.
|
|
span<const int, 7> i3(i1);
|
|
// Construct from a C-array.
|
|
span<int, 7> i4(i_data7);
|
|
span<const int, 7> i5(i_data7);
|
|
span<int> i6(i_data7);
|
|
span<const int> i7(i_data7);
|
|
TEST_EQ(i7.size(), 7);
|
|
// Check construction from a const array.
|
|
const int i_cdata5[5] = { 4, 3, 2, 1, 0 };
|
|
span<const int, 5> i8(i_cdata5);
|
|
span<const int> i9(i_cdata5);
|
|
TEST_EQ(i9.size(), 5);
|
|
// Construction from a (ptr, size) pair.
|
|
span<int, 7> i10(i_data7, 7);
|
|
span<int> i11(i_data7, 7);
|
|
TEST_EQ(i11.size(), 7);
|
|
span<const int, 5> i12(i_cdata5, 5);
|
|
span<const int> i13(i_cdata5, 5);
|
|
TEST_EQ(i13.size(), 5);
|
|
// Construction from std::array.
|
|
std::array<int, 6> i_arr6 = { { 0, 1, 2, 3, 4, 5 } };
|
|
span<int, 6> i14(i_arr6);
|
|
span<const int, 6> i15(i_arr6);
|
|
span<int> i16(i_arr6);
|
|
span<const int> i17(i_arr6);
|
|
TEST_EQ(i17.size(), 6);
|
|
const std::array<int, 8> i_carr8 = { { 0, 1, 2, 3, 4, 5, 6, 7 } };
|
|
span<const int, 8> i18(i_carr8);
|
|
span<const int> i19(i_carr8);
|
|
TEST_EQ(i18.size(), 8);
|
|
TEST_EQ(i19.size(), 8);
|
|
TEST_EQ(i19[7], 7);
|
|
// Check compatibility with flatbuffers::Array.
|
|
int fbs_int3_underlaying[3] = { 0 };
|
|
int fbs_int3_data[3] = { 1, 2, 3 };
|
|
auto &fbs_int3 = flatbuffers::CastToArray(fbs_int3_underlaying);
|
|
fbs_int3.CopyFromSpan(fbs_int3_data);
|
|
TEST_EQ(fbs_int3.Get(1), 2);
|
|
const int fbs_cint3_data[3] = { 2, 3, 4 };
|
|
fbs_int3.CopyFromSpan(fbs_cint3_data);
|
|
TEST_EQ(fbs_int3.Get(1), 3);
|
|
// Check with Array<Enum, N>
|
|
enum class Dummy : uint16_t { Zero = 0, One, Two };
|
|
Dummy fbs_dummy3_underlaying[3] = {};
|
|
Dummy fbs_dummy3_data[3] = { Dummy::One, Dummy::Two, Dummy::Two };
|
|
auto &fbs_dummy3 = flatbuffers::CastToArray(fbs_dummy3_underlaying);
|
|
fbs_dummy3.CopyFromSpan(fbs_dummy3_data);
|
|
TEST_EQ(fbs_dummy3.Get(1), Dummy::Two);
|
|
}
|
|
#else
|
|
void FlatbuffersSpanTest() {}
|
|
#endif
|
|
|
|
// VS10 does not support typed enums, exclude from tests
|
|
#if !defined(_MSC_VER) || _MSC_VER >= 1700
|
|
void FixedLengthArrayTest() {
|
|
// Generate an ArrayTable containing one ArrayStruct.
|
|
flatbuffers::FlatBufferBuilder fbb;
|
|
MyGame::Example::NestedStruct nStruct0(MyGame::Example::TestEnum::B);
|
|
TEST_NOTNULL(nStruct0.mutable_a());
|
|
nStruct0.mutable_a()->Mutate(0, 1);
|
|
nStruct0.mutable_a()->Mutate(1, 2);
|
|
TEST_NOTNULL(nStruct0.mutable_c());
|
|
nStruct0.mutable_c()->Mutate(0, MyGame::Example::TestEnum::C);
|
|
nStruct0.mutable_c()->Mutate(1, MyGame::Example::TestEnum::A);
|
|
TEST_NOTNULL(nStruct0.mutable_d());
|
|
nStruct0.mutable_d()->Mutate(0, flatbuffers::numeric_limits<int64_t>::max());
|
|
nStruct0.mutable_d()->Mutate(1, flatbuffers::numeric_limits<int64_t>::min());
|
|
MyGame::Example::NestedStruct nStruct1(MyGame::Example::TestEnum::C);
|
|
TEST_NOTNULL(nStruct1.mutable_a());
|
|
nStruct1.mutable_a()->Mutate(0, 3);
|
|
nStruct1.mutable_a()->Mutate(1, 4);
|
|
TEST_NOTNULL(nStruct1.mutable_c());
|
|
nStruct1.mutable_c()->Mutate(0, MyGame::Example::TestEnum::C);
|
|
nStruct1.mutable_c()->Mutate(1, MyGame::Example::TestEnum::A);
|
|
TEST_NOTNULL(nStruct1.mutable_d());
|
|
nStruct1.mutable_d()->Mutate(0, flatbuffers::numeric_limits<int64_t>::min());
|
|
nStruct1.mutable_d()->Mutate(1, flatbuffers::numeric_limits<int64_t>::max());
|
|
MyGame::Example::ArrayStruct aStruct(2, 12, 1);
|
|
TEST_NOTNULL(aStruct.b());
|
|
TEST_NOTNULL(aStruct.mutable_b());
|
|
TEST_NOTNULL(aStruct.mutable_d());
|
|
TEST_NOTNULL(aStruct.mutable_f());
|
|
for (int i = 0; i < aStruct.b()->size(); i++)
|
|
aStruct.mutable_b()->Mutate(i, i + 1);
|
|
aStruct.mutable_d()->Mutate(0, nStruct0);
|
|
aStruct.mutable_d()->Mutate(1, nStruct1);
|
|
auto aTable = MyGame::Example::CreateArrayTable(fbb, &aStruct);
|
|
MyGame::Example::FinishArrayTableBuffer(fbb, aTable);
|
|
// Verify correctness of the ArrayTable.
|
|
flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize());
|
|
TEST_ASSERT(MyGame::Example::VerifyArrayTableBuffer(verifier));
|
|
// Do test.
|
|
auto p = MyGame::Example::GetMutableArrayTable(fbb.GetBufferPointer());
|
|
auto mArStruct = p->mutable_a();
|
|
TEST_NOTNULL(mArStruct);
|
|
TEST_NOTNULL(mArStruct->b());
|
|
TEST_NOTNULL(mArStruct->d());
|
|
TEST_NOTNULL(mArStruct->f());
|
|
TEST_NOTNULL(mArStruct->mutable_b());
|
|
TEST_NOTNULL(mArStruct->mutable_d());
|
|
TEST_NOTNULL(mArStruct->mutable_f());
|
|
TEST_EQ(mArStruct->a(), 2);
|
|
TEST_EQ(mArStruct->b()->size(), 15);
|
|
mArStruct->mutable_b()->Mutate(14, -14);
|
|
TEST_EQ(mArStruct->b()->Get(14), -14);
|
|
TEST_EQ(mArStruct->c(), 12);
|
|
TEST_NOTNULL(mArStruct->d()->Get(0));
|
|
TEST_NOTNULL(mArStruct->d()->Get(0)->a());
|
|
TEST_EQ(mArStruct->d()->Get(0)->a()->Get(0), 1);
|
|
TEST_EQ(mArStruct->d()->Get(0)->a()->Get(1), 2);
|
|
TEST_NOTNULL(mArStruct->d()->Get(1));
|
|
TEST_NOTNULL(mArStruct->d()->Get(1)->a());
|
|
TEST_EQ(mArStruct->d()->Get(1)->a()->Get(0), 3);
|
|
TEST_EQ(mArStruct->d()->Get(1)->a()->Get(1), 4);
|
|
TEST_NOTNULL(mArStruct->mutable_d()->GetMutablePointer(1));
|
|
TEST_NOTNULL(mArStruct->mutable_d()->GetMutablePointer(1)->mutable_a());
|
|
mArStruct->mutable_d()->GetMutablePointer(1)->mutable_a()->Mutate(1, 5);
|
|
TEST_EQ(5, mArStruct->d()->Get(1)->a()->Get(1));
|
|
TEST_EQ(MyGame::Example::TestEnum::B, mArStruct->d()->Get(0)->b());
|
|
TEST_NOTNULL(mArStruct->d()->Get(0)->c());
|
|
TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(0)->c()->Get(0));
|
|
TEST_EQ(MyGame::Example::TestEnum::A, mArStruct->d()->Get(0)->c()->Get(1));
|
|
TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(),
|
|
mArStruct->d()->Get(0)->d()->Get(0));
|
|
TEST_EQ(flatbuffers::numeric_limits<int64_t>::min(),
|
|
mArStruct->d()->Get(0)->d()->Get(1));
|
|
TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(1)->b());
|
|
TEST_NOTNULL(mArStruct->d()->Get(1)->c());
|
|
TEST_EQ(MyGame::Example::TestEnum::C, mArStruct->d()->Get(1)->c()->Get(0));
|
|
TEST_EQ(MyGame::Example::TestEnum::A, mArStruct->d()->Get(1)->c()->Get(1));
|
|
TEST_EQ(flatbuffers::numeric_limits<int64_t>::min(),
|
|
mArStruct->d()->Get(1)->d()->Get(0));
|
|
TEST_EQ(flatbuffers::numeric_limits<int64_t>::max(),
|
|
mArStruct->d()->Get(1)->d()->Get(1));
|
|
for (int i = 0; i < mArStruct->b()->size() - 1; i++)
|
|
TEST_EQ(mArStruct->b()->Get(i), i + 1);
|
|
// Check alignment
|
|
TEST_EQ(0, reinterpret_cast<uintptr_t>(mArStruct->d()) % 8);
|
|
TEST_EQ(0, reinterpret_cast<uintptr_t>(mArStruct->f()) % 8);
|
|
|
|
// Check if default constructor set all memory zero
|
|
const size_t arr_size = sizeof(MyGame::Example::ArrayStruct);
|
|
char non_zero_memory[arr_size];
|
|
// set memory chunk of size ArrayStruct to 1's
|
|
std::memset(static_cast<void *>(non_zero_memory), 1, arr_size);
|
|
// after placement-new it should be all 0's
|
|
# if defined(_MSC_VER) && defined(_DEBUG)
|
|
# undef new
|
|
# endif
|
|
MyGame::Example::ArrayStruct *ap =
|
|
new (non_zero_memory) MyGame::Example::ArrayStruct;
|
|
# if defined(_MSC_VER) && defined(_DEBUG)
|
|
# define new DEBUG_NEW
|
|
# endif
|
|
(void)ap;
|
|
for (size_t i = 0; i < arr_size; ++i) { TEST_EQ(non_zero_memory[i], 0); }
|
|
}
|
|
#else
|
|
void FixedLengthArrayTest() {}
|
|
#endif // !defined(_MSC_VER) || _MSC_VER >= 1700
|
|
|
|
#if !defined(FLATBUFFERS_SPAN_MINIMAL) && \
|
|
(!defined(_MSC_VER) || _MSC_VER >= 1700)
|
|
void FixedLengthArrayConstructorTest() {
|
|
const int32_t nested_a[2] = { 1, 2 };
|
|
MyGame::Example::TestEnum nested_c[2] = { MyGame::Example::TestEnum::A,
|
|
MyGame::Example::TestEnum::B };
|
|
const int64_t int64_2[2] = { -2, -1 };
|
|
|
|
std::array<MyGame::Example::NestedStruct, 2> init_d = {
|
|
{ MyGame::Example::NestedStruct(nested_a, MyGame::Example::TestEnum::B,
|
|
nested_c, int64_2),
|
|
MyGame::Example::NestedStruct(nested_a, MyGame::Example::TestEnum::A,
|
|
nested_c,
|
|
std::array<int64_t, 2>{ { 12, 13 } }) }
|
|
};
|
|
|
|
MyGame::Example::ArrayStruct arr_struct(
|
|
8.125,
|
|
std::array<int32_t, 0xF>{
|
|
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } },
|
|
-17, init_d, 10, int64_2);
|
|
TEST_EQ(arr_struct.a(), 8.125);
|
|
TEST_EQ(arr_struct.b()->Get(2), 3);
|
|
TEST_EQ(arr_struct.c(), -17);
|
|
|
|
TEST_NOTNULL(arr_struct.d());
|
|
const auto &arr_d_0 = *arr_struct.d()->Get(0);
|
|
TEST_EQ(arr_d_0.a()->Get(0), 1);
|
|
TEST_EQ(arr_d_0.a()->Get(1), 2);
|
|
TEST_EQ(arr_d_0.b(), MyGame::Example::TestEnum::B);
|
|
TEST_EQ(arr_d_0.c()->Get(0), MyGame::Example::TestEnum::A);
|
|
TEST_EQ(arr_d_0.c()->Get(1), MyGame::Example::TestEnum::B);
|
|
TEST_EQ(arr_d_0.d()->Get(0), -2);
|
|
TEST_EQ(arr_d_0.d()->Get(1), -1);
|
|
const auto &arr_d_1 = *arr_struct.d()->Get(1);
|
|
TEST_EQ(arr_d_1.a()->Get(0), 1);
|
|
TEST_EQ(arr_d_1.a()->Get(1), 2);
|
|
TEST_EQ(arr_d_1.b(), MyGame::Example::TestEnum::A);
|
|
TEST_EQ(arr_d_1.c()->Get(0), MyGame::Example::TestEnum::A);
|
|
TEST_EQ(arr_d_1.c()->Get(1), MyGame::Example::TestEnum::B);
|
|
TEST_EQ(arr_d_1.d()->Get(0), 12);
|
|
TEST_EQ(arr_d_1.d()->Get(1), 13);
|
|
|
|
TEST_EQ(arr_struct.e(), 10);
|
|
TEST_EQ(arr_struct.f()->Get(0), -2);
|
|
TEST_EQ(arr_struct.f()->Get(1), -1);
|
|
}
|
|
#else
|
|
void FixedLengthArrayConstructorTest() {}
|
|
#endif
|
|
|
|
void FixedLengthArrayOperatorEqualTest() {
|
|
const int32_t nested_a[2] = { 1, 2 };
|
|
MyGame::Example::TestEnum nested_c[2] = { MyGame::Example::TestEnum::A,
|
|
MyGame::Example::TestEnum::B };
|
|
|
|
MyGame::Example::TestEnum nested_cc[2] = { MyGame::Example::TestEnum::A,
|
|
MyGame::Example::TestEnum::C };
|
|
const int64_t int64_2[2] = { -2, -1 };
|
|
|
|
std::array<MyGame::Example::NestedStruct, 2> init_d = {
|
|
{ MyGame::Example::NestedStruct(nested_a, MyGame::Example::TestEnum::B,
|
|
nested_c, int64_2),
|
|
MyGame::Example::NestedStruct(nested_a, MyGame::Example::TestEnum::B,
|
|
nested_c,
|
|
std::array<int64_t, 2>{ { -2, -1 } }) }
|
|
};
|
|
|
|
auto different = MyGame::Example::NestedStruct(
|
|
nested_a, MyGame::Example::TestEnum::B, nested_cc,
|
|
std::array<int64_t, 2>{ { -2, -1 } });
|
|
|
|
TEST_ASSERT(init_d[0] == init_d[1]);
|
|
TEST_ASSERT(init_d[0] != different);
|
|
|
|
std::array<MyGame::Example::ArrayStruct, 3> arr_struct = {
|
|
MyGame::Example::ArrayStruct(
|
|
8.125,
|
|
std::array<int32_t, 0xF>{
|
|
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } },
|
|
-17, init_d, 10, int64_2),
|
|
|
|
MyGame::Example::ArrayStruct(
|
|
8.125,
|
|
std::array<int32_t, 0xF>{
|
|
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } },
|
|
-17, init_d, 10, int64_2),
|
|
|
|
MyGame::Example::ArrayStruct(
|
|
8.125,
|
|
std::array<int32_t, 0xF>{
|
|
{ 1000, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } },
|
|
-17, init_d, 10, int64_2)
|
|
};
|
|
|
|
TEST_ASSERT(arr_struct[0] == arr_struct[1]);
|
|
TEST_ASSERT(arr_struct[1] != arr_struct[2]);
|
|
}
|
|
|
|
void NativeTypeTest() {
|
|
const int N = 3;
|
|
|
|
Geometry::ApplicationDataT src_data;
|
|
src_data.vectors.reserve(N);
|
|
src_data.vectors_alt.reserve(N);
|
|
|
|
for (int i = 0; i < N; ++i) {
|
|
src_data.vectors.push_back(
|
|
Native::Vector3D(10 * i + 0.1f, 10 * i + 0.2f, 10 * i + 0.3f));
|
|
src_data.vectors_alt.push_back(
|
|
Native::Vector3D(20 * i + 0.1f, 20 * i + 0.2f, 20 * i + 0.3f));
|
|
}
|
|
|
|
flatbuffers::FlatBufferBuilder fbb;
|
|
fbb.Finish(Geometry::ApplicationData::Pack(fbb, &src_data));
|
|
|
|
auto dstDataT = Geometry::UnPackApplicationData(fbb.GetBufferPointer());
|
|
|
|
for (int i = 0; i < N; ++i) {
|
|
const Native::Vector3D &v = dstDataT->vectors[i];
|
|
TEST_EQ(v.x, 10 * i + 0.1f);
|
|
TEST_EQ(v.y, 10 * i + 0.2f);
|
|
TEST_EQ(v.z, 10 * i + 0.3f);
|
|
|
|
const Native::Vector3D &v2 = dstDataT->vectors_alt[i];
|
|
TEST_EQ(v2.x, 20 * i + 0.1f);
|
|
TEST_EQ(v2.y, 20 * i + 0.2f);
|
|
TEST_EQ(v2.z, 20 * i + 0.3f);
|
|
}
|
|
}
|
|
|
|
// Guard against -Wunused-function on platforms without file tests.
|
|
#ifndef FLATBUFFERS_NO_FILE_TESTS
|
|
// VS10 does not support typed enums, exclude from tests
|
|
# if !defined(_MSC_VER) || _MSC_VER >= 1700
|
|
void FixedLengthArrayJsonTest(const std::string &tests_data_path, bool binary) {
|
|
// load FlatBuffer schema (.fbs) and JSON from disk
|
|
std::string schemafile;
|
|
std::string jsonfile;
|
|
TEST_EQ(flatbuffers::LoadFile(
|
|
(tests_data_path + "arrays_test." + (binary ? "bfbs" : "fbs"))
|
|
.c_str(),
|
|
binary, &schemafile),
|
|
true);
|
|
TEST_EQ(
|
|
flatbuffers::LoadFile((tests_data_path + "arrays_test.golden").c_str(),
|
|
false, &jsonfile),
|
|
true);
|
|
|
|
// parse schema first, so we can use it to parse the data after
|
|
flatbuffers::Parser parserOrg, parserGen;
|
|
if (binary) {
|
|
flatbuffers::Verifier verifier(
|
|
reinterpret_cast<const uint8_t *>(schemafile.c_str()),
|
|
schemafile.size());
|
|
TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
|
|
TEST_EQ(parserOrg.Deserialize(
|
|
reinterpret_cast<const uint8_t *>(schemafile.c_str()),
|
|
schemafile.size()),
|
|
true);
|
|
TEST_EQ(parserGen.Deserialize(
|
|
reinterpret_cast<const uint8_t *>(schemafile.c_str()),
|
|
schemafile.size()),
|
|
true);
|
|
} else {
|
|
TEST_EQ(parserOrg.Parse(schemafile.c_str()), true);
|
|
TEST_EQ(parserGen.Parse(schemafile.c_str()), true);
|
|
}
|
|
TEST_EQ(parserOrg.Parse(jsonfile.c_str()), true);
|
|
|
|
// First, verify it, just in case:
|
|
flatbuffers::Verifier verifierOrg(parserOrg.builder_.GetBufferPointer(),
|
|
parserOrg.builder_.GetSize());
|
|
TEST_EQ(VerifyArrayTableBuffer(verifierOrg), true);
|
|
|
|
// Export to JSON
|
|
std::string jsonGen;
|
|
TEST_NULL(
|
|
GenerateText(parserOrg, parserOrg.builder_.GetBufferPointer(), &jsonGen));
|
|
|
|
// Import from JSON
|
|
TEST_EQ(parserGen.Parse(jsonGen.c_str()), true);
|
|
|
|
// Verify buffer from generated JSON
|
|
flatbuffers::Verifier verifierGen(parserGen.builder_.GetBufferPointer(),
|
|
parserGen.builder_.GetSize());
|
|
TEST_EQ(VerifyArrayTableBuffer(verifierGen), true);
|
|
|
|
// Compare generated buffer to original
|
|
TEST_EQ(parserOrg.builder_.GetSize(), parserGen.builder_.GetSize());
|
|
TEST_EQ(std::memcmp(parserOrg.builder_.GetBufferPointer(),
|
|
parserGen.builder_.GetBufferPointer(),
|
|
parserOrg.builder_.GetSize()),
|
|
0);
|
|
}
|
|
|
|
void FixedLengthArraySpanTest(const std::string &tests_data_path) {
|
|
// load FlatBuffer schema (.fbs) and JSON from disk
|
|
std::string schemafile;
|
|
std::string jsonfile;
|
|
TEST_EQ(flatbuffers::LoadFile((tests_data_path + "arrays_test.fbs").c_str(),
|
|
false, &schemafile),
|
|
true);
|
|
TEST_EQ(
|
|
flatbuffers::LoadFile((tests_data_path + "arrays_test.golden").c_str(),
|
|
false, &jsonfile),
|
|
true);
|
|
|
|
// parse schema first, so we can use it to parse the data after
|
|
flatbuffers::Parser parser;
|
|
TEST_EQ(parser.Parse(schemafile.c_str()), true);
|
|
TEST_EQ(parser.Parse(jsonfile.c_str()), true);
|
|
auto &fbb = parser.builder_;
|
|
auto verifier = flatbuffers::Verifier(fbb.GetBufferPointer(), fbb.GetSize());
|
|
TEST_EQ(true, VerifyArrayTableBuffer(verifier));
|
|
|
|
auto p = MyGame::Example::GetMutableArrayTable(fbb.GetBufferPointer());
|
|
TEST_NOTNULL(p);
|
|
auto table_struct = p->mutable_a();
|
|
TEST_NOTNULL(table_struct);
|
|
TEST_EQ(2, table_struct->d()->size());
|
|
TEST_NOTNULL(table_struct->d());
|
|
TEST_NOTNULL(table_struct->mutable_d());
|
|
// test array of structs
|
|
auto const_d = flatbuffers::make_span(*table_struct->d());
|
|
auto mutable_d = flatbuffers::make_span(*table_struct->mutable_d());
|
|
TEST_EQ(2, const_d.size());
|
|
TEST_EQ(2, mutable_d.size());
|
|
TEST_ASSERT(const_d[0] == mutable_d[0]);
|
|
TEST_ASSERT(const_d[1] == mutable_d[1]);
|
|
mutable_d[0] = const_d[0]; // mutate
|
|
// test scalars
|
|
auto &const_nested = const_d[0];
|
|
auto &mutable_nested = mutable_d[0];
|
|
static_assert(sizeof(MyGame::Example::TestEnum) == sizeof(uint8_t),
|
|
"TestEnum's underlaying type must by byte");
|
|
TEST_NOTNULL(const_nested.d());
|
|
TEST_NOTNULL(mutable_nested.d());
|
|
{
|
|
flatbuffers::span<const MyGame::Example::TestEnum, 2> const_d_c =
|
|
flatbuffers::make_span(*const_nested.c());
|
|
auto mutable_d_c = flatbuffers::make_span(*mutable_nested.mutable_c());
|
|
TEST_EQ(2, const_d_c.size());
|
|
TEST_EQ(2, mutable_d_c.size());
|
|
TEST_EQ(MyGame::Example::TestEnum::C, const_d_c[0]);
|
|
TEST_EQ(MyGame::Example::TestEnum::B, const_d_c[1]);
|
|
TEST_ASSERT(mutable_d_c.end() == std::copy(const_d_c.begin(),
|
|
const_d_c.end(),
|
|
mutable_d_c.begin()));
|
|
TEST_ASSERT(
|
|
std::equal(const_d_c.begin(), const_d_c.end(), mutable_d_c.begin()));
|
|
}
|
|
// test little endian array of int32
|
|
# if FLATBUFFERS_LITTLEENDIAN
|
|
{
|
|
flatbuffers::span<const int32_t, 2> const_d_a =
|
|
flatbuffers::make_span(*const_nested.a());
|
|
auto mutable_d_a = flatbuffers::make_span(*mutable_nested.mutable_a());
|
|
TEST_EQ(2, const_d_a.size());
|
|
TEST_EQ(2, mutable_d_a.size());
|
|
TEST_EQ(-1, const_d_a[0]);
|
|
TEST_EQ(2, const_d_a[1]);
|
|
TEST_ASSERT(mutable_d_a.end() == std::copy(const_d_a.begin(),
|
|
const_d_a.end(),
|
|
mutable_d_a.begin()));
|
|
TEST_ASSERT(
|
|
std::equal(const_d_a.begin(), const_d_a.end(), mutable_d_a.begin()));
|
|
}
|
|
# endif
|
|
}
|
|
# else
|
|
void FixedLengthArrayJsonTest(bool /*binary*/) {}
|
|
void FixedLengthArraySpanTest() {}
|
|
# endif
|
|
|
|
void TestEmbeddedBinarySchema(const std::string &tests_data_path) {
|
|
// load JSON from disk
|
|
std::string jsonfile;
|
|
TEST_EQ(flatbuffers::LoadFile(
|
|
(tests_data_path + "monsterdata_test.golden").c_str(), false,
|
|
&jsonfile),
|
|
true);
|
|
|
|
// parse schema first, so we can use it to parse the data after
|
|
flatbuffers::Parser parserOrg, parserGen;
|
|
flatbuffers::Verifier verifier(MyGame::Example::MonsterBinarySchema::data(),
|
|
MyGame::Example::MonsterBinarySchema::size());
|
|
TEST_EQ(reflection::VerifySchemaBuffer(verifier), true);
|
|
TEST_EQ(parserOrg.Deserialize(MyGame::Example::MonsterBinarySchema::data(),
|
|
MyGame::Example::MonsterBinarySchema::size()),
|
|
true);
|
|
TEST_EQ(parserGen.Deserialize(MyGame::Example::MonsterBinarySchema::data(),
|
|
MyGame::Example::MonsterBinarySchema::size()),
|
|
true);
|
|
TEST_EQ(parserOrg.Parse(jsonfile.c_str()), true);
|
|
|
|
// First, verify it, just in case:
|
|
flatbuffers::Verifier verifierOrg(parserOrg.builder_.GetBufferPointer(),
|
|
parserOrg.builder_.GetSize());
|
|
TEST_EQ(VerifyMonsterBuffer(verifierOrg), true);
|
|
|
|
// Export to JSON
|
|
std::string jsonGen;
|
|
TEST_NULL(
|
|
GenerateText(parserOrg, parserOrg.builder_.GetBufferPointer(), &jsonGen));
|
|
|
|
// Import from JSON
|
|
TEST_EQ(parserGen.Parse(jsonGen.c_str()), true);
|
|
|
|
// Verify buffer from generated JSON
|
|
flatbuffers::Verifier verifierGen(parserGen.builder_.GetBufferPointer(),
|
|
parserGen.builder_.GetSize());
|
|
TEST_EQ(VerifyMonsterBuffer(verifierGen), true);
|
|
|
|
// Compare generated buffer to original
|
|
TEST_EQ(parserOrg.builder_.GetSize(), parserGen.builder_.GetSize());
|
|
TEST_EQ(std::memcmp(parserOrg.builder_.GetBufferPointer(),
|
|
parserGen.builder_.GetBufferPointer(),
|
|
parserOrg.builder_.GetSize()),
|
|
0);
|
|
}
|
|
#endif
|
|
|
|
template<typename T> void EmbeddedSchemaAccessByType() {
|
|
// Get the binary schema from the Type itself.
|
|
// Verify the schema is OK.
|
|
flatbuffers::Verifier verifierEmbeddedSchema(
|
|
T::TableType::BinarySchema::data(), T::TableType::BinarySchema::size());
|
|
TEST_EQ(reflection::VerifySchemaBuffer(verifierEmbeddedSchema), true);
|
|
|
|
// Reflect it.
|
|
auto schema = reflection::GetSchema(T::TableType::BinarySchema::data());
|
|
|
|
// This should equal the expected root table.
|
|
TEST_EQ_STR(schema->root_table()->name()->c_str(), "MyGame.Example.Monster");
|
|
}
|
|
|
|
void EmbeddedSchemaAccess() {
|
|
// Get the binary schema for the monster.
|
|
// Verify the schema is OK.
|
|
flatbuffers::Verifier verifierEmbeddedSchema(Monster::BinarySchema::data(),
|
|
Monster::BinarySchema::size());
|
|
TEST_EQ(reflection::VerifySchemaBuffer(verifierEmbeddedSchema), true);
|
|
|
|
// Reflect it.
|
|
auto schema = reflection::GetSchema(Monster::BinarySchema::data());
|
|
|
|
// This should equal the expected root table.
|
|
TEST_EQ_STR(schema->root_table()->name()->c_str(), "MyGame.Example.Monster");
|
|
|
|
// Repeat above, but do so through a template parameter:
|
|
EmbeddedSchemaAccessByType<StatT>();
|
|
}
|
|
|
|
void NestedVerifierTest() {
|
|
// Create a nested monster.
|
|
flatbuffers::FlatBufferBuilder nested_builder;
|
|
FinishMonsterBuffer(
|
|
nested_builder,
|
|
CreateMonster(nested_builder, nullptr, 0, 0,
|
|
nested_builder.CreateString("NestedMonster")));
|
|
|
|
// Verify the nested monster
|
|
flatbuffers::Verifier verifier(nested_builder.GetBufferPointer(),
|
|
nested_builder.GetSize());
|
|
TEST_EQ(true, VerifyMonsterBuffer(verifier));
|
|
|
|
{
|
|
// Create the outer monster.
|
|
flatbuffers::FlatBufferBuilder builder;
|
|
|
|
// Add the nested monster as a vector of bytes.
|
|
auto nested_monster_bytes = builder.CreateVector(
|
|
nested_builder.GetBufferPointer(), nested_builder.GetSize());
|
|
|
|
auto name = builder.CreateString("OuterMonster");
|
|
|
|
MonsterBuilder mon_builder(builder);
|
|
mon_builder.add_name(name);
|
|
mon_builder.add_testnestedflatbuffer(nested_monster_bytes);
|
|
FinishMonsterBuffer(builder, mon_builder.Finish());
|
|
|
|
// Verify the root monster, which includes verifing the nested monster
|
|
flatbuffers::Verifier verifier(builder.GetBufferPointer(),
|
|
builder.GetSize());
|
|
TEST_EQ(true, VerifyMonsterBuffer(verifier));
|
|
}
|
|
|
|
{
|
|
// Create the outer monster.
|
|
flatbuffers::FlatBufferBuilder builder;
|
|
|
|
// Purposely invalidate the nested flatbuffer setting its length to 1, an
|
|
// invalid length.
|
|
uint8_t invalid_nested_buffer[1];
|
|
auto nested_monster_bytes = builder.CreateVector(invalid_nested_buffer, 1);
|
|
|
|
auto name = builder.CreateString("OuterMonster");
|
|
|
|
MonsterBuilder mon_builder(builder);
|
|
mon_builder.add_name(name);
|
|
mon_builder.add_testnestedflatbuffer(nested_monster_bytes);
|
|
FinishMonsterBuffer(builder, mon_builder.Finish());
|
|
|
|
// Verify the root monster fails, since the included nested monster fails.
|
|
flatbuffers::Verifier verifier(builder.GetBufferPointer(),
|
|
builder.GetSize());
|
|
TEST_EQ(false, VerifyMonsterBuffer(verifier));
|
|
|
|
// Verify the root monster succeeds, since we've disabled checking nested
|
|
// flatbuffers
|
|
flatbuffers::Verifier::Options options;
|
|
options.check_nested_flatbuffers = false;
|
|
flatbuffers::Verifier no_check_nested(builder.GetBufferPointer(),
|
|
builder.GetSize(), options);
|
|
TEST_EQ(true, VerifyMonsterBuffer(no_check_nested));
|
|
}
|
|
|
|
{
|
|
// Create the outer monster.
|
|
flatbuffers::FlatBufferBuilder builder;
|
|
|
|
// Purposely invalidate the nested flatbuffer setting its length to 0, an
|
|
// invalid length.
|
|
uint8_t *invalid_nested_buffer = nullptr;
|
|
auto nested_monster_bytes = builder.CreateVector(invalid_nested_buffer, 0);
|
|
|
|
auto name = builder.CreateString("OuterMonster");
|
|
|
|
MonsterBuilder mon_builder(builder);
|
|
mon_builder.add_name(name);
|
|
mon_builder.add_testnestedflatbuffer(nested_monster_bytes);
|
|
FinishMonsterBuffer(builder, mon_builder.Finish());
|
|
|
|
// Verify the root monster fails, since the included nested monster fails.
|
|
flatbuffers::Verifier verifier(builder.GetBufferPointer(),
|
|
builder.GetSize());
|
|
TEST_EQ(false, VerifyMonsterBuffer(verifier));
|
|
}
|
|
}
|
|
|
|
template<class T, class Container>
|
|
void TestIterators(const std::vector<T> &expected, const Container &tested) {
|
|
TEST_ASSERT(tested.rbegin().base() == tested.end());
|
|
TEST_ASSERT(tested.crbegin().base() == tested.cend());
|
|
TEST_ASSERT(tested.rend().base() == tested.begin());
|
|
TEST_ASSERT(tested.crend().base() == tested.cbegin());
|
|
|
|
size_t k = 0;
|
|
for (auto it = tested.begin(); it != tested.end(); ++it, ++k) {
|
|
const auto &e = expected.at(k);
|
|
TEST_EQ(*it, e);
|
|
}
|
|
TEST_EQ(k, expected.size());
|
|
|
|
k = expected.size();
|
|
for (auto it = tested.rbegin(); it != tested.rend(); ++it, --k) {
|
|
const auto &e = expected.at(k - 1);
|
|
TEST_EQ(*it, e);
|
|
}
|
|
TEST_EQ(k, 0);
|
|
}
|
|
|
|
void FlatbuffersIteratorsTest() {
|
|
{
|
|
flatbuffers::FlatBufferBuilder fbb;
|
|
const std::vector<unsigned char> inv_data = { 1, 2, 3 };
|
|
{
|
|
auto mon_name = fbb.CreateString("MyMonster"); // key, mandatory
|
|
auto inv_vec = fbb.CreateVector(inv_data);
|
|
auto empty_i64_vec =
|
|
fbb.CreateVector(static_cast<const int64_t *>(nullptr), 0);
|
|
MonsterBuilder mb(fbb);
|
|
mb.add_name(mon_name);
|
|
mb.add_inventory(inv_vec);
|
|
mb.add_vector_of_longs(empty_i64_vec);
|
|
FinishMonsterBuffer(fbb, mb.Finish());
|
|
}
|
|
const auto &mon = *flatbuffers::GetRoot<Monster>(fbb.GetBufferPointer());
|
|
|
|
TEST_EQ_STR("MyMonster", mon.name()->c_str());
|
|
TEST_ASSERT(mon.inventory());
|
|
TEST_ASSERT(mon.vector_of_longs());
|
|
TestIterators(inv_data, *mon.inventory());
|
|
TestIterators(std::vector<int64_t>(), *mon.vector_of_longs());
|
|
}
|
|
|
|
{
|
|
flatbuffers::FlatBufferBuilder fbb;
|
|
MyGame::Example::ArrayStruct aStruct;
|
|
MyGame::Example::FinishArrayTableBuffer(
|
|
fbb, MyGame::Example::CreateArrayTable(fbb, &aStruct));
|
|
const auto &array_table =
|
|
*flatbuffers::GetRoot<ArrayTable>(fbb.GetBufferPointer());
|
|
TEST_ASSERT(array_table.a());
|
|
auto &int_15 = *array_table.a()->b();
|
|
TestIterators(std::vector<int>(15, 0), int_15);
|
|
}
|
|
}
|
|
|
|
void PrivateAnnotationsLeaks() {
|
|
// Simple schemas and a "has optional scalar" sentinal.
|
|
std::vector<std::string> schemas;
|
|
std::vector<std::string> failure_schemas;
|
|
|
|
// (private) (table/struct)
|
|
schemas.push_back(
|
|
"table Monster (private) { mana: int; }"
|
|
"struct ABC (private) { mana: int; }");
|
|
|
|
// (public) (table/struct)
|
|
schemas.push_back(
|
|
"table Monster { mana: int; }"
|
|
"struct ABC { mana: int; }");
|
|
|
|
// (private) (union) containing (private) (table/struct)
|
|
schemas.push_back(
|
|
"table Monster (private) { mana: int; } "
|
|
"struct ABC (private) { mana: int; } "
|
|
"union Any (private) { Monster, ABC } ");
|
|
|
|
// (public) (union) containing (public) (table/struct)
|
|
schemas.push_back(
|
|
"table Monster { mana: int; }"
|
|
"struct ABC { mana: int; }"
|
|
"union Any { Monster, ABC }");
|
|
|
|
// (private) (table/struct/enum)
|
|
schemas.push_back(
|
|
"table Monster (private) { mana: int; }"
|
|
"struct ABC (private) { mana: int; }"
|
|
"enum Race:byte (private) { None = -1, Human = 0, }");
|
|
|
|
// (public) (table/struct/enum)
|
|
schemas.push_back(
|
|
"table Monster { mana: int; }"
|
|
"struct ABC { mana: int; }"
|
|
"enum Race:byte { None = -1, Human = 0, }");
|
|
|
|
// (private) (union) containing (private) (table/struct)
|
|
schemas.push_back(
|
|
"table Monster (private) { mana: int; }"
|
|
"struct ABC (private) { mana: int; }"
|
|
"enum Race:byte (private) { None = -1, Human = 0, }"
|
|
"union Any (private) { Monster, ABC }");
|
|
|
|
// (public) (union) containing (public) (table/struct)
|
|
schemas.push_back(
|
|
"table Monster { mana: int; }"
|
|
"struct ABC { mana: int; }"
|
|
"enum Race:byte { None = -1, Human = 0, }"
|
|
"union Any { Monster, ABC }");
|
|
|
|
// (private) (table), (public struct)
|
|
schemas.push_back(
|
|
"table Monster (private) { mana: int; }"
|
|
"struct ABC { mana: int; }");
|
|
|
|
// (private) (table), (public) (struct/enum)
|
|
schemas.push_back(
|
|
"table Monster (private) { mana: int; }"
|
|
"struct ABC { mana: int; }"
|
|
"enum Race:byte { None = -1, Human = 0, }");
|
|
|
|
// (public) (struct) containing (public) (enum)
|
|
schemas.push_back(
|
|
"enum Race:byte { None = -1, Human = 0, }"
|
|
"table Monster { mana: int; }"
|
|
"struct ABC { mana: int; type: Race; }");
|
|
|
|
// (public) (union) containing (private) (table) & (public) (struct)
|
|
failure_schemas.push_back(
|
|
"table Monster (private) { mana: int; }"
|
|
"struct ABC { mana: int; }"
|
|
"union Any { Monster, ABC }");
|
|
|
|
// (public) (union) containing (private) (table/struct)
|
|
failure_schemas.push_back(
|
|
"table Monster (private) { mana: int; }"
|
|
"struct ABC (private) { mana: int; }"
|
|
"enum Race:byte { None = -1, Human = 0, }"
|
|
"union Any { Monster, ABC }");
|
|
|
|
// (public) (table) containing (private) (struct)
|
|
failure_schemas.push_back(
|
|
"table Monster { mana: int; ab: ABC; }"
|
|
"struct ABC (private) { mana: int; }");
|
|
|
|
// (public) (struct) containing (private) (enum)
|
|
failure_schemas.push_back(
|
|
"enum Race:byte (private) { None = -1, Human = 0, }"
|
|
"table Monster { mana: int; }"
|
|
"struct ABC { mana: int; type: Race; }");
|
|
|
|
flatbuffers::IDLOptions opts;
|
|
opts.lang_to_generate = flatbuffers::IDLOptions::Language::kSwift;
|
|
opts.no_leak_private_annotations = true;
|
|
|
|
for (auto schema = schemas.begin(); schema < schemas.end(); schema++) {
|
|
flatbuffers::Parser parser(opts);
|
|
TEST_ASSERT(parser.Parse(schema->c_str()));
|
|
}
|
|
|
|
for (auto schema = failure_schemas.begin(); schema < failure_schemas.end();
|
|
schema++) {
|
|
flatbuffers::Parser parser(opts);
|
|
TEST_EQ(false, parser.Parse(schema->c_str()));
|
|
}
|
|
|
|
opts.no_leak_private_annotations = false;
|
|
|
|
for (auto schema = schemas.begin(); schema < schemas.end(); schema++) {
|
|
flatbuffers::Parser parser(opts);
|
|
TEST_ASSERT(parser.Parse(schema->c_str()));
|
|
}
|
|
|
|
for (auto schema = failure_schemas.begin(); schema < failure_schemas.end();
|
|
schema++) {
|
|
flatbuffers::Parser parser(opts);
|
|
TEST_ASSERT(parser.Parse(schema->c_str()));
|
|
}
|
|
}
|
|
|
|
void VectorSpanTest() {
|
|
flatbuffers::FlatBufferBuilder builder;
|
|
|
|
auto mloc = CreateMonster(
|
|
builder, nullptr, 0, 0, builder.CreateString("Monster"),
|
|
builder.CreateVector<uint8_t>({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }));
|
|
|
|
FinishMonsterBuffer(builder, mloc);
|
|
|
|
auto monster = GetMonster(builder.GetBufferPointer());
|
|
auto mutable_monster = GetMutableMonster(builder.GetBufferPointer());
|
|
|
|
{ // using references
|
|
TEST_NOTNULL(monster->inventory());
|
|
|
|
flatbuffers::span<const uint8_t> const_inventory =
|
|
flatbuffers::make_span(*monster->inventory());
|
|
TEST_EQ(const_inventory.size(), 10);
|
|
TEST_EQ(const_inventory[0], 0);
|
|
TEST_EQ(const_inventory[9], 9);
|
|
|
|
flatbuffers::span<uint8_t> mutable_inventory =
|
|
flatbuffers::make_span(*mutable_monster->mutable_inventory());
|
|
TEST_EQ(mutable_inventory.size(), 10);
|
|
TEST_EQ(mutable_inventory[0], 0);
|
|
TEST_EQ(mutable_inventory[9], 9);
|
|
|
|
mutable_inventory[0] = 42;
|
|
TEST_EQ(mutable_inventory[0], 42);
|
|
|
|
mutable_inventory[0] = 0;
|
|
TEST_EQ(mutable_inventory[0], 0);
|
|
}
|
|
|
|
{ // using pointers
|
|
TEST_EQ(flatbuffers::VectorLength(monster->inventory()), 10);
|
|
|
|
flatbuffers::span<const uint8_t> const_inventory =
|
|
flatbuffers::make_span(monster->inventory());
|
|
TEST_EQ(const_inventory.size(), 10);
|
|
TEST_EQ(const_inventory[0], 0);
|
|
TEST_EQ(const_inventory[9], 9);
|
|
|
|
flatbuffers::span<uint8_t> mutable_inventory =
|
|
flatbuffers::make_span(mutable_monster->mutable_inventory());
|
|
TEST_EQ(mutable_inventory.size(), 10);
|
|
TEST_EQ(mutable_inventory[0], 0);
|
|
TEST_EQ(mutable_inventory[9], 9);
|
|
|
|
mutable_inventory[0] = 42;
|
|
TEST_EQ(mutable_inventory[0], 42);
|
|
|
|
mutable_inventory[0] = 0;
|
|
TEST_EQ(mutable_inventory[0], 0);
|
|
}
|
|
|
|
{
|
|
TEST_ASSERT(nullptr == monster->testnestedflatbuffer());
|
|
|
|
TEST_EQ(flatbuffers::VectorLength(monster->testnestedflatbuffer()), 0);
|
|
|
|
flatbuffers::span<const uint8_t> const_nested =
|
|
flatbuffers::make_span(monster->testnestedflatbuffer());
|
|
TEST_ASSERT(const_nested.empty());
|
|
|
|
flatbuffers::span<uint8_t> mutable_nested =
|
|
flatbuffers::make_span(mutable_monster->mutable_testnestedflatbuffer());
|
|
TEST_ASSERT(mutable_nested.empty());
|
|
}
|
|
}
|
|
|
|
void NativeInlineTableVectorTest() {
|
|
TestNativeInlineTableT test;
|
|
for (int i = 0; i < 10; ++i) {
|
|
NativeInlineTableT t;
|
|
t.a = i;
|
|
test.t.push_back(t);
|
|
}
|
|
|
|
flatbuffers::FlatBufferBuilder fbb;
|
|
auto offset = TestNativeInlineTable::Pack(fbb, &test);
|
|
fbb.Finish(offset);
|
|
|
|
auto *root =
|
|
flatbuffers::GetRoot<TestNativeInlineTable>(fbb.GetBufferPointer());
|
|
TestNativeInlineTableT unpacked;
|
|
root->UnPackTo(&unpacked);
|
|
|
|
for (int i = 0; i < 10; ++i) { TEST_ASSERT(unpacked.t[i] == test.t[i]); }
|
|
|
|
TEST_ASSERT(unpacked.t == test.t);
|
|
}
|
|
|
|
// Guard against -Wunused-function on platforms without file tests.
|
|
#ifndef FLATBUFFERS_NO_FILE_TESTS
|
|
void DoNotRequireEofTest(const std::string &tests_data_path) {
|
|
std::string schemafile;
|
|
bool ok = flatbuffers::LoadFile(
|
|
(tests_data_path + "monster_test.fbs").c_str(), false, &schemafile);
|
|
TEST_EQ(ok, true);
|
|
auto include_test_path =
|
|
flatbuffers::ConCatPathFileName(tests_data_path, "include_test");
|
|
const char *include_directories[] = { tests_data_path.c_str(),
|
|
include_test_path.c_str(), nullptr };
|
|
flatbuffers::IDLOptions opt;
|
|
opt.require_json_eof = false;
|
|
flatbuffers::Parser parser(opt);
|
|
ok = parser.Parse(schemafile.c_str(), include_directories);
|
|
TEST_EQ(ok, true);
|
|
|
|
const char *str = R"(Some text at the beginning. {
|
|
"name": "Blob",
|
|
"hp": 5
|
|
}{
|
|
"name": "Imp",
|
|
"hp": 10
|
|
}
|
|
Some extra text at the end too.
|
|
)";
|
|
const char *tableStart = std::strchr(str, '{');
|
|
ok = parser.ParseJson(tableStart);
|
|
TEST_EQ(ok, true);
|
|
|
|
const Monster *monster = GetMonster(parser.builder_.GetBufferPointer());
|
|
TEST_EQ_STR(monster->name()->c_str(), "Blob");
|
|
TEST_EQ(monster->hp(), 5);
|
|
|
|
tableStart += parser.BytesConsumed();
|
|
|
|
ok = parser.ParseJson(tableStart);
|
|
TEST_EQ(ok, true);
|
|
|
|
monster = GetMonster(parser.builder_.GetBufferPointer());
|
|
TEST_EQ_STR(monster->name()->c_str(), "Imp");
|
|
TEST_EQ(monster->hp(), 10);
|
|
}
|
|
#endif
|
|
|
|
int FlatBufferTests(const std::string &tests_data_path) {
|
|
// Run our various test suites:
|
|
|
|
std::string rawbuf;
|
|
auto flatbuf1 = CreateFlatBufferTest(rawbuf);
|
|
auto flatbuf = std::move(flatbuf1); // Test move assignment.
|
|
|
|
AccessFlatBufferTest(reinterpret_cast<const uint8_t *>(rawbuf.c_str()),
|
|
rawbuf.length());
|
|
AccessFlatBufferTest(flatbuf.data(), flatbuf.size());
|
|
|
|
MutateFlatBuffersTest(flatbuf.data(), flatbuf.size());
|
|
|
|
ObjectFlatBuffersTest(flatbuf.data());
|
|
UnPackTo(flatbuf.data());
|
|
|
|
MiniReflectFlatBuffersTest(flatbuf.data());
|
|
MiniReflectFixedLengthArrayTest();
|
|
|
|
SizePrefixedTest();
|
|
|
|
AlignmentTest();
|
|
|
|
#ifndef FLATBUFFERS_NO_FILE_TESTS
|
|
ParseAndGenerateTextTest(tests_data_path, false);
|
|
ParseAndGenerateTextTest(tests_data_path, true);
|
|
FixedLengthArrayJsonTest(tests_data_path, false);
|
|
FixedLengthArrayJsonTest(tests_data_path, true);
|
|
ReflectionTest(tests_data_path, flatbuf.data(), flatbuf.size());
|
|
ParseProtoTest(tests_data_path);
|
|
EvolutionTest(tests_data_path);
|
|
UnionDeprecationTest(tests_data_path);
|
|
UnionVectorTest(tests_data_path);
|
|
GenerateTableTextTest(tests_data_path);
|
|
TestEmbeddedBinarySchema(tests_data_path);
|
|
JsonOptionalTest(tests_data_path, false);
|
|
JsonOptionalTest(tests_data_path, true);
|
|
MultiFileNameClashTest(tests_data_path);
|
|
InvalidNestedFlatbufferTest(tests_data_path);
|
|
JsonDefaultTest(tests_data_path);
|
|
JsonEnumsTest(tests_data_path);
|
|
TestMonsterExtraFloats(tests_data_path);
|
|
ParseIncorrectMonsterJsonTest(tests_data_path);
|
|
FixedLengthArraySpanTest(tests_data_path);
|
|
DoNotRequireEofTest(tests_data_path);
|
|
JsonUnionStructTest();
|
|
#else
|
|
// Guard against -Wunused-parameter.
|
|
(void)tests_data_path;
|
|
#endif
|
|
|
|
UtilConvertCase();
|
|
|
|
FuzzTest1();
|
|
FuzzTest2();
|
|
|
|
TriviallyCopyableTest();
|
|
ErrorTest();
|
|
ValueTest();
|
|
EnumValueTest();
|
|
NestedListTest();
|
|
EnumStringsTest();
|
|
EnumNamesTest();
|
|
EnumOutOfRangeTest();
|
|
IntegerOutOfRangeTest();
|
|
IntegerBoundaryTest();
|
|
UnicodeTest();
|
|
UnicodeTestAllowNonUTF8();
|
|
UnicodeTestGenerateTextFailsOnNonUTF8();
|
|
UnicodeSurrogatesTest();
|
|
UnicodeInvalidSurrogatesTest();
|
|
InvalidUTF8Test();
|
|
UnknownFieldsTest();
|
|
ParseUnionTest();
|
|
ValidSameNameDifferentNamespaceTest();
|
|
ConformTest();
|
|
ParseProtoBufAsciiTest();
|
|
TypeAliasesTest();
|
|
EndianSwapTest();
|
|
CreateSharedStringTest();
|
|
FlexBuffersTest();
|
|
FlexBuffersReuseBugTest();
|
|
FlexBuffersDeprecatedTest();
|
|
UninitializedVectorTest();
|
|
EqualOperatorTest();
|
|
NumericUtilsTest();
|
|
IsAsciiUtilsTest();
|
|
ValidFloatTest();
|
|
InvalidFloatTest();
|
|
FixedLengthArrayTest();
|
|
NativeTypeTest();
|
|
OptionalScalarsTest();
|
|
ParseFlexbuffersFromJsonWithNullTest();
|
|
FlatbuffersSpanTest();
|
|
FixedLengthArrayConstructorTest();
|
|
FixedLengthArrayOperatorEqualTest();
|
|
FieldIdentifierTest();
|
|
StringVectorDefaultsTest();
|
|
FlexBuffersFloatingPointTest();
|
|
FlatbuffersIteratorsTest();
|
|
WarningsAsErrorsTest();
|
|
NestedVerifierTest();
|
|
PrivateAnnotationsLeaks();
|
|
JsonUnsortedArrayTest();
|
|
VectorSpanTest();
|
|
NativeInlineTableVectorTest();
|
|
FixedSizedScalarKeyInStructTest();
|
|
StructKeyInStructTest();
|
|
NestedStructKeyInStructTest();
|
|
FixedSizedStructArrayKeyInStructTest();
|
|
EmbeddedSchemaAccess();
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
} // namespace tests
|
|
} // namespace flatbuffers
|
|
|
|
int main(int argc, const char *argv[]) {
|
|
std::string tests_data_path =
|
|
#ifdef BAZEL_TEST_DATA_PATH
|
|
"../com_github_google_flatbuffers/tests/";
|
|
#else
|
|
"tests/";
|
|
#endif
|
|
|
|
for (int argi = 1; argi < argc; argi++) {
|
|
std::string arg = argv[argi];
|
|
if (arg == "--test_path") {
|
|
if (++argi >= argc) {
|
|
fprintf(stderr, "error: missing path following: %s\n", arg.c_str());
|
|
exit(1);
|
|
}
|
|
// Override default path if provided one.
|
|
tests_data_path = argv[argi];
|
|
|
|
} else {
|
|
fprintf(stderr, "error: Unknown argument: %s\n", arg.c_str());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
InitTestEngine();
|
|
|
|
std::string req_locale;
|
|
if (flatbuffers::ReadEnvironmentVariable("FLATBUFFERS_TEST_LOCALE",
|
|
&req_locale)) {
|
|
TEST_OUTPUT_LINE("The environment variable FLATBUFFERS_TEST_LOCALE=%s",
|
|
req_locale.c_str());
|
|
req_locale = flatbuffers::RemoveStringQuotes(req_locale);
|
|
std::string the_locale;
|
|
TEST_ASSERT_FUNC(
|
|
flatbuffers::SetGlobalTestLocale(req_locale.c_str(), &the_locale));
|
|
TEST_OUTPUT_LINE("The global C-locale changed: %s", the_locale.c_str());
|
|
}
|
|
|
|
#ifdef FLATBUFFERS_TEST_PATH_PREFIX
|
|
tests_data_path =
|
|
FLATBUFFERS_STRING(FLATBUFFERS_TEST_PATH_PREFIX) + tests_data_path;
|
|
#endif
|
|
|
|
flatbuffers::tests::FlatBufferTests(tests_data_path);
|
|
FlatBufferBuilderTest();
|
|
|
|
if (!testing_fails) {
|
|
TEST_OUTPUT_LINE("ALL TESTS PASSED");
|
|
} else {
|
|
TEST_OUTPUT_LINE("%d FAILED TESTS", testing_fails);
|
|
}
|
|
return CloseTestEngine();
|
|
}
|