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