Austin Schuh | 2dd86a9 | 2022-09-14 21:19:23 -0700 | [diff] [blame^] | 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 | |
| 8 | namespace flatbuffers { |
| 9 | namespace tests { |
| 10 | |
| 11 | void 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 | |
| 83 | void ConformTest() { |
| 84 | flatbuffers::Parser parser; |
| 85 | TEST_EQ(parser.Parse("table T { A:int; } enum E:byte { A }"), true); |
| 86 | |
| 87 | auto test_conform = [](flatbuffers::Parser &parser1, const char *test, |
| 88 | const char *expected_err) { |
| 89 | flatbuffers::Parser parser2; |
| 90 | TEST_EQ(parser2.Parse(test), true); |
| 91 | auto err = parser2.ConformTo(parser1); |
| 92 | TEST_NOTNULL(strstr(err.c_str(), expected_err)); |
| 93 | }; |
| 94 | |
| 95 | test_conform(parser, "table T { A:byte; }", "types differ for field"); |
| 96 | test_conform(parser, "table T { B:int; A:int; }", "offsets differ for field"); |
| 97 | test_conform(parser, "table T { A:int = 1; }", "defaults differ for field"); |
| 98 | test_conform(parser, "table T { B:float; }", |
| 99 | "field renamed to different type"); |
| 100 | test_conform(parser, "enum E:byte { B, A }", "values differ for enum"); |
| 101 | } |
| 102 | |
| 103 | void UnionDeprecationTest(const std::string& tests_data_path) { |
| 104 | const int NUM_VERSIONS = 2; |
| 105 | std::string schemas[NUM_VERSIONS]; |
| 106 | std::string jsonfiles[NUM_VERSIONS]; |
| 107 | std::vector<uint8_t> binaries[NUM_VERSIONS]; |
| 108 | |
| 109 | flatbuffers::IDLOptions idl_opts; |
| 110 | idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary; |
| 111 | flatbuffers::Parser parser(idl_opts); |
| 112 | |
| 113 | // Load all the schema versions and their associated data. |
| 114 | for (int i = 0; i < NUM_VERSIONS; ++i) { |
| 115 | std::string schema = tests_data_path + "evolution_test/evolution_v" + |
| 116 | flatbuffers::NumToString(i + 1) + ".fbs"; |
| 117 | TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i])); |
| 118 | std::string json = tests_data_path + "evolution_test/evolution_v" + |
| 119 | flatbuffers::NumToString(i + 1) + ".json"; |
| 120 | TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i])); |
| 121 | |
| 122 | TEST_ASSERT(parser.Parse(schemas[i].c_str())); |
| 123 | TEST_ASSERT(parser.Parse(jsonfiles[i].c_str())); |
| 124 | |
| 125 | auto bufLen = parser.builder_.GetSize(); |
| 126 | auto buf = parser.builder_.GetBufferPointer(); |
| 127 | binaries[i].reserve(bufLen); |
| 128 | std::copy(buf, buf + bufLen, std::back_inserter(binaries[i])); |
| 129 | } |
| 130 | |
| 131 | auto v2 = parser.LookupStruct("Evolution.V2.Root"); |
| 132 | TEST_NOTNULL(v2); |
| 133 | auto j_type_field = v2->fields.Lookup("j_type"); |
| 134 | TEST_NOTNULL(j_type_field); |
| 135 | TEST_ASSERT(j_type_field->deprecated); |
| 136 | } |
| 137 | |
| 138 | } // namespace tests |
| 139 | } // namespace flatbuffers |