X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=tests%2Ftest.cpp;h=ffd293dd95ef97555e807fe218b9614abce47c88;hb=0e453ac3524100e7d78481d75e44ad3515dde0c1;hp=80c18768e7ae8166244a2cce0f3cb32089c87e3d;hpb=13c05f4da39054d6a3918614554f7f2a83770fcd;p=platform%2Fupstream%2Fflatbuffers.git diff --git a/tests/test.cpp b/tests/test.cpp index 80c1876..ffd293d 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -23,7 +23,6 @@ // clang-format off #ifdef FLATBUFFERS_CPP98_STL - #include "flatbuffers/stl_emulation.h" namespace std { using flatbuffers::unique_ptr; } @@ -34,8 +33,9 @@ #include "namespace_test/namespace_test1_generated.h" #include "namespace_test/namespace_test2_generated.h" #include "union_vector/union_vector_generated.h" -#include "monster_extra_generated.h" +#include "optional_scalars_generated.h" #if !defined(_MSC_VER) || _MSC_VER >= 1700 +# include "monster_extra_generated.h" # include "arrays_test_generated.h" # include "evolution_test/evolution_v1_generated.h" # include "evolution_test/evolution_v2_generated.h" @@ -45,6 +45,7 @@ #include "test_assert.h" #include "flatbuffers/flexbuffers.h" +#include "monster_test_bfbs_generated.h" // Generated using --bfbs-comments --bfbs-builtins --cpp --bfbs-gen-embed // clang-format off // Check that char* and uint8_t* are interoperable types. @@ -62,8 +63,8 @@ static_assert(flatbuffers::is_same::value || // clang-format on // Shortcuts for the infinity. -static const auto infinityf = std::numeric_limits::infinity(); -static const auto infinityd = std::numeric_limits::infinity(); +static const auto infinity_f = std::numeric_limits::infinity(); +static const auto infinity_d = std::numeric_limits::infinity(); using namespace MyGame::Example; @@ -165,9 +166,18 @@ flatbuffers::DetachedBuffer CreateFlatBufferTest(std::string &buffer) { abilities.push_back(Ability(4, 40)); abilities.push_back(Ability(3, 30)); abilities.push_back(Ability(2, 20)); - abilities.push_back(Ability(1, 10)); + abilities.push_back(Ability(0, 0)); auto vecofstructs = builder.CreateVectorOfSortedStructs(&abilities); + flatbuffers::Offset mlocs_stats[1]; + auto miss = builder.CreateString("miss"); + StatBuilder mb_miss(builder); + mb_miss.add_id(miss); + mb_miss.add_val(0); + mb_miss.add_count(0); // key + mlocs_stats[0] = mb_miss.Finish(); + auto vec_of_stats = builder.CreateVectorOfSortedTables(mlocs_stats, 1); + // Create a nested FlatBuffer. // Nested FlatBuffers are stored in a ubyte vector, which can be convenient // since they can be memcpy'd around much easier than other FlatBuffer @@ -194,7 +204,6 @@ flatbuffers::DetachedBuffer CreateFlatBufferTest(std::string &buffer) { flexbuild.Int(1234); flexbuild.Finish(); auto flex = builder.CreateVector(flexbuild.GetBuffer()); - // Test vector of enums. Color colors[] = { Color_Blue, Color_Green }; // We use this special creation function because we have an array of @@ -209,7 +218,8 @@ flatbuffers::DetachedBuffer CreateFlatBufferTest(std::string &buffer) { testv, vecofstrings, vecoftables, 0, nested_flatbuffer_vector, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3.14159f, 3.0f, 0.0f, vecofstrings2, vecofstructs, flex, testv2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - AnyUniqueAliases_NONE, 0, AnyAmbiguousAliases_NONE, 0, vecofcolors); + AnyUniqueAliases_NONE, 0, AnyAmbiguousAliases_NONE, 0, vecofcolors, + MyGame::Example::Race_None, 0, vec_of_stats); FinishMonsterBuffer(builder, mloc); @@ -360,11 +370,20 @@ void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length, auto right = vecofstructs->Get(i + 1); TEST_EQ(true, (left->KeyCompareLessThan(right))); } + TEST_NOTNULL(vecofstructs->LookupByKey(0)); // test default value TEST_NOTNULL(vecofstructs->LookupByKey(3)); TEST_EQ(static_cast(nullptr), vecofstructs->LookupByKey(5)); } + if (auto vec_of_stat = monster->scalar_key_sorted_tables()) { + auto stat_0 = vec_of_stat->LookupByKey(static_cast(0u)); + TEST_NOTNULL(stat_0); + TEST_NOTNULL(stat_0->id()); + TEST_EQ(0, stat_0->count()); + TEST_EQ_STR("miss", stat_0->id()->c_str()); + } + // Test nested FlatBuffers if available: auto nested_buffer = monster->testnestedflatbuffer(); if (nested_buffer) { @@ -650,6 +669,19 @@ void JsonEnumsTest() { auto result = GenerateText(parser, builder.GetBufferPointer(), &jsongen); TEST_EQ(result, true); TEST_EQ(std::string::npos != jsongen.find("color: \"Red Blue\""), true); + // Test forward compatibility with 'output_enum_identifiers = true'. + // Current Color doesn't have '(1u << 2)' field, let's add it. + builder.Clear(); + std::string future_json; + auto future_name = builder.CreateString("future bitflag_enum"); + MonsterBuilder future_color(builder); + future_color.add_name(future_name); + future_color.add_color( + static_cast((1u << 2) | Color_Blue | Color_Red)); + FinishMonsterBuffer(builder, future_color.Finish()); + result = GenerateText(parser, builder.GetBufferPointer(), &future_json); + TEST_EQ(result, true); + TEST_EQ(std::string::npos != future_json.find("color: 13"), true); } #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) @@ -664,17 +696,28 @@ template bool is_quiet_nan_impl(T v) { std::memcpy(&b, &v, sizeof(T)); return ((b & qnan_base) == qnan_base); } +# if defined(__mips__) || defined(__hppa__) +static bool is_quiet_nan(float v) { + return is_quiet_nan_impl(v) || + is_quiet_nan_impl(v); +} +static bool is_quiet_nan(double v) { + return is_quiet_nan_impl(v) || + is_quiet_nan_impl(v); +} +# else static bool is_quiet_nan(float v) { return is_quiet_nan_impl(v); } static bool is_quiet_nan(double v) { return is_quiet_nan_impl(v); } +# endif void TestMonsterExtraFloats() { TEST_EQ(is_quiet_nan(1.0), false); - TEST_EQ(is_quiet_nan(infinityd), false); - TEST_EQ(is_quiet_nan(-infinityf), false); + TEST_EQ(is_quiet_nan(infinity_d), false); + TEST_EQ(is_quiet_nan(-infinity_f), false); TEST_EQ(is_quiet_nan(std::numeric_limits::quiet_NaN()), true); TEST_EQ(is_quiet_nan(std::numeric_limits::quiet_NaN()), true); @@ -702,12 +745,12 @@ void TestMonsterExtraFloats() { TEST_NOTNULL(def_extra); TEST_EQ(is_quiet_nan(def_extra->f0()), true); TEST_EQ(is_quiet_nan(def_extra->f1()), true); - TEST_EQ(def_extra->f2(), +infinityf); - TEST_EQ(def_extra->f3(), -infinityf); + TEST_EQ(def_extra->f2(), +infinity_f); + TEST_EQ(def_extra->f3(), -infinity_f); TEST_EQ(is_quiet_nan(def_extra->d0()), true); TEST_EQ(is_quiet_nan(def_extra->d1()), true); - TEST_EQ(def_extra->d2(), +infinityd); - TEST_EQ(def_extra->d3(), -infinityd); + TEST_EQ(def_extra->d2(), +infinity_d); + TEST_EQ(def_extra->d3(), -infinity_d); std::string jsongen; auto result = GenerateText(parser, def_obj, &jsongen); TEST_EQ(result, true); @@ -733,23 +776,23 @@ void TestMonsterExtraFloats() { TEST_NOTNULL(extra); TEST_EQ(is_quiet_nan(extra->f0()), true); TEST_EQ(is_quiet_nan(extra->f1()), true); - TEST_EQ(extra->f2(), +infinityf); - TEST_EQ(extra->f3(), -infinityf); + TEST_EQ(extra->f2(), +infinity_f); + TEST_EQ(extra->f3(), -infinity_f); TEST_EQ(is_quiet_nan(extra->d0()), true); - TEST_EQ(extra->d1(), +infinityd); - TEST_EQ(extra->d2(), -infinityd); + TEST_EQ(extra->d1(), +infinity_d); + TEST_EQ(extra->d2(), -infinity_d); TEST_EQ(is_quiet_nan(extra->d3()), true); TEST_NOTNULL(extra->fvec()); TEST_EQ(extra->fvec()->size(), 4); TEST_EQ(extra->fvec()->Get(0), 1.0f); - TEST_EQ(extra->fvec()->Get(1), -infinityf); - TEST_EQ(extra->fvec()->Get(2), +infinityf); + TEST_EQ(extra->fvec()->Get(1), -infinity_f); + TEST_EQ(extra->fvec()->Get(2), +infinity_f); TEST_EQ(is_quiet_nan(extra->fvec()->Get(3)), true); TEST_NOTNULL(extra->dvec()); TEST_EQ(extra->dvec()->size(), 4); TEST_EQ(extra->dvec()->Get(0), 2.0); - TEST_EQ(extra->dvec()->Get(1), +infinityd); - TEST_EQ(extra->dvec()->Get(2), -infinityd); + TEST_EQ(extra->dvec()->Get(1), +infinity_d); + TEST_EQ(extra->dvec()->Get(2), -infinity_d); TEST_EQ(is_quiet_nan(extra->dvec()->Get(3)), true); } #else @@ -791,7 +834,7 @@ void ParseAndGenerateTextTest(bool binary) { } else { TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true); } - TEST_EQ(parser.Parse(jsonfile.c_str(), include_directories), true); + TEST_EQ(parser.ParseJson(jsonfile.c_str()), true); // here, parser.builder_ contains a binary buffer that is the parsed data. @@ -875,6 +918,7 @@ void ReflectionTest(uint8_t *flatbuf, size_t length) { TEST_EQ_STR(hp_field.name()->c_str(), "hp"); TEST_EQ(hp_field.id(), 2); TEST_EQ(hp_field.type()->base_type(), reflection::Short); + auto friendly_field_ptr = fields->LookupByKey("friendly"); TEST_NOTNULL(friendly_field_ptr); TEST_NOTNULL(friendly_field_ptr->attributes()); @@ -888,6 +932,12 @@ void ReflectionTest(uint8_t *flatbuf, size_t length) { TEST_NOTNULL(pos_table_ptr); TEST_EQ_STR(pos_table_ptr->name()->c_str(), "MyGame.Example.Vec3"); + // Test nullability of fields: hp is a 0-default scalar, pos is a struct => + // optional, and name is a required string => not optional. + TEST_EQ(hp_field.optional(), false); + TEST_EQ(pos_field_ptr->optional(), true); + TEST_EQ(fields->LookupByKey("name")->optional(), false); + // Now use it to dynamically access a buffer. auto &root = *flatbuffers::GetAnyRoot(flatbuf); @@ -1056,12 +1106,13 @@ void MiniReflectFlatBuffersTest(uint8_t *flatbuf) { "4, 0, 6, 0, 8, 0, 12, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 13, 0, 0, 0, 78, " "101, 115, 116, 101, 100, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0 ], " "testarrayofstring2: [ \"jane\", \"mary\" ], " - "testarrayofsortedstruct: [ { id: 1, distance: 10 }, " + "testarrayofsortedstruct: [ { id: 0, distance: 0 }, " "{ id: 2, distance: 20 }, { id: 3, distance: 30 }, " "{ id: 4, distance: 40 } ], " "flex: [ 210, 4, 5, 2 ], " "test5: [ { a: 10, b: 20 }, { a: 30, b: 40 } ], " - "vector_of_enums: [ Blue, Green ] " + "vector_of_enums: [ Blue, Green ], " + "scalar_key_sorted_tables: [ { id: \"miss\" } ] " "}"); Test test(16, 32); @@ -1076,6 +1127,30 @@ void MiniReflectFlatBuffersTest(uint8_t *flatbuf) { "16, b: 32 } }"); } +void MiniReflectFixedLengthArrayTest() { + // VS10 does not support typed enums, exclude from tests +#if !defined(_MSC_VER) || _MSC_VER >= 1700 + flatbuffers::FlatBufferBuilder fbb; + MyGame::Example::ArrayStruct aStruct(2, 12, 1); + auto aTable = MyGame::Example::CreateArrayTable(fbb, &aStruct); + fbb.Finish(aTable); + + auto flatbuf = fbb.Release(); + auto s = flatbuffers::FlatBufferToString( + flatbuf.data(), MyGame::Example::ArrayTableTypeTable()); + TEST_EQ_STR( + "{ " + "a: { a: 2.0, " + "b: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], " + "c: 12, " + "d: [ { a: [ 0, 0 ], b: A, c: [ A, A ], d: [ 0, 0 ] }, " + "{ a: [ 0, 0 ], b: A, c: [ A, A ], d: [ 0, 0 ] } ], " + "e: 1, f: [ 0, 0 ] } " + "}", + s.c_str()); +#endif +} + // Parse a .proto schema, output as .fbs void ParseProtoTest() { // load the .proto and the golden file from disk @@ -1128,27 +1203,79 @@ void ParseProtoTest() { } // Parse a .proto schema, output as .fbs -void ParseProtoTestWithIncludes() { +void ParseProtoTestWithSuffix() { // load the .proto and the golden file from disk std::string protofile; std::string goldenfile; std::string goldenunionfile; - std::string importprotofile; TEST_EQ( flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(), false, &protofile), true); + TEST_EQ(flatbuffers::LoadFile( + (test_data_path + "prototest/test_suffix.golden").c_str(), false, + &goldenfile), + true); + TEST_EQ(flatbuffers::LoadFile( + (test_data_path + "prototest/test_union_suffix.golden").c_str(), + false, &goldenunionfile), + true); + + flatbuffers::IDLOptions opts; + opts.include_dependence_headers = false; + opts.proto_mode = true; + opts.proto_namespace_suffix = "test_namespace_suffix"; + + // Parse proto. + flatbuffers::Parser parser(opts); + auto protopath = test_data_path + "prototest/"; + const char *include_directories[] = { protopath.c_str(), nullptr }; + TEST_EQ(parser.Parse(protofile.c_str(), include_directories), true); + + // Generate fbs. + auto fbs = flatbuffers::GenerateFBS(parser, "test"); + + // Ensure generated file is parsable. + flatbuffers::Parser parser2; + TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true); + TEST_EQ_STR(fbs.c_str(), goldenfile.c_str()); + + // Parse proto with --oneof-union option. + opts.proto_oneof_union = true; + flatbuffers::Parser parser3(opts); + TEST_EQ(parser3.Parse(protofile.c_str(), include_directories), true); + + // Generate fbs. + auto fbs_union = flatbuffers::GenerateFBS(parser3, "test"); + + // Ensure generated file is parsable. + flatbuffers::Parser parser4; + TEST_EQ(parser4.Parse(fbs_union.c_str(), nullptr), true); + TEST_EQ_STR(fbs_union.c_str(), goldenunionfile.c_str()); +} + +// Parse a .proto schema, output as .fbs +void ParseProtoTestWithIncludes() { + // load the .proto and the golden file from disk + std::string protofile; + std::string goldenfile; + std::string goldenunionfile; + std::string importprotofile; TEST_EQ( - flatbuffers::LoadFile((test_data_path + "prototest/imported.proto").c_str(), - false, &importprotofile), - true); - TEST_EQ( - flatbuffers::LoadFile((test_data_path + "prototest/test_include.golden").c_str(), - false, &goldenfile), + flatbuffers::LoadFile((test_data_path + "prototest/test.proto").c_str(), + false, &protofile), true); TEST_EQ(flatbuffers::LoadFile( - (test_data_path + "prototest/test_union_include.golden").c_str(), false, - &goldenunionfile), + (test_data_path + "prototest/imported.proto").c_str(), false, + &importprotofile), + true); + TEST_EQ(flatbuffers::LoadFile( + (test_data_path + "prototest/test_include.golden").c_str(), false, + &goldenfile), + true); + TEST_EQ(flatbuffers::LoadFile( + (test_data_path + "prototest/test_union_include.golden").c_str(), + false, &goldenunionfile), true); flatbuffers::IDLOptions opts; @@ -1166,15 +1293,16 @@ void ParseProtoTestWithIncludes() { // Generate fbs from import.proto flatbuffers::Parser import_parser(opts); - TEST_EQ(import_parser.Parse(importprotofile.c_str(), include_directories), true); + TEST_EQ(import_parser.Parse(importprotofile.c_str(), include_directories), + true); auto import_fbs = flatbuffers::GenerateFBS(import_parser, "test"); // Ensure generated file is parsable. flatbuffers::Parser parser2; - TEST_EQ(parser2.Parse(import_fbs.c_str(), include_directories, "imported.fbs"), true); + TEST_EQ( + parser2.Parse(import_fbs.c_str(), include_directories, "imported.fbs"), + true); TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true); - //printf("Golden\n%s\n", goldenfile.c_str()); - printf("FBS\n%s\n", fbs.c_str()); TEST_EQ_STR(fbs.c_str(), goldenfile.c_str()); // Parse proto with --oneof-union option. @@ -1459,7 +1587,7 @@ void FuzzTest2() { break; } } - TEST_NOTNULL(nullptr); + TEST_NOTNULL(nullptr); //-V501 (this comment supresses CWE-570 warning) } // clang-format off @@ -1482,7 +1610,7 @@ void TestError_(const char *src, const char *error_substr, bool strict_json, ("parser.Parse(\"" + std::string(src) + "\")").c_str(), file, line, func); } else if (!strstr(parser.error_.c_str(), error_substr)) { - TestFail(parser.error_.c_str(), error_substr, + TestFail(error_substr, parser.error_.c_str(), ("parser.Parse(\"" + std::string(src) + "\")").c_str(), file, line, func); } @@ -1516,7 +1644,6 @@ void ErrorTest() { TestError("table X { Y:int; Y:int; }", "field already"); TestError("table Y {} table X { Y:int; }", "same as table"); TestError("struct X { Y:string; }", "only scalar"); - TestError("table X { Y:string = \"\"; }", "default values"); TestError("struct X { a:uint = 42; }", "default values"); TestError("enum Y:byte { Z = 1 } table X { y:Y; }", "not part of enum"); TestError("struct X { Y:int (deprecated); }", "deprecate"); @@ -1538,7 +1665,7 @@ void ErrorTest() { TestError("enum X:byte { Y } enum X {", "enum already"); TestError("enum X:float {}", "underlying"); TestError("enum X:byte { Y, Y }", "value already"); - TestError("enum X:byte { Y=2, Z=1 }", "ascending"); + TestError("enum X:byte { Y=2, Z=2 }", "unique"); TestError("table X { Y:int; } table X {", "datatype already"); TestError("struct X (force_align: 7) { Y:int; }", "force_align"); TestError("struct X {}", "size 0"); @@ -1556,6 +1683,20 @@ void ErrorTest() { TestError("table X { Y:int; } root_type X; { Y:1.0 }", "float"); TestError("table X { Y:bool; } root_type X; { Y:1.0 }", "float"); TestError("enum X:bool { Y = true }", "must be integral"); + // Array of non-scalar + TestError("table X { x:int; } struct Y { y:[X:2]; }", + "may contain only scalar or struct fields"); + // Non-snake case field names + TestError("table X { Y: int; } root_type Y: {Y:1.0}", "snake_case"); + // Complex defaults + TestError("table X { y: string = 1; }", "expecting: string"); + TestError("table X { y: string = []; }", " Cannot assign token"); + TestError("table X { y: [int] = [1]; }", "Expected `]`"); + TestError("table X { y: [int] = [; }", "Expected `]`"); + TestError("table X { y: [int] = \"\"; }", "type mismatch"); + // An identifier can't start from sign (+|-) + TestError("table X { -Y: int; } root_type Y: {Y:1.0}", "identifier"); + TestError("table X { +Y: int; } root_type Y: {Y:1.0}", "identifier"); } template @@ -1567,7 +1708,7 @@ T TestValue(const char *json, const char *type_name, if (check_default) { parser.opts.output_default_scalars_in_json = true; } // Simple schema. std::string schema = std::string(decls ? decls : "") + "\n" + - "table X { Y:" + std::string(type_name) + + "table X { y:" + std::string(type_name) + "; } root_type X;"; auto schema_done = parser.Parse(schema.c_str()); TEST_EQ_STR(parser.error_.c_str(), ""); @@ -1596,45 +1737,45 @@ bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; } void ValueTest() { // Test scientific notation numbers. TEST_EQ( - FloatCompare(TestValue("{ Y:0.0314159e+2 }", "float"), 3.14159f), + FloatCompare(TestValue("{ y:0.0314159e+2 }", "float"), 3.14159f), true); // number in string - TEST_EQ(FloatCompare(TestValue("{ Y:\"0.0314159e+2\" }", "float"), + TEST_EQ(FloatCompare(TestValue("{ y:\"0.0314159e+2\" }", "float"), 3.14159f), true); // Test conversion functions. - TEST_EQ(FloatCompare(TestValue("{ Y:cos(rad(180)) }", "float"), -1), + TEST_EQ(FloatCompare(TestValue("{ y:cos(rad(180)) }", "float"), -1), true); // int embedded to string - TEST_EQ(TestValue("{ Y:\"-876\" }", "int=-123"), -876); - TEST_EQ(TestValue("{ Y:\"876\" }", "int=-123"), 876); + TEST_EQ(TestValue("{ y:\"-876\" }", "int=-123"), -876); + TEST_EQ(TestValue("{ y:\"876\" }", "int=-123"), 876); // Test negative hex constant. - TEST_EQ(TestValue("{ Y:-0x8ea0 }", "int=-0x8ea0"), -36512); + TEST_EQ(TestValue("{ y:-0x8ea0 }", "int=-0x8ea0"), -36512); TEST_EQ(TestValue(nullptr, "int=-0x8ea0"), -36512); // positive hex constant - TEST_EQ(TestValue("{ Y:0x1abcdef }", "int=0x1"), 0x1abcdef); + TEST_EQ(TestValue("{ y:0x1abcdef }", "int=0x1"), 0x1abcdef); // with optional '+' sign - TEST_EQ(TestValue("{ Y:+0x1abcdef }", "int=+0x1"), 0x1abcdef); + TEST_EQ(TestValue("{ y:+0x1abcdef }", "int=+0x1"), 0x1abcdef); // hex in string - TEST_EQ(TestValue("{ Y:\"0x1abcdef\" }", "int=+0x1"), 0x1abcdef); + TEST_EQ(TestValue("{ y:\"0x1abcdef\" }", "int=+0x1"), 0x1abcdef); // Make sure we do unsigned 64bit correctly. - TEST_EQ(TestValue("{ Y:12335089644688340133 }", "ulong"), + TEST_EQ(TestValue("{ y:12335089644688340133 }", "ulong"), 12335089644688340133ULL); // bool in string - TEST_EQ(TestValue("{ Y:\"false\" }", "bool=true"), false); - TEST_EQ(TestValue("{ Y:\"true\" }", "bool=\"true\""), true); - TEST_EQ(TestValue("{ Y:'false' }", "bool=true"), false); - TEST_EQ(TestValue("{ Y:'true' }", "bool=\"true\""), true); + TEST_EQ(TestValue("{ y:\"false\" }", "bool=true"), false); + TEST_EQ(TestValue("{ y:\"true\" }", "bool=\"true\""), true); + TEST_EQ(TestValue("{ y:'false' }", "bool=true"), false); + TEST_EQ(TestValue("{ y:'true' }", "bool=\"true\""), true); // check comments before and after json object - TEST_EQ(TestValue("/*before*/ { Y:1 } /*after*/", "int"), 1); - TEST_EQ(TestValue("//before \n { Y:1 } //after", "int"), 1); + TEST_EQ(TestValue("/*before*/ { y:1 } /*after*/", "int"), 1); + TEST_EQ(TestValue("//before \n { y:1 } //after", "int"), 1); } void NestedListTest() { @@ -1687,9 +1828,7 @@ void EnumOutOfRangeTest() { TestError("enum X:ubyte { Y = -1 }", "enum value does not fit"); TestError("enum X:ubyte { Y = 256 }", "enum value does not fit"); TestError("enum X:ubyte { Y = 255, Z }", "enum value does not fit"); - // Unions begin with an implicit "NONE = 0". - TestError("table Y{} union X { Y = -1 }", - "enum values must be specified in ascending order"); + TestError("table Y{} union X { Y = -1 }", "enum value does not fit"); TestError("table Y{} union X { Y = 256 }", "enum value does not fit"); TestError("table Y{} union X { Y = 255, Z:Y }", "enum value does not fit"); TestError("enum X:int { Y = -2147483649 }", "enum value does not fit"); @@ -1708,18 +1847,18 @@ void EnumOutOfRangeTest() { } void EnumValueTest() { - // json: "{ Y:0 }", schema: table X { Y : "E"} + // json: "{ Y:0 }", schema: table X { y: "E"} // 0 in enum (V=0) E then Y=0 is valid. - TEST_EQ(TestValue("{ Y:0 }", "E", "enum E:int { V }"), 0); - TEST_EQ(TestValue("{ Y:V }", "E", "enum E:int { V }"), 0); + TEST_EQ(TestValue("{ y:0 }", "E", "enum E:int { V }"), 0); + TEST_EQ(TestValue("{ y:V }", "E", "enum E:int { V }"), 0); // A default value of Y is 0. TEST_EQ(TestValue("{ }", "E", "enum E:int { V }"), 0); - TEST_EQ(TestValue("{ Y:5 }", "E=V", "enum E:int { V=5 }"), 5); + TEST_EQ(TestValue("{ y:5 }", "E=V", "enum E:int { V=5 }"), 5); // Generate json with defaults and check. TEST_EQ(TestValue(nullptr, "E=V", "enum E:int { V=5 }"), 5); // 5 in enum - TEST_EQ(TestValue("{ Y:5 }", "E", "enum E:int { Z, V=5 }"), 5); - TEST_EQ(TestValue("{ Y:5 }", "E=V", "enum E:int { Z, V=5 }"), 5); + TEST_EQ(TestValue("{ y:5 }", "E", "enum E:int { Z, V=5 }"), 5); + TEST_EQ(TestValue("{ y:5 }", "E=V", "enum E:int { Z, V=5 }"), 5); // Generate json with defaults and check. TEST_EQ(TestValue(nullptr, "E", "enum E:int { Z, V=5 }"), 0); TEST_EQ(TestValue(nullptr, "E=V", "enum E:int { Z, V=5 }"), 5); @@ -1731,7 +1870,9 @@ void EnumValueTest() { "enum E:ulong { V = 18446744073709551615 }"), 18446744073709551615ULL); // Assign non-enum value to enum field. Is it right? - TEST_EQ(TestValue("{ Y:7 }", "E", "enum E:int { V = 0 }"), 7); + TEST_EQ(TestValue("{ y:7 }", "E", "enum E:int { V = 0 }"), 7); + // Check that non-ascending values are valid. + TEST_EQ(TestValue("{ y:5 }", "E=V", "enum E:int { Z=10, V=5 }"), 5); } void IntegerOutOfRangeTest() { @@ -1825,26 +1966,26 @@ void IntegerBoundaryTest() { TEST_EQ(flatbuffers::numeric_limits::max(), 18446744073709551615ULL); - TEST_EQ(TestValue("{ Y:127 }", "byte"), 127); - TEST_EQ(TestValue("{ Y:-128 }", "byte"), -128); - TEST_EQ(TestValue("{ Y:255 }", "ubyte"), 255); - TEST_EQ(TestValue("{ Y:0 }", "ubyte"), 0); - TEST_EQ(TestValue("{ Y:32767 }", "short"), 32767); - TEST_EQ(TestValue("{ Y:-32768 }", "short"), -32768); - TEST_EQ(TestValue("{ Y:65535 }", "ushort"), 65535); - TEST_EQ(TestValue("{ Y:0 }", "ushort"), 0); - TEST_EQ(TestValue("{ Y:2147483647 }", "int"), 2147483647); - TEST_EQ(TestValue("{ Y:-2147483648 }", "int") + 1, -2147483647); - TEST_EQ(TestValue("{ Y:4294967295 }", "uint"), 4294967295); - TEST_EQ(TestValue("{ Y:0 }", "uint"), 0); - TEST_EQ(TestValue("{ Y:9223372036854775807 }", "long"), + TEST_EQ(TestValue("{ y:127 }", "byte"), 127); + TEST_EQ(TestValue("{ y:-128 }", "byte"), -128); + TEST_EQ(TestValue("{ y:255 }", "ubyte"), 255); + TEST_EQ(TestValue("{ y:0 }", "ubyte"), 0); + TEST_EQ(TestValue("{ y:32767 }", "short"), 32767); + TEST_EQ(TestValue("{ y:-32768 }", "short"), -32768); + TEST_EQ(TestValue("{ y:65535 }", "ushort"), 65535); + TEST_EQ(TestValue("{ y:0 }", "ushort"), 0); + TEST_EQ(TestValue("{ y:2147483647 }", "int"), 2147483647); + TEST_EQ(TestValue("{ y:-2147483648 }", "int") + 1, -2147483647); + TEST_EQ(TestValue("{ y:4294967295 }", "uint"), 4294967295); + TEST_EQ(TestValue("{ y:0 }", "uint"), 0); + TEST_EQ(TestValue("{ y:9223372036854775807 }", "long"), 9223372036854775807LL); - TEST_EQ(TestValue("{ Y:-9223372036854775808 }", "long") + 1LL, + TEST_EQ(TestValue("{ y:-9223372036854775808 }", "long") + 1LL, -9223372036854775807LL); - TEST_EQ(TestValue("{ Y:18446744073709551615 }", "ulong"), + TEST_EQ(TestValue("{ y:18446744073709551615 }", "ulong"), 18446744073709551615ULL); - TEST_EQ(TestValue("{ Y:0 }", "ulong"), 0); - TEST_EQ(TestValue("{ Y: 18446744073709551615 }", "uint64"), + TEST_EQ(TestValue("{ y:0 }", "ulong"), 0); + TEST_EQ(TestValue("{ y: 18446744073709551615 }", "uint64"), 18446744073709551615ULL); // check that the default works TEST_EQ(TestValue(nullptr, "uint64 = 18446744073709551615"), @@ -1853,77 +1994,81 @@ void IntegerBoundaryTest() { void ValidFloatTest() { // check rounding to infinity - TEST_EQ(TestValue("{ Y:+3.4029e+38 }", "float"), +infinityf); - TEST_EQ(TestValue("{ Y:-3.4029e+38 }", "float"), -infinityf); - TEST_EQ(TestValue("{ Y:+1.7977e+308 }", "double"), +infinityd); - TEST_EQ(TestValue("{ Y:-1.7977e+308 }", "double"), -infinityd); + TEST_EQ(TestValue("{ y:+3.4029e+38 }", "float"), +infinity_f); + TEST_EQ(TestValue("{ y:-3.4029e+38 }", "float"), -infinity_f); + TEST_EQ(TestValue("{ y:+1.7977e+308 }", "double"), +infinity_d); + TEST_EQ(TestValue("{ y:-1.7977e+308 }", "double"), -infinity_d); TEST_EQ( - FloatCompare(TestValue("{ Y:0.0314159e+2 }", "float"), 3.14159f), + FloatCompare(TestValue("{ y:0.0314159e+2 }", "float"), 3.14159f), true); // float in string - TEST_EQ(FloatCompare(TestValue("{ Y:\" 0.0314159e+2 \" }", "float"), + TEST_EQ(FloatCompare(TestValue("{ y:\" 0.0314159e+2 \" }", "float"), 3.14159f), true); - TEST_EQ(TestValue("{ Y:1 }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:1.0 }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:1. }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:+1. }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:-1. }", "float"), -1.0f); - TEST_EQ(TestValue("{ Y:1.e0 }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:1.e+0 }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:1.e-0 }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:0.125 }", "float"), 0.125f); - TEST_EQ(TestValue("{ Y:.125 }", "float"), 0.125f); - TEST_EQ(TestValue("{ Y:-.125 }", "float"), -0.125f); - TEST_EQ(TestValue("{ Y:+.125 }", "float"), +0.125f); - TEST_EQ(TestValue("{ Y:5 }", "float"), 5.0f); - TEST_EQ(TestValue("{ Y:\"5\" }", "float"), 5.0f); + TEST_EQ(TestValue("{ y:1 }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:1.0 }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:1. }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:+1. }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:-1. }", "float"), -1.0f); + TEST_EQ(TestValue("{ y:1.e0 }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:1.e+0 }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:1.e-0 }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:0.125 }", "float"), 0.125f); + TEST_EQ(TestValue("{ y:.125 }", "float"), 0.125f); + TEST_EQ(TestValue("{ y:-.125 }", "float"), -0.125f); + TEST_EQ(TestValue("{ y:+.125 }", "float"), +0.125f); + TEST_EQ(TestValue("{ y:5 }", "float"), 5.0f); + TEST_EQ(TestValue("{ y:\"5\" }", "float"), 5.0f); #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) // Old MSVC versions may have problem with this check. // https://www.exploringbinary.com/visual-c-plus-plus-strtod-still-broken/ - TEST_EQ(TestValue("{ Y:6.9294956446009195e15 }", "double"), + TEST_EQ(TestValue("{ y:6.9294956446009195e15 }", "double"), 6929495644600920.0); // check nan's - TEST_EQ(std::isnan(TestValue("{ Y:nan }", "double")), true); - TEST_EQ(std::isnan(TestValue("{ Y:nan }", "float")), true); - TEST_EQ(std::isnan(TestValue("{ Y:\"nan\" }", "float")), true); - TEST_EQ(std::isnan(TestValue("{ Y:+nan }", "float")), true); - TEST_EQ(std::isnan(TestValue("{ Y:-nan }", "float")), true); + TEST_EQ(std::isnan(TestValue("{ y:nan }", "double")), true); + TEST_EQ(std::isnan(TestValue("{ y:nan }", "float")), true); + TEST_EQ(std::isnan(TestValue("{ y:\"nan\" }", "float")), true); + TEST_EQ(std::isnan(TestValue("{ y:\"+nan\" }", "float")), true); + TEST_EQ(std::isnan(TestValue("{ y:\"-nan\" }", "float")), true); + TEST_EQ(std::isnan(TestValue("{ y:+nan }", "float")), true); + TEST_EQ(std::isnan(TestValue("{ y:-nan }", "float")), true); TEST_EQ(std::isnan(TestValue(nullptr, "float=nan")), true); TEST_EQ(std::isnan(TestValue(nullptr, "float=-nan")), true); // check inf - TEST_EQ(TestValue("{ Y:inf }", "float"), infinityf); - TEST_EQ(TestValue("{ Y:\"inf\" }", "float"), infinityf); - TEST_EQ(TestValue("{ Y:+inf }", "float"), infinityf); - TEST_EQ(TestValue("{ Y:-inf }", "float"), -infinityf); - TEST_EQ(TestValue(nullptr, "float=inf"), infinityf); - TEST_EQ(TestValue(nullptr, "float=-inf"), -infinityf); + TEST_EQ(TestValue("{ y:inf }", "float"), infinity_f); + TEST_EQ(TestValue("{ y:\"inf\" }", "float"), infinity_f); + TEST_EQ(TestValue("{ y:\"-inf\" }", "float"), -infinity_f); + TEST_EQ(TestValue("{ y:\"+inf\" }", "float"), infinity_f); + TEST_EQ(TestValue("{ y:+inf }", "float"), infinity_f); + TEST_EQ(TestValue("{ y:-inf }", "float"), -infinity_f); + TEST_EQ(TestValue(nullptr, "float=inf"), infinity_f); + TEST_EQ(TestValue(nullptr, "float=-inf"), -infinity_f); TestValue( - "{ Y : [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, " + "{ y: [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, " "3.0e2] }", "[double]"); TestValue( - "{ Y : [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, " + "{ y: [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, " "3.0e2] }", "[float]"); // Test binary format of float point. // https://en.cppreference.com/w/cpp/language/floating_literal // 0x11.12p-1 = (1*16^1 + 2*16^0 + 3*16^-1 + 4*16^-2) * 2^-1 = - TEST_EQ(TestValue("{ Y:0x12.34p-1 }", "double"), 9.1015625); + TEST_EQ(TestValue("{ y:0x12.34p-1 }", "double"), 9.1015625); // hex fraction 1.2 (decimal 1.125) scaled by 2^3, that is 9.0 - TEST_EQ(TestValue("{ Y:-0x0.2p0 }", "float"), -0.125f); - TEST_EQ(TestValue("{ Y:-0x.2p1 }", "float"), -0.25f); - TEST_EQ(TestValue("{ Y:0x1.2p3 }", "float"), 9.0f); - TEST_EQ(TestValue("{ Y:0x10.1p0 }", "float"), 16.0625f); - TEST_EQ(TestValue("{ Y:0x1.2p3 }", "double"), 9.0); - TEST_EQ(TestValue("{ Y:0x10.1p0 }", "double"), 16.0625); - TEST_EQ(TestValue("{ Y:0xC.68p+2 }", "double"), 49.625); - TestValue("{ Y : [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[double]"); - TestValue("{ Y : [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[float]"); + TEST_EQ(TestValue("{ y:-0x0.2p0 }", "float"), -0.125f); + TEST_EQ(TestValue("{ y:-0x.2p1 }", "float"), -0.25f); + TEST_EQ(TestValue("{ y:0x1.2p3 }", "float"), 9.0f); + TEST_EQ(TestValue("{ y:0x10.1p0 }", "float"), 16.0625f); + TEST_EQ(TestValue("{ y:0x1.2p3 }", "double"), 9.0); + TEST_EQ(TestValue("{ y:0x10.1p0 }", "double"), 16.0625); + TEST_EQ(TestValue("{ y:0xC.68p+2 }", "double"), 49.625); + TestValue("{ y: [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[double]"); + TestValue("{ y: [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[float]"); #else // FLATBUFFERS_HAS_NEW_STRTOD TEST_OUTPUT_LINE("FLATBUFFERS_HAS_NEW_STRTOD tests skipped"); @@ -1952,6 +2097,9 @@ void InvalidFloatTest() { TestError("table T { F:float; } root_type T; { F:0x0 }", invalid_msg); TestError("table T { F:float; } root_type T; { F:-0x. }", invalid_msg); TestError("table T { F:float; } root_type T; { F:0x. }", invalid_msg); + TestError("table T { F:float; } root_type T; { F:0Xe }", invalid_msg); + TestError("table T { F:float; } root_type T; { F:\"0Xe\" }", invalid_msg); + TestError("table T { F:float; } root_type T; { F:\"nan(1)\" }", invalid_msg); // eE not exponent in hex-float! TestError("table T { F:float; } root_type T; { F:0x0.0e+ }", invalid_msg); TestError("table T { F:float; } root_type T; { F:0x0.0e- }", invalid_msg); @@ -2017,6 +2165,15 @@ void GenerateTableTextTest() { 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); @@ -2380,71 +2537,110 @@ void InvalidNestedFlatbufferTest() { void EvolutionTest() { // VS10 does not support typed enums, exclude from tests #if !defined(_MSC_VER) || _MSC_VER >= 1700 - const int NUM_VERSIONS = 2; - std::string schemas[NUM_VERSIONS]; - std::string jsonfiles[NUM_VERSIONS]; - std::vector binaries[NUM_VERSIONS]; - - flatbuffers::IDLOptions idl_opts; - idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary; - flatbuffers::Parser parser(idl_opts); - - // Load all the schema versions and their associated data. - for (int i = 0; i < NUM_VERSIONS; ++i) { - std::string schema = test_data_path + "evolution_test/evolution_v" + - flatbuffers::NumToString(i + 1) + ".fbs"; - TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i])); - std::string json = test_data_path + "evolution_test/evolution_v" + - flatbuffers::NumToString(i + 1) + ".json"; - TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i])); - - TEST_ASSERT(parser.Parse(schemas[i].c_str())); - TEST_ASSERT(parser.Parse(jsonfiles[i].c_str())); - - auto bufLen = parser.builder_.GetSize(); - auto buf = parser.builder_.GetBufferPointer(); - binaries[i].reserve(bufLen); - std::copy(buf, buf + bufLen, std::back_inserter(binaries[i])); - } + const int NUM_VERSIONS = 2; + std::string schemas[NUM_VERSIONS]; + std::string jsonfiles[NUM_VERSIONS]; + std::vector binaries[NUM_VERSIONS]; - // Assert that all the verifiers for the different schema versions properly verify any version data. - for (int i = 0; i < NUM_VERSIONS; ++i) { - flatbuffers::Verifier verifier(&binaries[i].front(), binaries[i].size()); - TEST_ASSERT(Evolution::V1::VerifyRootBuffer(verifier)); - TEST_ASSERT(Evolution::V2::VerifyRootBuffer(verifier)); - } + flatbuffers::IDLOptions idl_opts; + idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary; + flatbuffers::Parser parser(idl_opts); + + // Load all the schema versions and their associated data. + for (int i = 0; i < NUM_VERSIONS; ++i) { + std::string schema = test_data_path + "evolution_test/evolution_v" + + flatbuffers::NumToString(i + 1) + ".fbs"; + TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i])); + std::string json = test_data_path + "evolution_test/evolution_v" + + flatbuffers::NumToString(i + 1) + ".json"; + TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i])); + + TEST_ASSERT(parser.Parse(schemas[i].c_str())); + TEST_ASSERT(parser.Parse(jsonfiles[i].c_str())); + + auto bufLen = parser.builder_.GetSize(); + auto buf = parser.builder_.GetBufferPointer(); + binaries[i].reserve(bufLen); + std::copy(buf, buf + bufLen, std::back_inserter(binaries[i])); + } + + // Assert that all the verifiers for the different schema versions properly + // verify any version data. + for (int i = 0; i < NUM_VERSIONS; ++i) { + flatbuffers::Verifier verifier(&binaries[i].front(), binaries[i].size()); + TEST_ASSERT(Evolution::V1::VerifyRootBuffer(verifier)); + TEST_ASSERT(Evolution::V2::VerifyRootBuffer(verifier)); + } - // Test backwards compatibility by reading old data with an evolved schema. - auto root_v1_viewed_from_v2 = Evolution::V2::GetRoot(&binaries[0].front()); - // field 'j' is new in version 2, so it should be null. - TEST_ASSERT(nullptr == root_v1_viewed_from_v2->j()); - // field 'k' is new in version 2 with a default of 56. - TEST_EQ(root_v1_viewed_from_v2->k(), 56); - // field 'c' of 'TableA' is new in version 2, so it should be null. - TEST_ASSERT(nullptr == root_v1_viewed_from_v2->e()->c()); - // 'TableC' was added to field 'c' union in version 2, so it should be null. - TEST_ASSERT(nullptr == root_v1_viewed_from_v2->c_as_TableC()); - // The field 'c' union should be of type 'TableB' regardless of schema version - TEST_ASSERT(root_v1_viewed_from_v2->c_type() == Evolution::V2::Union::TableB); - // The field 'f' was renamed to 'ff' in version 2, it should still be readable. - TEST_EQ(root_v1_viewed_from_v2->ff()->a(), 16); - - // Test forwards compatibility by reading new data with an old schema. - auto root_v2_viewed_from_v1 = Evolution::V1::GetRoot(&binaries[1].front()); - // The field 'c' union in version 2 is a new table (index = 3) and should still be accessible, - // but not interpretable. - TEST_EQ(static_cast(root_v2_viewed_from_v1->c_type()), 3); - TEST_NOTNULL(root_v2_viewed_from_v1->c()); - // The field 'd' enum in verison 2 has new members and should still be accessible, but not interpretable. - TEST_EQ(static_cast(root_v2_viewed_from_v1->d()), 3); - // The field 'a' in version 2 is deprecated and should return the default value (0) instead of the value stored in - // the in the buffer (42). - TEST_EQ(root_v2_viewed_from_v1->a(), 0); - // The field 'ff' was originally named 'f' in version 1, it should still be readable. - TEST_EQ(root_v2_viewed_from_v1->f()->a(), 35); + // Test backwards compatibility by reading old data with an evolved schema. + auto root_v1_viewed_from_v2 = Evolution::V2::GetRoot(&binaries[0].front()); + // field 'k' is new in version 2, so it should be null. + TEST_ASSERT(nullptr == root_v1_viewed_from_v2->k()); + // field 'l' is new in version 2 with a default of 56. + TEST_EQ(root_v1_viewed_from_v2->l(), 56); + // field 'c' of 'TableA' is new in version 2, so it should be null. + TEST_ASSERT(nullptr == root_v1_viewed_from_v2->e()->c()); + // 'TableC' was added to field 'c' union in version 2, so it should be null. + TEST_ASSERT(nullptr == root_v1_viewed_from_v2->c_as_TableC()); + // The field 'c' union should be of type 'TableB' regardless of schema version + TEST_ASSERT(root_v1_viewed_from_v2->c_type() == Evolution::V2::Union::TableB); + // The field 'f' was renamed to 'ff' in version 2, it should still be + // readable. + TEST_EQ(root_v1_viewed_from_v2->ff()->a(), 16); + + // Test forwards compatibility by reading new data with an old schema. + auto root_v2_viewed_from_v1 = Evolution::V1::GetRoot(&binaries[1].front()); + // The field 'c' union in version 2 is a new table (index = 3) and should + // still be accessible, but not interpretable. + TEST_EQ(static_cast(root_v2_viewed_from_v1->c_type()), 3); + TEST_NOTNULL(root_v2_viewed_from_v1->c()); + // The field 'd' enum in verison 2 has new members and should still be + // accessible, but not interpretable. + TEST_EQ(static_cast(root_v2_viewed_from_v1->d()), 3); + // The field 'a' in version 2 is deprecated and should return the default + // value (0) instead of the value stored in the in the buffer (42). + TEST_EQ(root_v2_viewed_from_v1->a(), 0); + // The field 'ff' was originally named 'f' in version 1, it should still be + // readable. + TEST_EQ(root_v2_viewed_from_v1->f()->a(), 35); #endif } +void UnionDeprecationTest() { + const int NUM_VERSIONS = 2; + std::string schemas[NUM_VERSIONS]; + std::string jsonfiles[NUM_VERSIONS]; + std::vector binaries[NUM_VERSIONS]; + + flatbuffers::IDLOptions idl_opts; + idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary; + flatbuffers::Parser parser(idl_opts); + + // Load all the schema versions and their associated data. + for (int i = 0; i < NUM_VERSIONS; ++i) { + std::string schema = test_data_path + "evolution_test/evolution_v" + + flatbuffers::NumToString(i + 1) + ".fbs"; + TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i])); + std::string json = test_data_path + "evolution_test/evolution_v" + + flatbuffers::NumToString(i + 1) + ".json"; + TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i])); + + TEST_ASSERT(parser.Parse(schemas[i].c_str())); + TEST_ASSERT(parser.Parse(jsonfiles[i].c_str())); + + auto bufLen = parser.builder_.GetSize(); + auto buf = parser.builder_.GetBufferPointer(); + binaries[i].reserve(bufLen); + std::copy(buf, buf + bufLen, std::back_inserter(binaries[i])); + } + + auto v2 = parser.LookupStruct("Evolution.V2.Root"); + TEST_NOTNULL(v2); + auto j_type_field = v2->fields.Lookup("j_type"); + TEST_NOTNULL(j_type_field); + TEST_ASSERT(j_type_field->deprecated); +} + void UnionVectorTest() { // load FlatBuffer fbs schema and json. std::string schemafile, jsonfile; @@ -2811,6 +3007,79 @@ void FlexBuffersTest() { // And from FlexBuffer back to JSON: auto jsonback = jroot.ToString(); TEST_EQ_STR(jsontest, jsonback.c_str()); + + slb.Clear(); + slb.Vector([&]() { + for (int i = 0; i < 130; ++i) slb.Add(static_cast(255)); + slb.Vector([&]() { + for (int i = 0; i < 130; ++i) slb.Add(static_cast(255)); + slb.Vector([] {}); + }); + }); + slb.Finish(); + TEST_EQ(slb.GetSize(), 664); +} + +void FlexBuffersDeprecatedTest() { + // FlexBuffers as originally designed had a flaw involving the + // FBT_VECTOR_STRING datatype, and this test documents/tests the fix for it. + // Discussion: https://github.com/google/flatbuffers/issues/5627 + flexbuffers::Builder slb; + // FBT_VECTOR_* are "typed vectors" where all elements are of the same type. + // Problem is, when storing FBT_STRING elements, it relies on that type to + // get the bit-width for the size field of the string, which in this case + // isn't present, and instead defaults to 8-bit. This means that any strings + // stored inside such a vector, when accessed thru the old API that returns + // a String reference, will appear to be truncated if the string stored is + // actually >=256 bytes. + std::string test_data(300, 'A'); + auto start = slb.StartVector(); + // This one will have a 16-bit size field. + slb.String(test_data); + // This one will have an 8-bit size field. + slb.String("hello"); + // We're asking this to be serialized as a typed vector (true), but not + // fixed size (false). The type will be FBT_VECTOR_STRING with a bit-width + // of whatever the offsets in the vector need, the bit-widths of the strings + // are not stored(!) <- the actual design flaw. + // Note that even in the fixed code, we continue to serialize the elements of + // FBT_VECTOR_STRING as FBT_STRING, since there may be old code out there + // reading new data that we want to continue to function. + // Thus, FBT_VECTOR_STRING, while deprecated, will always be represented the + // same way, the fix lies on the reading side. + slb.EndVector(start, true, false); + slb.Finish(); + // So now lets read this data back. + // For existing data, since we have no way of knowing what the actual + // bit-width of the size field of the string is, we are going to ignore this + // field, and instead treat these strings as FBT_KEY (null-terminated), so we + // can deal with strings of arbitrary length. This of course truncates strings + // with embedded nulls, but we think that that is preferrable over truncating + // strings >= 256 bytes. + auto vec = flexbuffers::GetRoot(slb.GetBuffer()).AsTypedVector(); + // Even though this was serialized as FBT_VECTOR_STRING, it is read as + // FBT_VECTOR_KEY: + TEST_EQ(vec.ElementType(), flexbuffers::FBT_KEY); + // Access the long string. Previously, this would return a string of size 1, + // since it would read the high-byte of the 16-bit length. + // This should now correctly test the full 300 bytes, using AsKey(): + TEST_EQ_STR(vec[0].AsKey(), test_data.c_str()); + // Old code that called AsString will continue to work, as the String + // accessor objects now use a cached size that can come from a key as well. + TEST_EQ_STR(vec[0].AsString().c_str(), test_data.c_str()); + // Short strings work as before: + TEST_EQ_STR(vec[1].AsKey(), "hello"); + TEST_EQ_STR(vec[1].AsString().c_str(), "hello"); + // So, while existing code and data mostly "just work" with the fixes applied + // to AsTypedVector and AsString, what do you do going forward? + // Code accessing existing data doesn't necessarily need to change, though + // you could consider using AsKey instead of AsString for a) documenting + // that you are accessing keys, or b) a speedup if you don't actually use + // the string size. + // For new data, or data that doesn't need to be backwards compatible, + // instead serialize as FBT_VECTOR (call EndVector with typed = false, then + // read elements with AsString), or, for maximum compactness, use + // FBT_VECTOR_KEY (call slb.Key above instead, read with AsKey or AsString). } void TypeAliasesTest() { @@ -2985,6 +3254,89 @@ void CreateSharedStringTest() { TEST_EQ((*a[6]) < (*a[5]), true); } +#if !defined(FLATBUFFERS_SPAN_MINIMAL) +void FlatbuffersSpanTest() { + // Compile-time checking of non-const [] to const [] conversions. + using flatbuffers::internal::is_span_convertable; + (void)is_span_convertable::type(123); + (void)is_span_convertable::type(123); + (void)is_span_convertable::type(123); + (void)is_span_convertable::type(123); + (void)is_span_convertable::type(123); + (void)is_span_convertable::type(123); + (void)is_span_convertable::type(123); + + using flatbuffers::span; + span c1; + TEST_EQ(c1.size(), 0); + span c2; + TEST_EQ(c2.size(), 0); + span 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 i1(&i_data7[0], 7); + span 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 i3(i1); + // Construct from a C-array. + span i4(i_data7); + span i5(i_data7); + span i6(i_data7); + span 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 i8(i_cdata5); + span i9(i_cdata5); + TEST_EQ(i9.size(), 5); + // Construction from a (ptr, size) pair. + span i10(i_data7, 7); + span i11(i_data7, 7); + TEST_EQ(i11.size(), 7); + span i12(i_cdata5, 5); + span i13(i_cdata5, 5); + TEST_EQ(i13.size(), 5); + // Construction from std::array. + std::array i_arr6 = { { 0, 1, 2, 3, 4, 5 } }; + span i14(i_arr6); + span i15(i_arr6); + span i16(i_arr6); + span i17(i_arr6); + TEST_EQ(i17.size(), 6); + const std::array i_carr8 = { { 0, 1, 2, 3, 4, 5, 6, 7 } }; + span i18(i_carr8); + span 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 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 + void FixedLengthArrayTest() { // VS10 does not support typed enums, exclude from tests #if !defined(_MSC_VER) || _MSC_VER >= 1700 @@ -3020,7 +3372,7 @@ void FixedLengthArrayTest() { aStruct.mutable_d()->Mutate(0, nStruct0); aStruct.mutable_d()->Mutate(1, nStruct1); auto aTable = MyGame::Example::CreateArrayTable(fbb, &aStruct); - fbb.Finish(aTable); + MyGame::Example::FinishArrayTableBuffer(fbb, aTable); // Verify correctness of the ArrayTable. flatbuffers::Verifier verifier(fbb.GetBufferPointer(), fbb.GetSize()); @@ -3072,9 +3424,77 @@ void FixedLengthArrayTest() { // Check alignment TEST_EQ(0, reinterpret_cast(mArStruct->d()) % 8); TEST_EQ(0, reinterpret_cast(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(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); } #endif } +#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 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{ { 12, 13 } }) } + }; + + MyGame::Example::ArrayStruct arr_struct( + 8.125, + std::array{ + { 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 NativeTypeTest() { const int N = 3; @@ -3163,6 +3583,237 @@ void FixedLengthArrayJsonTest(bool binary) { #endif } +void TestEmbeddedBinarySchema() { + // load JSON from disk + std::string jsonfile; + TEST_EQ(flatbuffers::LoadFile( + (test_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_EQ( + GenerateText(parserOrg, parserOrg.builder_.GetBufferPointer(), &jsonGen), + true); + + // 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); +} + +void StringVectorDefaultsTest() { + std::vector schemas; + schemas.push_back("table Monster { mana: string = \"\"; }"); + schemas.push_back("table Monster { mana: string = \"mystr\"; }"); + schemas.push_back("table Monster { mana: string = \" \"; }"); + schemas.push_back("table Monster { mana: [int] = []; }"); + schemas.push_back("table Monster { mana: [uint] = [ ]; }"); + schemas.push_back("table Monster { mana: [byte] = [\t\t\n]; }"); + for (auto s = schemas.begin(); s < schemas.end(); s++) { + flatbuffers::Parser parser; + TEST_ASSERT(parser.Parse(s->c_str())); + const auto *mana = parser.structs_.Lookup("Monster")->fields.Lookup("mana"); + TEST_EQ(mana->IsDefault(), true); + } +} + +void OptionalScalarsTest() { + // Simple schemas and a "has optional scalar" sentinal. + std::vector schemas; + schemas.push_back("table Monster { mana : int; }"); + schemas.push_back("table Monster { mana : int = 42; }"); + schemas.push_back("table Monster { mana : int = null; }"); + schemas.push_back("table Monster { mana : long; }"); + schemas.push_back("table Monster { mana : long = 42; }"); + schemas.push_back("table Monster { mana : long = null; }"); + schemas.push_back("table Monster { mana : float; }"); + schemas.push_back("table Monster { mana : float = 42; }"); + schemas.push_back("table Monster { mana : float = null; }"); + schemas.push_back("table Monster { mana : double; }"); + schemas.push_back("table Monster { mana : double = 42; }"); + schemas.push_back("table Monster { mana : double = null; }"); + schemas.push_back("table Monster { mana : bool; }"); + schemas.push_back("table Monster { mana : bool = 42; }"); + schemas.push_back("table Monster { mana : bool = null; }"); + schemas.push_back( + "enum Enum: int {A=0, B=1} " + "table Monster { mana : Enum; }"); + schemas.push_back( + "enum Enum: int {A=0, B=1} " + "table Monster { mana : Enum = B; }"); + schemas.push_back( + "enum Enum: int {A=0, B=1} " + "table Monster { mana : Enum = null; }"); + + // Check the FieldDef is correctly set. + for (auto schema = schemas.begin(); schema < schemas.end(); schema++) { + const bool has_null = schema->find("null") != std::string::npos; + flatbuffers::Parser parser; + TEST_ASSERT(parser.Parse(schema->c_str())); + const auto *mana = parser.structs_.Lookup("Monster")->fields.Lookup("mana"); + TEST_EQ(mana->IsOptional(), has_null); + } + + // Test if nullable scalars are allowed for each language. + for (unsigned lang = 1; lang < flatbuffers::IDLOptions::kMAX; lang <<= 1) { + flatbuffers::IDLOptions opts; + opts.lang_to_generate = lang; + if (false == flatbuffers::Parser::SupportsOptionalScalars(opts)) { + continue; + } + for (auto schema = schemas.begin(); schema < schemas.end(); schema++) { + flatbuffers::Parser parser(opts); + auto done = parser.Parse(schema->c_str()); + TEST_EQ_STR(parser.error_.c_str(), ""); + TEST_ASSERT(done); + } + } + + // test C++ nullable + flatbuffers::FlatBufferBuilder fbb; + FinishScalarStuffBuffer( + fbb, optional_scalars::CreateScalarStuff(fbb, 1, static_cast(2))); + auto opts = optional_scalars::GetMutableScalarStuff(fbb.GetBufferPointer()); + TEST_ASSERT(!opts->maybe_bool()); + TEST_ASSERT(!opts->maybe_f32().has_value()); + TEST_ASSERT(opts->maybe_i8().has_value()); + TEST_EQ(opts->maybe_i8().value(), 2); + TEST_ASSERT(opts->mutate_maybe_i8(3)); + TEST_ASSERT(opts->maybe_i8().has_value()); + TEST_EQ(opts->maybe_i8().value(), 3); + TEST_ASSERT(!opts->mutate_maybe_i16(-10)); + + optional_scalars::ScalarStuffT obj; + TEST_ASSERT(!obj.maybe_bool); + TEST_ASSERT(!obj.maybe_f32.has_value()); + opts->UnPackTo(&obj); + TEST_ASSERT(!obj.maybe_bool); + TEST_ASSERT(!obj.maybe_f32.has_value()); + TEST_ASSERT(obj.maybe_i8.has_value() && obj.maybe_i8.value() == 3); + TEST_ASSERT(obj.maybe_i8 && *obj.maybe_i8 == 3); + obj.maybe_i32 = -1; + obj.maybe_enum = optional_scalars::OptionalByte_Two; + + fbb.Clear(); + FinishScalarStuffBuffer(fbb, optional_scalars::ScalarStuff::Pack(fbb, &obj)); + opts = optional_scalars::GetMutableScalarStuff(fbb.GetBufferPointer()); + TEST_ASSERT(opts->maybe_i8().has_value()); + TEST_EQ(opts->maybe_i8().value(), 3); + TEST_ASSERT(opts->maybe_i32().has_value()); + TEST_EQ(opts->maybe_i32().value(), -1); + TEST_EQ(opts->maybe_enum().value(), optional_scalars::OptionalByte_Two); + TEST_ASSERT(opts->maybe_i32() == flatbuffers::Optional(-1)); +} + +void ParseFlexbuffersFromJsonWithNullTest() { + // Test nulls are handled appropriately through flexbuffers to exercise other + // code paths of ParseSingleValue in the optional scalars change. + // TODO(cneo): Json -> Flatbuffers test once some language can generate code + // with optional scalars. + { + char json[] = "{\"opt_field\": 123 }"; + flatbuffers::Parser parser; + flexbuffers::Builder flexbuild; + parser.ParseFlexBuffer(json, nullptr, &flexbuild); + auto root = flexbuffers::GetRoot(flexbuild.GetBuffer()); + TEST_EQ(root.AsMap()["opt_field"].AsInt64(), 123); + } + { + char json[] = "{\"opt_field\": 123.4 }"; + flatbuffers::Parser parser; + flexbuffers::Builder flexbuild; + parser.ParseFlexBuffer(json, nullptr, &flexbuild); + auto root = flexbuffers::GetRoot(flexbuild.GetBuffer()); + TEST_EQ(root.AsMap()["opt_field"].AsDouble(), 123.4); + } + { + char json[] = "{\"opt_field\": null }"; + flatbuffers::Parser parser; + flexbuffers::Builder flexbuild; + parser.ParseFlexBuffer(json, nullptr, &flexbuild); + auto root = flexbuffers::GetRoot(flexbuild.GetBuffer()); + TEST_ASSERT(!root.AsMap().IsTheEmptyMap()); + TEST_ASSERT(root.AsMap()["opt_field"].IsNull()); + TEST_EQ(root.ToString(), std::string("{ opt_field: null }")); + } +} + +void FieldIdentifierTest() { + using flatbuffers::Parser; + TEST_EQ(true, Parser().Parse("table T{ f: int (id:0); }")); + // non-integer `id` should be rejected + TEST_EQ(false, Parser().Parse("table T{ f: int (id:text); }")); + TEST_EQ(false, Parser().Parse("table T{ f: int (id:\"text\"); }")); + TEST_EQ(false, Parser().Parse("table T{ f: int (id:0text); }")); + TEST_EQ(false, Parser().Parse("table T{ f: int (id:1.0); }")); + TEST_EQ(false, Parser().Parse("table T{ f: int (id:-1); g: int (id:0); }")); + TEST_EQ(false, Parser().Parse("table T{ f: int (id:129496726); }")); + // A unuion filed occupys two ids: enumerator + pointer (offset). + TEST_EQ(false, + Parser().Parse("union X{} table T{ u: X(id:0); table F{x:int;\n}")); + // Positive tests for unions + TEST_EQ(true, Parser().Parse("union X{} table T{ u: X (id:1); }")); + TEST_EQ(true, Parser().Parse("union X{} table T{ u: X; }")); + // Test using 'inf' and 'nan' words both as identifiers and as default values. + TEST_EQ(true, Parser().Parse("table T{ nan: string; }")); + TEST_EQ(true, Parser().Parse("table T{ inf: string; }")); +#if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) + TEST_EQ(true, Parser().Parse("table T{ inf: float = inf; }")); + TEST_EQ(true, Parser().Parse("table T{ nan: float = inf; }")); +#endif +} + +void ParseIncorrectMonsterJsonTest() { + std::string schemafile; + TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.bfbs").c_str(), + true, &schemafile), + true); + flatbuffers::Parser parser; + flatbuffers::Verifier verifier( + reinterpret_cast(schemafile.c_str()), schemafile.size()); + TEST_EQ(reflection::VerifySchemaBuffer(verifier), true); + TEST_EQ(parser.Deserialize((const uint8_t *)schemafile.c_str(), + schemafile.size()), + true); + TEST_EQ(parser.ParseJson("{name:\"monster\"}"), true); + TEST_EQ(parser.ParseJson(""), false); + TEST_EQ(parser.ParseJson("{name: 1}"), false); + TEST_EQ(parser.ParseJson("{name:+1}"), false); + TEST_EQ(parser.ParseJson("{name:-1}"), false); + TEST_EQ(parser.ParseJson("{name:-f}"), false); + TEST_EQ(parser.ParseJson("{name:+f}"), false); +} + int FlatBufferTests() { // clang-format off @@ -3187,6 +3838,7 @@ int FlatBufferTests() { ObjectFlatBuffersTest(flatbuf.data()); MiniReflectFlatBuffersTest(flatbuf.data()); + MiniReflectFixedLengthArrayTest(); SizePrefixedTest(); @@ -3201,11 +3853,14 @@ int FlatBufferTests() { FixedLengthArrayJsonTest(true); ReflectionTest(flatbuf.data(), flatbuf.size()); ParseProtoTest(); + ParseProtoTestWithSuffix(); ParseProtoTestWithIncludes(); EvolutionTest(); + UnionDeprecationTest(); UnionVectorTest(); LoadVerifyBinaryTest(); GenerateTableTextTest(); + TestEmbeddedBinarySchema(); #endif // clang-format on @@ -3237,6 +3892,7 @@ int FlatBufferTests() { JsonDefaultTest(); JsonEnumsTest(); FlexBuffersTest(); + FlexBuffersDeprecatedTest(); UninitializedVectorTest(); EqualOperatorTest(); NumericUtilsTest(); @@ -3246,6 +3902,13 @@ int FlatBufferTests() { TestMonsterExtraFloats(); FixedLengthArrayTest(); NativeTypeTest(); + OptionalScalarsTest(); + ParseFlexbuffersFromJsonWithNullTest(); + FlatbuffersSpanTest(); + FixedLengthArrayConstructorTest(); + FieldIdentifierTest(); + StringVectorDefaultsTest(); + ParseIncorrectMonsterJsonTest(); return 0; }