capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

schema-loader-test.c++ (14347B)


      1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #define CAPNP_TESTING_CAPNP 1
     23 
     24 #include "schema-loader.h"
     25 #include <kj/compat/gtest.h>
     26 #include "test-util.h"
     27 #include <kj/debug.h>
     28 
     29 namespace capnp {
     30 namespace _ {  // private
     31 namespace {
     32 
     33 TEST(SchemaLoader, Load) {
     34   SchemaLoader loader;
     35 
     36   Schema struct32Schema = loader.load(Schema::from<test::TestLists::Struct32>().getProto());
     37 
     38   auto nativeSchema = Schema::from<test::TestLists>();
     39   Schema testListsSchema = loader.load(nativeSchema.getProto());
     40 
     41   Schema struct8Schema = loader.load(Schema::from<test::TestLists::Struct8>().getProto());
     42   Schema structPSchema = loader.load(Schema::from<test::TestLists::StructP>().getProto());
     43 
     44   EXPECT_EQ(kj::str(nativeSchema.getProto()), kj::str(testListsSchema.getProto()));
     45 
     46   EXPECT_FALSE(testListsSchema == nativeSchema);
     47   EXPECT_FALSE(struct32Schema == Schema::from<test::TestLists::Struct32>());
     48   EXPECT_FALSE(struct8Schema == Schema::from<test::TestLists::Struct8>());
     49   EXPECT_FALSE(structPSchema == Schema::from<test::TestLists::StructP>());
     50 
     51   EXPECT_TRUE(testListsSchema.getDependency(typeId<test::TestLists::Struct32>()) == struct32Schema);
     52   EXPECT_TRUE(testListsSchema.getDependency(typeId<test::TestLists::Struct8>()) == struct8Schema);
     53   EXPECT_TRUE(testListsSchema.getDependency(typeId<test::TestLists::StructP>()) == structPSchema);
     54 
     55   auto struct16Schema = testListsSchema.getDependency(typeId<test::TestLists::Struct16>());
     56   EXPECT_EQ(0u, struct16Schema.getProto().getStruct().getFields().size());
     57 }
     58 
     59 TEST(SchemaLoader, LoadLateUnion) {
     60   SchemaLoader loader;
     61 
     62   StructSchema schema =
     63       loader.load(Schema::from<test::TestLateUnion>().getProto()).asStruct();
     64   loader.load(Schema::from<test::TestLateUnion::TheUnion>().getProto()).asStruct();
     65   loader.load(Schema::from<test::TestLateUnion::AnotherUnion>().getProto()).asStruct();
     66 
     67   EXPECT_EQ(6,
     68       schema.getDependency(schema.getFieldByName("theUnion").getProto().getGroup().getTypeId())
     69             .asStruct().getFieldByName("grault").getProto().getOrdinal().getExplicit());
     70   EXPECT_EQ(9,
     71       schema.getDependency(schema.getFieldByName("anotherUnion").getProto().getGroup().getTypeId())
     72             .asStruct().getFieldByName("corge").getProto().getOrdinal().getExplicit());
     73   EXPECT_TRUE(schema.findFieldByName("corge") == nullptr);
     74   EXPECT_TRUE(schema.findFieldByName("grault") == nullptr);
     75 }
     76 
     77 TEST(SchemaLoader, LoadUnnamedUnion) {
     78   SchemaLoader loader;
     79 
     80   StructSchema schema =
     81       loader.load(Schema::from<test::TestUnnamedUnion>().getProto()).asStruct();
     82 
     83   EXPECT_TRUE(schema.findFieldByName("") == nullptr);
     84 
     85   EXPECT_TRUE(schema.findFieldByName("foo") != nullptr);
     86   EXPECT_TRUE(schema.findFieldByName("bar") != nullptr);
     87   EXPECT_TRUE(schema.findFieldByName("before") != nullptr);
     88   EXPECT_TRUE(schema.findFieldByName("after") != nullptr);
     89 }
     90 
     91 TEST(SchemaLoader, Use) {
     92   SchemaLoader loader;
     93 
     94   StructSchema schema = loader.load(Schema::from<TestAllTypes>().getProto()).asStruct();
     95 
     96   // Also have to load TestEnum.
     97   loader.load(Schema::from<TestEnum>().getProto());
     98 
     99   {
    100     MallocMessageBuilder builder;
    101     auto root = builder.getRoot<DynamicStruct>(schema);
    102 
    103     initDynamicTestMessage(root);
    104     checkDynamicTestMessage(root.asReader());
    105 
    106     // Can't convert to TestAllTypes because we didn't use loadCompiledTypeAndDependencies().
    107     EXPECT_ANY_THROW(root.as<TestAllTypes>());
    108 
    109     // But if we reinterpret the raw bytes, it works.
    110     checkTestMessage(builder.getRoot<TestAllTypes>());
    111   }
    112 
    113   loader.loadCompiledTypeAndDependencies<TestAllTypes>();
    114 
    115   {
    116     MallocMessageBuilder builder;
    117     auto root = builder.getRoot<DynamicStruct>(schema);
    118 
    119     initDynamicTestMessage(root);
    120 
    121     // Now we can actually cast.
    122     checkTestMessage(root.as<TestAllTypes>());
    123   }
    124 
    125   // Let's also test TestListDefaults, but as we do so, let's load the compiled types first, to
    126   // make sure the opposite order works.
    127 
    128   loader.loadCompiledTypeAndDependencies<TestListDefaults>();
    129   StructSchema testListsSchema = loader.get(typeId<TestListDefaults>()).asStruct();
    130   EXPECT_TRUE(testListsSchema != Schema::from<TestListDefaults>());
    131 
    132   {
    133     MallocMessageBuilder builder;
    134     auto root = builder.getRoot<DynamicStruct>(testListsSchema);
    135 
    136     initDynamicTestLists(root);
    137     checkDynamicTestLists(root.asReader());
    138 
    139     checkTestMessage(root.as<TestListDefaults>());
    140   }
    141 
    142   EXPECT_TRUE(loader.load(Schema::from<TestListDefaults>().getProto()) == testListsSchema);
    143 
    144   {
    145     MallocMessageBuilder builder;
    146     auto root = builder.getRoot<DynamicStruct>(testListsSchema);
    147 
    148     initDynamicTestLists(root);
    149     checkTestMessage(root.as<TestListDefaults>());
    150   }
    151 
    152   // Finally, let's test some unions.
    153   StructSchema unionSchema = loader.load(Schema::from<TestUnion>().getProto()).asStruct();
    154   loader.load(Schema::from<TestUnion::Union0>().getProto());
    155   loader.load(Schema::from<TestUnion::Union1>().getProto());
    156   {
    157     MallocMessageBuilder builder;
    158     auto root = builder.getRoot<DynamicStruct>(unionSchema);
    159 
    160     root.get("union0").as<DynamicStruct>().set("u0f1s16", 123);
    161     root.get("union1").as<DynamicStruct>().set("u1f0sp", "hello");
    162 
    163     auto reader = builder.getRoot<TestUnion>().asReader();
    164     EXPECT_EQ(123, reader.getUnion0().getU0f1s16());
    165     EXPECT_EQ("hello", reader.getUnion1().getU1f0sp());
    166   }
    167 }
    168 
    169 template <typename T>
    170 Schema loadUnderAlternateTypeId(SchemaLoader& loader, uint64_t id) {
    171   MallocMessageBuilder schemaBuilder;
    172   schemaBuilder.setRoot(Schema::from<T>().getProto());
    173   auto root = schemaBuilder.getRoot<schema::Node>();
    174   root.setId(id);
    175 
    176   if (root.isStruct()) {
    177     // If the struct contains any self-referential members, change their type IDs as well.
    178     auto fields = root.getStruct().getFields();
    179     for (auto field: fields) {
    180       if (field.isSlot()) {
    181         auto type = field.getSlot().getType();
    182         if (type.isStruct() && type.getStruct().getTypeId() == typeId<T>()) {
    183           type.getStruct().setTypeId(id);
    184         }
    185       }
    186     }
    187   }
    188 
    189   return loader.load(root);
    190 }
    191 
    192 TEST(SchemaLoader, Upgrade) {
    193   SchemaLoader loader;
    194 
    195   loader.loadCompiledTypeAndDependencies<test::TestOldVersion>();
    196 
    197   StructSchema schema = loader.get(typeId<test::TestOldVersion>()).asStruct();
    198 
    199   EXPECT_EQ(kj::str(Schema::from<test::TestOldVersion>().getProto()),
    200             kj::str(schema.getProto()));
    201 
    202   loadUnderAlternateTypeId<test::TestNewVersion>(loader, typeId<test::TestOldVersion>());
    203 
    204   // The new version replaced the old.
    205   EXPECT_EQ(Schema::from<test::TestNewVersion>().getProto().getDisplayName(),
    206             schema.getProto().getDisplayName());
    207 
    208   // But it is still usable as the old version.
    209   schema.requireUsableAs<test::TestOldVersion>();
    210 }
    211 
    212 TEST(SchemaLoader, Downgrade) {
    213   SchemaLoader loader;
    214 
    215   loader.loadCompiledTypeAndDependencies<test::TestNewVersion>();
    216 
    217   StructSchema schema = loader.get(typeId<test::TestNewVersion>()).asStruct();
    218 
    219   EXPECT_EQ(kj::str(Schema::from<test::TestNewVersion>().getProto()), kj::str(schema.getProto()));
    220 
    221   loadUnderAlternateTypeId<test::TestOldVersion>(loader, typeId<test::TestNewVersion>());
    222 
    223   // We kept the new version, because the replacement was older.
    224   EXPECT_EQ(Schema::from<test::TestNewVersion>().getProto().getDisplayName(),
    225             schema.getProto().getDisplayName());
    226   schema.requireUsableAs<test::TestNewVersion>();
    227 }
    228 
    229 TEST(SchemaLoader, Incompatible) {
    230   SchemaLoader loader;
    231   loader.loadCompiledTypeAndDependencies<test::TestListDefaults>();
    232   EXPECT_NONFATAL_FAILURE(
    233       loadUnderAlternateTypeId<test::TestAllTypes>(loader, typeId<test::TestListDefaults>()));
    234 }
    235 
    236 TEST(SchemaLoader, Enumerate) {
    237   SchemaLoader loader;
    238   loader.loadCompiledTypeAndDependencies<TestAllTypes>();
    239   auto list = loader.getAllLoaded();
    240 
    241   ASSERT_EQ(2u, list.size());
    242   if (list[0] == loader.get(typeId<TestAllTypes>())) {
    243     EXPECT_TRUE(list[1] == loader.get(typeId<TestEnum>()));
    244   } else {
    245     EXPECT_TRUE(list[0] == loader.get(typeId<TestEnum>()));
    246     EXPECT_TRUE(list[1] == loader.get(typeId<TestAllTypes>()));
    247   }
    248 }
    249 
    250 TEST(SchemaLoader, EnumerateNoPlaceholders) {
    251   SchemaLoader loader;
    252   Schema schema = loader.load(Schema::from<TestDefaults>().getProto());
    253 
    254   {
    255     auto list = loader.getAllLoaded();
    256     ASSERT_EQ(1u, list.size());
    257     EXPECT_TRUE(list[0] == schema);
    258   }
    259 
    260   Schema dep = schema.getDependency(typeId<TestAllTypes>());
    261 
    262   {
    263     auto list = loader.getAllLoaded();
    264     ASSERT_EQ(2u, list.size());
    265     if (list[0] == schema) {
    266       EXPECT_TRUE(list[1] == dep);
    267     } else {
    268       EXPECT_TRUE(list[0] == dep);
    269       EXPECT_TRUE(list[1] == schema);
    270     }
    271   }
    272 }
    273 
    274 class FakeLoaderCallback: public SchemaLoader::LazyLoadCallback {
    275 public:
    276   FakeLoaderCallback(const schema::Node::Reader node): node(node), loaded(false) {}
    277 
    278   bool isLoaded() { return loaded; }
    279 
    280   void load(const SchemaLoader& loader, uint64_t id) const override {
    281     if (id == 1234) {
    282       // Magic "not found" ID.
    283       return;
    284     }
    285 
    286     EXPECT_EQ(node.getId(), id);
    287     EXPECT_FALSE(loaded);
    288     loaded = true;
    289     loader.loadOnce(node);
    290   }
    291 
    292 private:
    293   const schema::Node::Reader node;
    294   mutable bool loaded = false;
    295 };
    296 
    297 TEST(SchemaLoader, LazyLoad) {
    298   FakeLoaderCallback callback(Schema::from<TestAllTypes>().getProto());
    299   SchemaLoader loader(callback);
    300 
    301   EXPECT_TRUE(loader.tryGet(1234) == nullptr);
    302 
    303   EXPECT_FALSE(callback.isLoaded());
    304   Schema schema = loader.get(typeId<TestAllTypes>());
    305   EXPECT_TRUE(callback.isLoaded());
    306 
    307   EXPECT_EQ(schema.getProto().getDisplayName(),
    308             Schema::from<TestAllTypes>().getProto().getDisplayName());
    309 
    310   EXPECT_EQ(schema, schema.getDependency(typeId<TestAllTypes>()));
    311   EXPECT_EQ(schema, loader.get(typeId<TestAllTypes>()));
    312 }
    313 
    314 TEST(SchemaLoader, LazyLoadGetDependency) {
    315   FakeLoaderCallback callback(Schema::from<TestAllTypes>().getProto());
    316   SchemaLoader loader(callback);
    317 
    318   Schema schema = loader.load(Schema::from<TestDefaults>().getProto());
    319 
    320   EXPECT_FALSE(callback.isLoaded());
    321 
    322   Schema dep = schema.getDependency(typeId<TestAllTypes>());
    323 
    324   EXPECT_TRUE(callback.isLoaded());
    325 
    326   EXPECT_EQ(dep.getProto().getDisplayName(),
    327             Schema::from<TestAllTypes>().getProto().getDisplayName());
    328 
    329   EXPECT_EQ(dep, schema.getDependency(typeId<TestAllTypes>()));
    330   EXPECT_EQ(dep, loader.get(typeId<TestAllTypes>()));
    331 }
    332 
    333 TEST(SchemaLoader, Generics) {
    334   SchemaLoader loader;
    335 
    336   StructSchema allTypes = loader.load(Schema::from<TestAllTypes>().getProto()).asStruct();
    337   StructSchema tap = loader.load(Schema::from<test::TestAnyPointer>().getProto()).asStruct();
    338   loader.load(Schema::from<test::TestGenerics<>::Inner>().getProto());
    339   loader.load(Schema::from<test::TestGenerics<>::Inner2<>>().getProto());
    340   loader.load(Schema::from<test::TestGenerics<>::Interface<>>().getProto());
    341   loader.load(Schema::from<test::TestGenerics<>::Interface<>::CallResults>().getProto());
    342   loader.load(Schema::from<test::TestGenerics<>>().getProto());
    343   StructSchema schema = loader.load(Schema::from<test::TestUseGenerics>().getProto()).asStruct();
    344 
    345   StructSchema branded;
    346 
    347   {
    348     StructSchema::Field basic = schema.getFieldByName("basic");
    349     branded = basic.getType().asStruct();
    350 
    351     StructSchema::Field foo = branded.getFieldByName("foo");
    352     EXPECT_TRUE(foo.getType().asStruct() == allTypes);
    353     EXPECT_TRUE(foo.getType().asStruct() != tap);
    354 
    355     StructSchema instance2 = branded.getFieldByName("rev").getType().asStruct();
    356     StructSchema::Field foo2 = instance2.getFieldByName("foo");
    357     EXPECT_TRUE(foo2.getType().asStruct() == tap);
    358     EXPECT_TRUE(foo2.getType().asStruct() != allTypes);
    359   }
    360 
    361   {
    362     StructSchema inner2 = schema.getFieldByName("inner2").getType().asStruct();
    363 
    364     StructSchema bound = inner2.getFieldByName("innerBound").getType().asStruct();
    365     Type boundFoo = bound.getFieldByName("foo").getType();
    366     EXPECT_FALSE(boundFoo.isAnyPointer());
    367     EXPECT_TRUE(boundFoo.asStruct() == allTypes);
    368 
    369     StructSchema unbound = inner2.getFieldByName("innerUnbound").getType().asStruct();
    370     Type unboundFoo = unbound.getFieldByName("foo").getType();
    371     EXPECT_TRUE(unboundFoo.isAnyPointer());
    372   }
    373 
    374   {
    375     InterfaceSchema cap = schema.getFieldByName("genericCap").getType().asInterface();
    376     InterfaceSchema::Method method = cap.getMethodByName("call");
    377 
    378     StructSchema inner2 = method.getParamType();
    379     StructSchema bound = inner2.getFieldByName("innerBound").getType().asStruct();
    380     Type boundFoo = bound.getFieldByName("foo").getType();
    381     EXPECT_FALSE(boundFoo.isAnyPointer());
    382     EXPECT_TRUE(boundFoo.asStruct() == allTypes);
    383     EXPECT_TRUE(inner2.getFieldByName("baz").getType().isText());
    384 
    385     StructSchema results = method.getResultType();
    386     EXPECT_TRUE(results.getFieldByName("qux").getType().isData());
    387 
    388     EXPECT_TRUE(results.getFieldByName("gen").getType().asStruct() == branded);
    389   }
    390 }
    391 
    392 TEST(SchemaLoader, LoadStreaming) {
    393   SchemaLoader loader;
    394 
    395   InterfaceSchema schema =
    396       loader.load(Schema::from<test::TestStreaming>().getProto()).asInterface();
    397 
    398   auto results = schema.getMethodByName("doStreamI").getResultType();
    399   KJ_EXPECT(results.isStreamResult());
    400   KJ_EXPECT(results.getShortDisplayName() == "StreamResult", results.getShortDisplayName());
    401 }
    402 
    403 }  // namespace
    404 }  // namespace _ (private)
    405 }  // namespace capnp