[C++] Adds basic schema evolution tests (#5611)
authorDerek Bailey <dbaileychess@gmail.com>
Thu, 14 Nov 2019 23:44:18 +0000 (15:44 -0800)
committerWouter van Oortmerssen <aardappel@gmail.com>
Thu, 14 Nov 2019 23:44:18 +0000 (15:44 -0800)
* 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

BUILD
tests/evolution_test/evolution_v1.fbs [new file with mode: 0644]
tests/evolution_test/evolution_v1.json [new file with mode: 0644]
tests/evolution_test/evolution_v1_generated.h [new file with mode: 0644]
tests/evolution_test/evolution_v2.fbs [new file with mode: 0644]
tests/evolution_test/evolution_v2.json [new file with mode: 0644]
tests/evolution_test/evolution_v2_generated.h [new file with mode: 0644]
tests/generate_code.bat
tests/generate_code.sh
tests/test.cpp

diff --git a/BUILD b/BUILD
index 210e104..b697602 100644 (file)
--- a/BUILD
+++ b/BUILD
@@ -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",
diff --git a/tests/evolution_test/evolution_v1.fbs b/tests/evolution_test/evolution_v1.fbs
new file mode 100644 (file)
index 0000000..2576c87
--- /dev/null
@@ -0,0 +1,39 @@
+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
diff --git a/tests/evolution_test/evolution_v1.json b/tests/evolution_test/evolution_v1.json
new file mode 100644 (file)
index 0000000..8b4b352
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "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
diff --git a/tests/evolution_test/evolution_v1_generated.h b/tests/evolution_test/evolution_v1_generated.h
new file mode 100644 (file)
index 0000000..1d447ff
--- /dev/null
@@ -0,0 +1,453 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+
+#ifndef FLATBUFFERS_GENERATED_EVOLUTIONV1_EVOLUTION_V1_H_
+#define FLATBUFFERS_GENERATED_EVOLUTIONV1_EVOLUTION_V1_H_
+
+#include "flatbuffers/flatbuffers.h"
+
+namespace Evolution {
+namespace V1 {
+
+struct TableA;
+
+struct TableB;
+
+struct Struct;
+
+struct Root;
+
+enum class Enum : int8_t {
+  King = 0,
+  Queen = 1,
+  MIN = King,
+  MAX = Queen
+};
+
+inline const Enum (&EnumValuesEnum())[2] {
+  static const Enum values[] = {
+    Enum::King,
+    Enum::Queen
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesEnum() {
+  static const char * const names[3] = {
+    "King",
+    "Queen",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameEnum(Enum e) {
+  if (e < Enum::King || e > Enum::Queen) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesEnum()[index];
+}
+
+enum class Union : uint8_t {
+  NONE = 0,
+  TableA = 1,
+  TableB = 2,
+  MIN = NONE,
+  MAX = TableB
+};
+
+inline const Union (&EnumValuesUnion())[3] {
+  static const Union values[] = {
+    Union::NONE,
+    Union::TableA,
+    Union::TableB
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesUnion() {
+  static const char * const names[4] = {
+    "NONE",
+    "TableA",
+    "TableB",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameUnion(Union e) {
+  if (e < Union::NONE || e > Union::TableB) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesUnion()[index];
+}
+
+template<typename T> struct UnionTraits {
+  static const Union enum_value = Union::NONE;
+};
+
+template<> struct UnionTraits<Evolution::V1::TableA> {
+  static const Union enum_value = Union::TableA;
+};
+
+template<> struct UnionTraits<Evolution::V1::TableB> {
+  static const Union enum_value = Union::TableB;
+};
+
+bool VerifyUnion(flatbuffers::Verifier &verifier, const void *obj, Union type);
+bool VerifyUnionVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
+
+FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Struct FLATBUFFERS_FINAL_CLASS {
+ private:
+  int32_t a_;
+  int32_t padding0__;
+  double b_;
+
+ public:
+  Struct() {
+    memset(static_cast<void *>(this), 0, sizeof(Struct));
+  }
+  Struct(int32_t _a, double _b)
+      : a_(flatbuffers::EndianScalar(_a)),
+        padding0__(0),
+        b_(flatbuffers::EndianScalar(_b)) {
+    (void)padding0__;
+  }
+  int32_t a() const {
+    return flatbuffers::EndianScalar(a_);
+  }
+  double b() const {
+    return flatbuffers::EndianScalar(b_);
+  }
+};
+FLATBUFFERS_STRUCT_END(Struct, 16);
+
+struct TableA FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_A = 4,
+    VT_B = 6
+  };
+  float a() const {
+    return GetField<float>(VT_A, 0.0f);
+  }
+  int32_t b() const {
+    return GetField<int32_t>(VT_B, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<float>(verifier, VT_A) &&
+           VerifyField<int32_t>(verifier, VT_B) &&
+           verifier.EndTable();
+  }
+};
+
+struct TableABuilder {
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_a(float a) {
+    fbb_.AddElement<float>(TableA::VT_A, a, 0.0f);
+  }
+  void add_b(int32_t b) {
+    fbb_.AddElement<int32_t>(TableA::VT_B, b, 0);
+  }
+  explicit TableABuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  TableABuilder &operator=(const TableABuilder &);
+  flatbuffers::Offset<TableA> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<TableA>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<TableA> CreateTableA(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    float a = 0.0f,
+    int32_t b = 0) {
+  TableABuilder builder_(_fbb);
+  builder_.add_b(b);
+  builder_.add_a(a);
+  return builder_.Finish();
+}
+
+struct TableB FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_A = 4
+  };
+  int32_t a() const {
+    return GetField<int32_t>(VT_A, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int32_t>(verifier, VT_A) &&
+           verifier.EndTable();
+  }
+};
+
+struct TableBBuilder {
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_a(int32_t a) {
+    fbb_.AddElement<int32_t>(TableB::VT_A, a, 0);
+  }
+  explicit TableBBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  TableBBuilder &operator=(const TableBBuilder &);
+  flatbuffers::Offset<TableB> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<TableB>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<TableB> CreateTableB(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int32_t a = 0) {
+  TableBBuilder builder_(_fbb);
+  builder_.add_a(a);
+  return builder_.Finish();
+}
+
+struct Root FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_A = 4,
+    VT_B = 6,
+    VT_C_TYPE = 8,
+    VT_C = 10,
+    VT_D = 12,
+    VT_E = 14,
+    VT_F = 16,
+    VT_G = 18,
+    VT_H = 20,
+    VT_I = 22
+  };
+  int32_t a() const {
+    return GetField<int32_t>(VT_A, 0);
+  }
+  bool b() const {
+    return GetField<uint8_t>(VT_B, 0) != 0;
+  }
+  Evolution::V1::Union c_type() const {
+    return static_cast<Evolution::V1::Union>(GetField<uint8_t>(VT_C_TYPE, 0));
+  }
+  const void *c() const {
+    return GetPointer<const void *>(VT_C);
+  }
+  template<typename T> const T *c_as() const;
+  const Evolution::V1::TableA *c_as_TableA() const {
+    return c_type() == Evolution::V1::Union::TableA ? static_cast<const Evolution::V1::TableA *>(c()) : nullptr;
+  }
+  const Evolution::V1::TableB *c_as_TableB() const {
+    return c_type() == Evolution::V1::Union::TableB ? static_cast<const Evolution::V1::TableB *>(c()) : nullptr;
+  }
+  Evolution::V1::Enum d() const {
+    return static_cast<Evolution::V1::Enum>(GetField<int8_t>(VT_D, 0));
+  }
+  const Evolution::V1::TableA *e() const {
+    return GetPointer<const Evolution::V1::TableA *>(VT_E);
+  }
+  const Evolution::V1::Struct *f() const {
+    return GetStruct<const Evolution::V1::Struct *>(VT_F);
+  }
+  const flatbuffers::Vector<int32_t> *g() const {
+    return GetPointer<const flatbuffers::Vector<int32_t> *>(VT_G);
+  }
+  const flatbuffers::Vector<flatbuffers::Offset<Evolution::V1::TableB>> *h() const {
+    return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Evolution::V1::TableB>> *>(VT_H);
+  }
+  int32_t i() const {
+    return GetField<int32_t>(VT_I, 1234);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int32_t>(verifier, VT_A) &&
+           VerifyField<uint8_t>(verifier, VT_B) &&
+           VerifyField<uint8_t>(verifier, VT_C_TYPE) &&
+           VerifyOffset(verifier, VT_C) &&
+           VerifyUnion(verifier, c(), c_type()) &&
+           VerifyField<int8_t>(verifier, VT_D) &&
+           VerifyOffset(verifier, VT_E) &&
+           verifier.VerifyTable(e()) &&
+           VerifyField<Evolution::V1::Struct>(verifier, VT_F) &&
+           VerifyOffset(verifier, VT_G) &&
+           verifier.VerifyVector(g()) &&
+           VerifyOffset(verifier, VT_H) &&
+           verifier.VerifyVector(h()) &&
+           verifier.VerifyVectorOfTables(h()) &&
+           VerifyField<int32_t>(verifier, VT_I) &&
+           verifier.EndTable();
+  }
+};
+
+template<> inline const Evolution::V1::TableA *Root::c_as<Evolution::V1::TableA>() const {
+  return c_as_TableA();
+}
+
+template<> inline const Evolution::V1::TableB *Root::c_as<Evolution::V1::TableB>() const {
+  return c_as_TableB();
+}
+
+struct RootBuilder {
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_a(int32_t a) {
+    fbb_.AddElement<int32_t>(Root::VT_A, a, 0);
+  }
+  void add_b(bool b) {
+    fbb_.AddElement<uint8_t>(Root::VT_B, static_cast<uint8_t>(b), 0);
+  }
+  void add_c_type(Evolution::V1::Union c_type) {
+    fbb_.AddElement<uint8_t>(Root::VT_C_TYPE, static_cast<uint8_t>(c_type), 0);
+  }
+  void add_c(flatbuffers::Offset<void> c) {
+    fbb_.AddOffset(Root::VT_C, c);
+  }
+  void add_d(Evolution::V1::Enum d) {
+    fbb_.AddElement<int8_t>(Root::VT_D, static_cast<int8_t>(d), 0);
+  }
+  void add_e(flatbuffers::Offset<Evolution::V1::TableA> e) {
+    fbb_.AddOffset(Root::VT_E, e);
+  }
+  void add_f(const Evolution::V1::Struct *f) {
+    fbb_.AddStruct(Root::VT_F, f);
+  }
+  void add_g(flatbuffers::Offset<flatbuffers::Vector<int32_t>> g) {
+    fbb_.AddOffset(Root::VT_G, g);
+  }
+  void add_h(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Evolution::V1::TableB>>> h) {
+    fbb_.AddOffset(Root::VT_H, h);
+  }
+  void add_i(int32_t i) {
+    fbb_.AddElement<int32_t>(Root::VT_I, i, 1234);
+  }
+  explicit RootBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  RootBuilder &operator=(const RootBuilder &);
+  flatbuffers::Offset<Root> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<Root>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<Root> CreateRoot(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int32_t a = 0,
+    bool b = false,
+    Evolution::V1::Union c_type = Evolution::V1::Union::NONE,
+    flatbuffers::Offset<void> c = 0,
+    Evolution::V1::Enum d = Evolution::V1::Enum::King,
+    flatbuffers::Offset<Evolution::V1::TableA> e = 0,
+    const Evolution::V1::Struct *f = 0,
+    flatbuffers::Offset<flatbuffers::Vector<int32_t>> g = 0,
+    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Evolution::V1::TableB>>> h = 0,
+    int32_t i = 1234) {
+  RootBuilder builder_(_fbb);
+  builder_.add_i(i);
+  builder_.add_h(h);
+  builder_.add_g(g);
+  builder_.add_f(f);
+  builder_.add_e(e);
+  builder_.add_c(c);
+  builder_.add_a(a);
+  builder_.add_d(d);
+  builder_.add_c_type(c_type);
+  builder_.add_b(b);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<Root> CreateRootDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int32_t a = 0,
+    bool b = false,
+    Evolution::V1::Union c_type = Evolution::V1::Union::NONE,
+    flatbuffers::Offset<void> c = 0,
+    Evolution::V1::Enum d = Evolution::V1::Enum::King,
+    flatbuffers::Offset<Evolution::V1::TableA> e = 0,
+    const Evolution::V1::Struct *f = 0,
+    const std::vector<int32_t> *g = nullptr,
+    const std::vector<flatbuffers::Offset<Evolution::V1::TableB>> *h = nullptr,
+    int32_t i = 1234) {
+  auto g__ = g ? _fbb.CreateVector<int32_t>(*g) : 0;
+  auto h__ = h ? _fbb.CreateVector<flatbuffers::Offset<Evolution::V1::TableB>>(*h) : 0;
+  return Evolution::V1::CreateRoot(
+      _fbb,
+      a,
+      b,
+      c_type,
+      c,
+      d,
+      e,
+      f,
+      g__,
+      h__,
+      i);
+}
+
+inline bool VerifyUnion(flatbuffers::Verifier &verifier, const void *obj, Union type) {
+  switch (type) {
+    case Union::NONE: {
+      return true;
+    }
+    case Union::TableA: {
+      auto ptr = reinterpret_cast<const Evolution::V1::TableA *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case Union::TableB: {
+      auto ptr = reinterpret_cast<const Evolution::V1::TableB *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    default: return true;
+  }
+}
+
+inline bool VerifyUnionVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
+  if (!values || !types) return !values && !types;
+  if (values->size() != types->size()) return false;
+  for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
+    if (!VerifyUnion(
+        verifier,  values->Get(i), types->GetEnum<Union>(i))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+inline const Evolution::V1::Root *GetRoot(const void *buf) {
+  return flatbuffers::GetRoot<Evolution::V1::Root>(buf);
+}
+
+inline const Evolution::V1::Root *GetSizePrefixedRoot(const void *buf) {
+  return flatbuffers::GetSizePrefixedRoot<Evolution::V1::Root>(buf);
+}
+
+inline bool VerifyRootBuffer(
+    flatbuffers::Verifier &verifier) {
+  return verifier.VerifyBuffer<Evolution::V1::Root>(nullptr);
+}
+
+inline bool VerifySizePrefixedRootBuffer(
+    flatbuffers::Verifier &verifier) {
+  return verifier.VerifySizePrefixedBuffer<Evolution::V1::Root>(nullptr);
+}
+
+inline void FinishRootBuffer(
+    flatbuffers::FlatBufferBuilder &fbb,
+    flatbuffers::Offset<Evolution::V1::Root> root) {
+  fbb.Finish(root);
+}
+
+inline void FinishSizePrefixedRootBuffer(
+    flatbuffers::FlatBufferBuilder &fbb,
+    flatbuffers::Offset<Evolution::V1::Root> root) {
+  fbb.FinishSizePrefixed(root);
+}
+
+}  // namespace V1
+}  // namespace Evolution
+
+#endif  // FLATBUFFERS_GENERATED_EVOLUTIONV1_EVOLUTION_V1_H_
diff --git a/tests/evolution_test/evolution_v2.fbs b/tests/evolution_test/evolution_v2.fbs
new file mode 100644 (file)
index 0000000..5f4601e
--- /dev/null
@@ -0,0 +1,50 @@
+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
diff --git a/tests/evolution_test/evolution_v2.json b/tests/evolution_test/evolution_v2.json
new file mode 100644 (file)
index 0000000..625f506
--- /dev/null
@@ -0,0 +1,34 @@
+{
+  "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"
+  }
+}
diff --git a/tests/evolution_test/evolution_v2_generated.h b/tests/evolution_test/evolution_v2_generated.h
new file mode 100644 (file)
index 0000000..4e1b840
--- /dev/null
@@ -0,0 +1,578 @@
+// automatically generated by the FlatBuffers compiler, do not modify
+
+
+#ifndef FLATBUFFERS_GENERATED_EVOLUTIONV2_EVOLUTION_V2_H_
+#define FLATBUFFERS_GENERATED_EVOLUTIONV2_EVOLUTION_V2_H_
+
+#include "flatbuffers/flatbuffers.h"
+
+namespace Evolution {
+namespace V2 {
+
+struct TableA;
+
+struct TableB;
+
+struct TableC;
+
+struct Struct;
+
+struct Root;
+
+enum class Enum : int8_t {
+  King = 0,
+  Queen = 1,
+  Rook = 2,
+  Bishop = 3,
+  MIN = King,
+  MAX = Bishop
+};
+
+inline const Enum (&EnumValuesEnum())[4] {
+  static const Enum values[] = {
+    Enum::King,
+    Enum::Queen,
+    Enum::Rook,
+    Enum::Bishop
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesEnum() {
+  static const char * const names[5] = {
+    "King",
+    "Queen",
+    "Rook",
+    "Bishop",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameEnum(Enum e) {
+  if (e < Enum::King || e > Enum::Bishop) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesEnum()[index];
+}
+
+enum class Union : uint8_t {
+  NONE = 0,
+  TableA = 1,
+  TableB = 2,
+  TableC = 3,
+  MIN = NONE,
+  MAX = TableC
+};
+
+inline const Union (&EnumValuesUnion())[4] {
+  static const Union values[] = {
+    Union::NONE,
+    Union::TableA,
+    Union::TableB,
+    Union::TableC
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesUnion() {
+  static const char * const names[5] = {
+    "NONE",
+    "TableA",
+    "TableB",
+    "TableC",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNameUnion(Union e) {
+  if (e < Union::NONE || e > Union::TableC) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesUnion()[index];
+}
+
+template<typename T> struct UnionTraits {
+  static const Union enum_value = Union::NONE;
+};
+
+template<> struct UnionTraits<Evolution::V2::TableA> {
+  static const Union enum_value = Union::TableA;
+};
+
+template<> struct UnionTraits<Evolution::V2::TableB> {
+  static const Union enum_value = Union::TableB;
+};
+
+template<> struct UnionTraits<Evolution::V2::TableC> {
+  static const Union enum_value = Union::TableC;
+};
+
+bool VerifyUnion(flatbuffers::Verifier &verifier, const void *obj, Union type);
+bool VerifyUnionVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
+
+FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Struct FLATBUFFERS_FINAL_CLASS {
+ private:
+  int32_t a_;
+  int32_t padding0__;
+  double b_;
+
+ public:
+  Struct() {
+    memset(static_cast<void *>(this), 0, sizeof(Struct));
+  }
+  Struct(int32_t _a, double _b)
+      : a_(flatbuffers::EndianScalar(_a)),
+        padding0__(0),
+        b_(flatbuffers::EndianScalar(_b)) {
+    (void)padding0__;
+  }
+  int32_t a() const {
+    return flatbuffers::EndianScalar(a_);
+  }
+  double b() const {
+    return flatbuffers::EndianScalar(b_);
+  }
+};
+FLATBUFFERS_STRUCT_END(Struct, 16);
+
+struct TableA FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_A = 4,
+    VT_B = 6,
+    VT_C = 8
+  };
+  float a() const {
+    return GetField<float>(VT_A, 0.0f);
+  }
+  int32_t b() const {
+    return GetField<int32_t>(VT_B, 0);
+  }
+  const flatbuffers::String *c() const {
+    return GetPointer<const flatbuffers::String *>(VT_C);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<float>(verifier, VT_A) &&
+           VerifyField<int32_t>(verifier, VT_B) &&
+           VerifyOffset(verifier, VT_C) &&
+           verifier.VerifyString(c()) &&
+           verifier.EndTable();
+  }
+};
+
+struct TableABuilder {
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_a(float a) {
+    fbb_.AddElement<float>(TableA::VT_A, a, 0.0f);
+  }
+  void add_b(int32_t b) {
+    fbb_.AddElement<int32_t>(TableA::VT_B, b, 0);
+  }
+  void add_c(flatbuffers::Offset<flatbuffers::String> c) {
+    fbb_.AddOffset(TableA::VT_C, c);
+  }
+  explicit TableABuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  TableABuilder &operator=(const TableABuilder &);
+  flatbuffers::Offset<TableA> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<TableA>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<TableA> CreateTableA(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    float a = 0.0f,
+    int32_t b = 0,
+    flatbuffers::Offset<flatbuffers::String> c = 0) {
+  TableABuilder builder_(_fbb);
+  builder_.add_c(c);
+  builder_.add_b(b);
+  builder_.add_a(a);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<TableA> CreateTableADirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    float a = 0.0f,
+    int32_t b = 0,
+    const char *c = nullptr) {
+  auto c__ = c ? _fbb.CreateString(c) : 0;
+  return Evolution::V2::CreateTableA(
+      _fbb,
+      a,
+      b,
+      c__);
+}
+
+struct TableB FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_A = 4
+  };
+  int32_t a() const {
+    return GetField<int32_t>(VT_A, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<int32_t>(verifier, VT_A) &&
+           verifier.EndTable();
+  }
+};
+
+struct TableBBuilder {
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_a(int32_t a) {
+    fbb_.AddElement<int32_t>(TableB::VT_A, a, 0);
+  }
+  explicit TableBBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  TableBBuilder &operator=(const TableBBuilder &);
+  flatbuffers::Offset<TableB> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<TableB>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<TableB> CreateTableB(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    int32_t a = 0) {
+  TableBBuilder builder_(_fbb);
+  builder_.add_a(a);
+  return builder_.Finish();
+}
+
+struct TableC FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_A = 4,
+    VT_B = 6
+  };
+  double a() const {
+    return GetField<double>(VT_A, 0.0);
+  }
+  const flatbuffers::String *b() const {
+    return GetPointer<const flatbuffers::String *>(VT_B);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<double>(verifier, VT_A) &&
+           VerifyOffset(verifier, VT_B) &&
+           verifier.VerifyString(b()) &&
+           verifier.EndTable();
+  }
+};
+
+struct TableCBuilder {
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_a(double a) {
+    fbb_.AddElement<double>(TableC::VT_A, a, 0.0);
+  }
+  void add_b(flatbuffers::Offset<flatbuffers::String> b) {
+    fbb_.AddOffset(TableC::VT_B, b);
+  }
+  explicit TableCBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  TableCBuilder &operator=(const TableCBuilder &);
+  flatbuffers::Offset<TableC> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<TableC>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<TableC> CreateTableC(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    double a = 0.0,
+    flatbuffers::Offset<flatbuffers::String> b = 0) {
+  TableCBuilder builder_(_fbb);
+  builder_.add_a(a);
+  builder_.add_b(b);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<TableC> CreateTableCDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    double a = 0.0,
+    const char *b = nullptr) {
+  auto b__ = b ? _fbb.CreateString(b) : 0;
+  return Evolution::V2::CreateTableC(
+      _fbb,
+      a,
+      b__);
+}
+
+struct Root FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_B = 6,
+    VT_C_TYPE = 8,
+    VT_C = 10,
+    VT_D = 12,
+    VT_E = 14,
+    VT_FF = 16,
+    VT_G = 18,
+    VT_H = 20,
+    VT_I = 22,
+    VT_J = 24,
+    VT_K = 26
+  };
+  bool b() const {
+    return GetField<uint8_t>(VT_B, 0) != 0;
+  }
+  Evolution::V2::Union c_type() const {
+    return static_cast<Evolution::V2::Union>(GetField<uint8_t>(VT_C_TYPE, 0));
+  }
+  const void *c() const {
+    return GetPointer<const void *>(VT_C);
+  }
+  template<typename T> const T *c_as() const;
+  const Evolution::V2::TableA *c_as_TableA() const {
+    return c_type() == Evolution::V2::Union::TableA ? static_cast<const Evolution::V2::TableA *>(c()) : nullptr;
+  }
+  const Evolution::V2::TableB *c_as_TableB() const {
+    return c_type() == Evolution::V2::Union::TableB ? static_cast<const Evolution::V2::TableB *>(c()) : nullptr;
+  }
+  const Evolution::V2::TableC *c_as_TableC() const {
+    return c_type() == Evolution::V2::Union::TableC ? static_cast<const Evolution::V2::TableC *>(c()) : nullptr;
+  }
+  Evolution::V2::Enum d() const {
+    return static_cast<Evolution::V2::Enum>(GetField<int8_t>(VT_D, 0));
+  }
+  const Evolution::V2::TableA *e() const {
+    return GetPointer<const Evolution::V2::TableA *>(VT_E);
+  }
+  const Evolution::V2::Struct *ff() const {
+    return GetStruct<const Evolution::V2::Struct *>(VT_FF);
+  }
+  const flatbuffers::Vector<int32_t> *g() const {
+    return GetPointer<const flatbuffers::Vector<int32_t> *>(VT_G);
+  }
+  const flatbuffers::Vector<flatbuffers::Offset<Evolution::V2::TableB>> *h() const {
+    return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Evolution::V2::TableB>> *>(VT_H);
+  }
+  uint32_t i() const {
+    return GetField<uint32_t>(VT_I, 1234);
+  }
+  const Evolution::V2::TableC *j() const {
+    return GetPointer<const Evolution::V2::TableC *>(VT_J);
+  }
+  uint8_t k() const {
+    return GetField<uint8_t>(VT_K, 56);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint8_t>(verifier, VT_B) &&
+           VerifyField<uint8_t>(verifier, VT_C_TYPE) &&
+           VerifyOffset(verifier, VT_C) &&
+           VerifyUnion(verifier, c(), c_type()) &&
+           VerifyField<int8_t>(verifier, VT_D) &&
+           VerifyOffset(verifier, VT_E) &&
+           verifier.VerifyTable(e()) &&
+           VerifyField<Evolution::V2::Struct>(verifier, VT_FF) &&
+           VerifyOffset(verifier, VT_G) &&
+           verifier.VerifyVector(g()) &&
+           VerifyOffset(verifier, VT_H) &&
+           verifier.VerifyVector(h()) &&
+           verifier.VerifyVectorOfTables(h()) &&
+           VerifyField<uint32_t>(verifier, VT_I) &&
+           VerifyOffset(verifier, VT_J) &&
+           verifier.VerifyTable(j()) &&
+           VerifyField<uint8_t>(verifier, VT_K) &&
+           verifier.EndTable();
+  }
+};
+
+template<> inline const Evolution::V2::TableA *Root::c_as<Evolution::V2::TableA>() const {
+  return c_as_TableA();
+}
+
+template<> inline const Evolution::V2::TableB *Root::c_as<Evolution::V2::TableB>() const {
+  return c_as_TableB();
+}
+
+template<> inline const Evolution::V2::TableC *Root::c_as<Evolution::V2::TableC>() const {
+  return c_as_TableC();
+}
+
+struct RootBuilder {
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_b(bool b) {
+    fbb_.AddElement<uint8_t>(Root::VT_B, static_cast<uint8_t>(b), 0);
+  }
+  void add_c_type(Evolution::V2::Union c_type) {
+    fbb_.AddElement<uint8_t>(Root::VT_C_TYPE, static_cast<uint8_t>(c_type), 0);
+  }
+  void add_c(flatbuffers::Offset<void> c) {
+    fbb_.AddOffset(Root::VT_C, c);
+  }
+  void add_d(Evolution::V2::Enum d) {
+    fbb_.AddElement<int8_t>(Root::VT_D, static_cast<int8_t>(d), 0);
+  }
+  void add_e(flatbuffers::Offset<Evolution::V2::TableA> e) {
+    fbb_.AddOffset(Root::VT_E, e);
+  }
+  void add_ff(const Evolution::V2::Struct *ff) {
+    fbb_.AddStruct(Root::VT_FF, ff);
+  }
+  void add_g(flatbuffers::Offset<flatbuffers::Vector<int32_t>> g) {
+    fbb_.AddOffset(Root::VT_G, g);
+  }
+  void add_h(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Evolution::V2::TableB>>> h) {
+    fbb_.AddOffset(Root::VT_H, h);
+  }
+  void add_i(uint32_t i) {
+    fbb_.AddElement<uint32_t>(Root::VT_I, i, 1234);
+  }
+  void add_j(flatbuffers::Offset<Evolution::V2::TableC> j) {
+    fbb_.AddOffset(Root::VT_J, j);
+  }
+  void add_k(uint8_t k) {
+    fbb_.AddElement<uint8_t>(Root::VT_K, k, 56);
+  }
+  explicit RootBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  RootBuilder &operator=(const RootBuilder &);
+  flatbuffers::Offset<Root> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<Root>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<Root> CreateRoot(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    bool b = false,
+    Evolution::V2::Union c_type = Evolution::V2::Union::NONE,
+    flatbuffers::Offset<void> c = 0,
+    Evolution::V2::Enum d = Evolution::V2::Enum::King,
+    flatbuffers::Offset<Evolution::V2::TableA> e = 0,
+    const Evolution::V2::Struct *ff = 0,
+    flatbuffers::Offset<flatbuffers::Vector<int32_t>> g = 0,
+    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Evolution::V2::TableB>>> h = 0,
+    uint32_t i = 1234,
+    flatbuffers::Offset<Evolution::V2::TableC> j = 0,
+    uint8_t k = 56) {
+  RootBuilder builder_(_fbb);
+  builder_.add_j(j);
+  builder_.add_i(i);
+  builder_.add_h(h);
+  builder_.add_g(g);
+  builder_.add_ff(ff);
+  builder_.add_e(e);
+  builder_.add_c(c);
+  builder_.add_k(k);
+  builder_.add_d(d);
+  builder_.add_c_type(c_type);
+  builder_.add_b(b);
+  return builder_.Finish();
+}
+
+inline flatbuffers::Offset<Root> CreateRootDirect(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    bool b = false,
+    Evolution::V2::Union c_type = Evolution::V2::Union::NONE,
+    flatbuffers::Offset<void> c = 0,
+    Evolution::V2::Enum d = Evolution::V2::Enum::King,
+    flatbuffers::Offset<Evolution::V2::TableA> e = 0,
+    const Evolution::V2::Struct *ff = 0,
+    const std::vector<int32_t> *g = nullptr,
+    const std::vector<flatbuffers::Offset<Evolution::V2::TableB>> *h = nullptr,
+    uint32_t i = 1234,
+    flatbuffers::Offset<Evolution::V2::TableC> j = 0,
+    uint8_t k = 56) {
+  auto g__ = g ? _fbb.CreateVector<int32_t>(*g) : 0;
+  auto h__ = h ? _fbb.CreateVector<flatbuffers::Offset<Evolution::V2::TableB>>(*h) : 0;
+  return Evolution::V2::CreateRoot(
+      _fbb,
+      b,
+      c_type,
+      c,
+      d,
+      e,
+      ff,
+      g__,
+      h__,
+      i,
+      j,
+      k);
+}
+
+inline bool VerifyUnion(flatbuffers::Verifier &verifier, const void *obj, Union type) {
+  switch (type) {
+    case Union::NONE: {
+      return true;
+    }
+    case Union::TableA: {
+      auto ptr = reinterpret_cast<const Evolution::V2::TableA *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case Union::TableB: {
+      auto ptr = reinterpret_cast<const Evolution::V2::TableB *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    case Union::TableC: {
+      auto ptr = reinterpret_cast<const Evolution::V2::TableC *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
+    default: return true;
+  }
+}
+
+inline bool VerifyUnionVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
+  if (!values || !types) return !values && !types;
+  if (values->size() != types->size()) return false;
+  for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
+    if (!VerifyUnion(
+        verifier,  values->Get(i), types->GetEnum<Union>(i))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+inline const Evolution::V2::Root *GetRoot(const void *buf) {
+  return flatbuffers::GetRoot<Evolution::V2::Root>(buf);
+}
+
+inline const Evolution::V2::Root *GetSizePrefixedRoot(const void *buf) {
+  return flatbuffers::GetSizePrefixedRoot<Evolution::V2::Root>(buf);
+}
+
+inline bool VerifyRootBuffer(
+    flatbuffers::Verifier &verifier) {
+  return verifier.VerifyBuffer<Evolution::V2::Root>(nullptr);
+}
+
+inline bool VerifySizePrefixedRootBuffer(
+    flatbuffers::Verifier &verifier) {
+  return verifier.VerifySizePrefixedBuffer<Evolution::V2::Root>(nullptr);
+}
+
+inline void FinishRootBuffer(
+    flatbuffers::FlatBufferBuilder &fbb,
+    flatbuffers::Offset<Evolution::V2::Root> root) {
+  fbb.Finish(root);
+}
+
+inline void FinishSizePrefixedRootBuffer(
+    flatbuffers::FlatBufferBuilder &fbb,
+    flatbuffers::Offset<Evolution::V2::Root> root) {
+  fbb.FinishSizePrefixed(root);
+}
+
+}  // namespace V2
+}  // namespace Evolution
+
+#endif  // FLATBUFFERS_GENERATED_EVOLUTIONV2_EVOLUTION_V2_H_
index 8b78a5d..29a029c 100644 (file)
@@ -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
index 37b4058..14b621c 100755 (executable)
@@ -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
index 361b835..68716c6 100644 (file)
@@ -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();