Commit dda09502 authored by Derek Bailey's avatar Derek Bailey Committed by Wouter van Oortmerssen

[C++] Adds basic schema evolution tests (#5611)

* Added basic schema evolution tests

* Add BUILD targets for evolution tests. Added to test/generate_code scripts

* Use vector.front() instead of vector.data()

* Added --scoped-enums option for evolution test
parent adbcbba5
......@@ -139,6 +139,8 @@ cc_test(
"src/idl_parser.cpp",
"src/reflection.cpp",
"src/util.cpp",
"tests/evolution_test/evolution_v1_generated.h",
"tests/evolution_test/evolution_v2_generated.h",
"tests/namespace_test/namespace_test1_generated.h",
"tests/namespace_test/namespace_test2_generated.h",
"tests/native_type_test_impl.cpp",
......@@ -159,6 +161,10 @@ cc_test(
":tests/arrays_test.bfbs",
":tests/arrays_test.fbs",
":tests/arrays_test.golden",
":tests/evolution_test/evolution_v1.fbs",
":tests/evolution_test/evolution_v1.json",
":tests/evolution_test/evolution_v2.fbs",
":tests/evolution_test/evolution_v2.json",
":tests/include_test/include_test1.fbs",
":tests/include_test/sub/include_test2.fbs",
":tests/monster_extra.fbs",
......
namespace Evolution.V1;
table TableA {
a:float;
b:int;
}
table TableB {
a:int;
}
enum Enum : byte {
King,
Queen
}
union Union {
TableA,
TableB
}
struct Struct {
a:int;
b:double;
}
table Root {
a:int;
b:bool;
c:Union;
d:Enum;
e:TableA;
f:Struct;
g:[int];
h:[TableB];
i:int = 1234;
}
root_type Root;
\ No newline at end of file
{
"a": 42,
"b": true,
"c_type": "TableB",
"c": {
"a": 15
},
"d": "King",
"e": {
"a": 3.1452,
"b": 325
},
"f":{
"a": 16,
"b": 243.980943
},
"g": [ 7, 8, 9],
"h": [
{
"a": 212
},
{
"a": 459
}
]
}
\ No newline at end of file
This diff is collapsed.
namespace Evolution.V2;
table TableA {
b:int (id: 1); // swapped with 'a'
a:float (id: 0); // swapped with 'b'
c:string (id: 2); // new in v2
}
table TableB {
a:int;
}
table TableC { // new in v2
a:double;
b:string;
}
enum Enum : byte {
King,
Queen,
Rook, // new in v2
Bishop // new in v2
}
union Union {
TableA,
TableB,
TableC
}
struct Struct {
a:int;
b:double;
}
table Root {
a:int (deprecated); // deprecated in v2
b:bool;
c:Union;
d:Enum;
e:TableA;
ff:Struct; // renamed from 'f' in v1
g:[int];
h:[TableB];
i:uint = 1234;
j:TableC; // new in v2
k:uint8 = 56; // new in v2
}
root_type Root;
\ No newline at end of file
{
"b": false,
"c_type": "TableC",
"c": {
"a": 984.2494
},
"d": "Bishop",
"e": {
"a": 3.1452,
"b": 435,
"c": "yummy yummy fig bar bar"
},
"ff":{
"a": 35,
"b": 243.980943
},
"g": [ 7, 8, 10],
"h": [
{
"a": 212
},
{
"a": 459
},
{
"a": 333
}
],
"i": 4321,
"j": {
"a": 9874.342,
"b": "more please"
}
}
This diff is collapsed.
......@@ -18,6 +18,7 @@ if "%1"=="-b" set buildtype=%2
..\%buildtype%\flatc.exe --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --grpc --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json || goto FAIL
..\%buildtype%\flatc.exe --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs || goto FAIL
..\%buildtype%\flatc.exe --cpp --java --csharp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs || goto FAIL
..\%buildtype%\flatc.exe --cpp --scoped-enums -o evolution_test ./evolution_test/evolution_v1.fbs ./evolution_test/evolution_v2.fbs|| goto FAIL
..\%buildtype%\flatc.exe -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs || goto FAIL
..\%buildtype%\flatc.exe -b --schema --bfbs-comments --bfbs-builtins -I include_test arrays_test.fbs || goto FAIL
..\%buildtype%\flatc.exe --jsonschema --schema -I include_test monster_test.fbs || goto FAIL
......
......@@ -18,6 +18,7 @@ set -e
../flatc --cpp --java --kotlin --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --grpc --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
../flatc --cpp --java --kotlin --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
../flatc --cpp --java --kotlin --csharp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs
../flatc --cpp --scoped-enums -o evolution_test ./evolution_test/evolution_v*.fbs
../flatc -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs
../flatc -b --schema --bfbs-comments --bfbs-builtins -I include_test arrays_test.fbs
../flatc --jsonschema --schema -I include_test monster_test.fbs
......
......@@ -37,6 +37,8 @@
#include "monster_extra_generated.h"
#if !defined(_MSC_VER) || _MSC_VER >= 1700
# include "arrays_test_generated.h"
# include "evolution_test/evolution_v1_generated.h"
# include "evolution_test/evolution_v2_generated.h"
#endif
#include "native_type_test_generated.h"
......@@ -2310,6 +2312,76 @@ void InvalidNestedFlatbufferTest() {
false);
}
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<uint8_t> binaries[NUM_VERSIONS];
flatbuffers::Verifier *verifiers[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]));
verifiers[i] = new flatbuffers::Verifier(&binaries[i].front(), bufLen);
}
// Assert that all the verifiers for the different schema versions properly verify any version data.
for (int i = 0; i < NUM_VERSIONS; ++i) {
TEST_ASSERT(Evolution::V1::VerifyRootBuffer(*verifiers[i]));
TEST_ASSERT(Evolution::V2::VerifyRootBuffer(*verifiers[i]));
}
// 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_EQ(root_v1_viewed_from_v2->j(), NULL);
// 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_EQ(root_v1_viewed_from_v2->e()->c(), NULL);
// 'TableC' was added to field 'c' union in version 2, so it should be null.
TEST_EQ(root_v1_viewed_from_v2->c_as_TableC(), NULL);
// 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<uint8_t>(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<int8_t>(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 UnionVectorTest() {
// load FlatBuffer fbs schema and json.
std::string schemafile, jsonfile;
......@@ -3070,6 +3142,7 @@ int FlatBufferTests() {
FixedLengthArrayJsonTest(true);
ReflectionTest(flatbuf.data(), flatbuf.size());
ParseProtoTest();
EvolutionTest();
UnionVectorTest();
LoadVerifyBinaryTest();
GenerateTableTextTest();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment