...

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

Documentation: github.com/google/flatbuffers/tests

     1#include "evolution_test.h"
     2
     3#include "evolution_test/evolution_v1_generated.h"
     4#include "evolution_test/evolution_v2_generated.h"
     5#include "flatbuffers/idl.h"
     6#include "test_assert.h"
     7
     8namespace flatbuffers {
     9namespace tests {
    10
    11void EvolutionTest(const std::string &tests_data_path) {
    12  // VS10 does not support typed enums, exclude from tests
    13#if !defined(_MSC_VER) || _MSC_VER >= 1700
    14  const int NUM_VERSIONS = 2;
    15  std::string schemas[NUM_VERSIONS];
    16  std::string jsonfiles[NUM_VERSIONS];
    17  std::vector<uint8_t> binaries[NUM_VERSIONS];
    18
    19  flatbuffers::IDLOptions idl_opts;
    20  idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
    21  flatbuffers::Parser parser(idl_opts);
    22
    23  // Load all the schema versions and their associated data.
    24  for (int i = 0; i < NUM_VERSIONS; ++i) {
    25    std::string schema = tests_data_path + "evolution_test/evolution_v" +
    26                         flatbuffers::NumToString(i + 1) + ".fbs";
    27    TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i]));
    28    std::string json = tests_data_path + "evolution_test/evolution_v" +
    29                       flatbuffers::NumToString(i + 1) + ".json";
    30    TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i]));
    31
    32    TEST_ASSERT(parser.Parse(schemas[i].c_str()));
    33    TEST_ASSERT(parser.Parse(jsonfiles[i].c_str()));
    34
    35    auto bufLen = parser.builder_.GetSize();
    36    auto buf = parser.builder_.GetBufferPointer();
    37    binaries[i].reserve(bufLen);
    38    std::copy(buf, buf + bufLen, std::back_inserter(binaries[i]));
    39  }
    40
    41  // Assert that all the verifiers for the different schema versions properly
    42  // verify any version data.
    43  for (int i = 0; i < NUM_VERSIONS; ++i) {
    44    flatbuffers::Verifier verifier(&binaries[i].front(), binaries[i].size());
    45    TEST_ASSERT(Evolution::V1::VerifyRootBuffer(verifier));
    46    TEST_ASSERT(Evolution::V2::VerifyRootBuffer(verifier));
    47  }
    48
    49  // Test backwards compatibility by reading old data with an evolved schema.
    50  auto root_v1_viewed_from_v2 = Evolution::V2::GetRoot(&binaries[0].front());
    51  // field 'k' is new in version 2, so it should be null.
    52  TEST_ASSERT(nullptr == root_v1_viewed_from_v2->k());
    53  // field 'l' is new in version 2 with a default of 56.
    54  TEST_EQ(root_v1_viewed_from_v2->l(), 56);
    55  // field 'c' of 'TableA' is new in version 2, so it should be null.
    56  TEST_ASSERT(nullptr == root_v1_viewed_from_v2->e()->c());
    57  // 'TableC' was added to field 'c' union in version 2, so it should be null.
    58  TEST_ASSERT(nullptr == root_v1_viewed_from_v2->c_as_TableC());
    59  // The field 'c' union should be of type 'TableB' regardless of schema version
    60  TEST_ASSERT(root_v1_viewed_from_v2->c_type() == Evolution::V2::Union::TableB);
    61  // The field 'f' was renamed to 'ff' in version 2, it should still be
    62  // readable.
    63  TEST_EQ(root_v1_viewed_from_v2->ff()->a(), 16);
    64
    65  // Test forwards compatibility by reading new data with an old schema.
    66  auto root_v2_viewed_from_v1 = Evolution::V1::GetRoot(&binaries[1].front());
    67  // The field 'c' union in version 2 is a new table (index = 3) and should
    68  // still be accessible, but not interpretable.
    69  TEST_EQ(static_cast<uint8_t>(root_v2_viewed_from_v1->c_type()), 3);
    70  TEST_NOTNULL(root_v2_viewed_from_v1->c());
    71  // The field 'd' enum in verison 2 has new members and should still be
    72  // accessible, but not interpretable.
    73  TEST_EQ(static_cast<int8_t>(root_v2_viewed_from_v1->d()), 3);
    74  // The field 'a' in version 2 is deprecated and should return the default
    75  // value (0) instead of the value stored in the in the buffer (42).
    76  TEST_EQ(root_v2_viewed_from_v1->a(), 0);
    77  // The field 'ff' was originally named 'f' in version 1, it should still be
    78  // readable.
    79  TEST_EQ(root_v2_viewed_from_v1->f()->a(), 35);
    80#endif
    81}
    82
    83void ConformTest() {
    84  const char ref[] = "table T { A:int; } enum E:byte { A }";
    85
    86  auto test_conform = [](const char *ref, const char *test,
    87                         const char *expected_err) {
    88    flatbuffers::Parser parser1;
    89    TEST_EQ(parser1.Parse(ref), true);
    90    flatbuffers::Parser parser2;
    91    TEST_EQ(parser2.Parse(test), true);
    92    auto err = parser2.ConformTo(parser1);
    93    if (*expected_err == '\0') {
    94      TEST_EQ_STR(err.c_str(), expected_err);
    95    } else {
    96      TEST_NOTNULL(strstr(err.c_str(), expected_err));
    97    }
    98  };
    99
   100  test_conform(ref, "table T { A:byte; }", "types differ for field: T.A");
   101  test_conform(ref, "table T { B:int; A:int; }",
   102               "offsets differ for field: T.A");
   103  test_conform(ref, "table T { A:int = 1; }", "defaults differ for field: T.A");
   104  test_conform(ref, "table T { B:float; }",
   105               "field renamed to different type: T.B (renamed from T.A)");
   106  test_conform(ref, "enum E:byte { B, A }", "values differ for enum: A");
   107  test_conform(ref, "table T { }", "field deleted: T.A");
   108  test_conform(ref, "table T { B:int; }", "");  // renaming a field is allowed
   109
   110  const char ref2[] = "enum E:byte { A } table T2 { f:E; } ";
   111  test_conform(ref2, "enum E:int32 { A } table T2 { df:byte; f:E; }",
   112               "field renamed to different type: T2.df (renamed from T2.f)");
   113
   114  // Check enum underlying type changes.
   115  test_conform("enum E:int32 {A}", "enum E: byte {A}", "underlying type differ for enum: E");
   116  
   117  // Check union underlying type changes.
   118  const char ref3[] = "table A {} table B {} union C {A, B}";
   119  test_conform(ref3, "table A {} table B {} union C:int32 {A, B}", "underlying type differ for union: C");
   120
   121  // Check conformity for Offset64-related changes.
   122  {
   123    const char ref[] = "table T { a:[uint8]; b:string; }";
   124
   125    // Adding a 'vector64' changes the type.
   126    test_conform(ref, "table T { a:[uint8] (vector64); b:string; }",
   127                 "types differ for field: T.a");
   128
   129    // Adding a 'offset64' to the vector changes the type.
   130    test_conform(ref, "table T { a:[uint8] (offset64); b:string; }",
   131                 "offset types differ for field: T.a");
   132
   133    // Adding a 'offset64' to the string also changes the type.
   134    test_conform(ref, "table T { a:[uint8]; b:string (offset64); }",
   135                 "offset types differ for field: T.b");
   136
   137    // Now try the opposite direction of removing an attribute from an existing
   138    // field.
   139
   140    // Removing a 'vector64' changes the type.
   141    test_conform("table T { a:[uint8] (vector64); b:string; }", ref,
   142                 "types differ for field: T.a");
   143
   144    // Removing a 'offset64' to the string also changes the type.
   145    test_conform("table T { a:[uint8] (offset64); b:string; }", ref,
   146                 "offset types differ for field: T.a");
   147
   148    // Remove a 'offset64' to the string also changes the type.
   149    test_conform("table T { a:[uint8]; b:string (offset64); }", ref,
   150                 "offset types differ for field: T.b");
   151  }
   152}
   153
   154void UnionDeprecationTest(const std::string &tests_data_path) {
   155  const int NUM_VERSIONS = 2;
   156  std::string schemas[NUM_VERSIONS];
   157  std::string jsonfiles[NUM_VERSIONS];
   158  std::vector<uint8_t> binaries[NUM_VERSIONS];
   159
   160  flatbuffers::IDLOptions idl_opts;
   161  idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary;
   162  flatbuffers::Parser parser(idl_opts);
   163
   164  // Load all the schema versions and their associated data.
   165  for (int i = 0; i < NUM_VERSIONS; ++i) {
   166    std::string schema = tests_data_path + "evolution_test/evolution_v" +
   167                         flatbuffers::NumToString(i + 1) + ".fbs";
   168    TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i]));
   169    std::string json = tests_data_path + "evolution_test/evolution_v" +
   170                       flatbuffers::NumToString(i + 1) + ".json";
   171    TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i]));
   172
   173    TEST_ASSERT(parser.Parse(schemas[i].c_str()));
   174    TEST_ASSERT(parser.Parse(jsonfiles[i].c_str()));
   175
   176    auto bufLen = parser.builder_.GetSize();
   177    auto buf = parser.builder_.GetBufferPointer();
   178    binaries[i].reserve(bufLen);
   179    std::copy(buf, buf + bufLen, std::back_inserter(binaries[i]));
   180  }
   181
   182  auto v2 = parser.LookupStruct("Evolution.V2.Root");
   183  TEST_NOTNULL(v2);
   184  auto j_type_field = v2->fields.Lookup("j_type");
   185  TEST_NOTNULL(j_type_field);
   186  TEST_ASSERT(j_type_field->deprecated);
   187}
   188
   189}  // namespace tests
   190}  // namespace flatbuffers

View as plain text