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-parser-test.c++ (10730B)


      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-parser.h"
     25 #include <kj/compat/gtest.h>
     26 #include "test-util.h"
     27 #include <kj/debug.h>
     28 #include <map>
     29 
     30 namespace capnp {
     31 namespace {
     32 
     33 #if _WIN32
     34 #define ABS(x) "C:\\" x
     35 #else
     36 #define ABS(x) "/" x
     37 #endif
     38 
     39 class FakeFileReader final: public kj::Filesystem {
     40 public:
     41   void add(kj::StringPtr name, kj::StringPtr content) {
     42     root->openFile(cwd.evalNative(name), kj::WriteMode::CREATE | kj::WriteMode::CREATE_PARENT)
     43         ->writeAll(content);
     44   }
     45 
     46   const kj::Directory& getRoot() const override { return *root; }
     47   const kj::Directory& getCurrent() const override { return *current; }
     48   kj::PathPtr getCurrentPath() const override { return cwd; }
     49 
     50 private:
     51   kj::Own<const kj::Directory> root = kj::newInMemoryDirectory(kj::nullClock());
     52   kj::Path cwd = kj::Path({}).evalNative(ABS("path/to/current/dir"));
     53   kj::Own<const kj::Directory> current = root->openSubdir(cwd,
     54       kj::WriteMode::CREATE | kj::WriteMode::CREATE_PARENT);
     55 };
     56 
     57 static uint64_t getFieldTypeFileId(StructSchema::Field field) {
     58   return field.getContainingStruct()
     59       .getDependency(field.getProto().getSlot().getType().getStruct().getTypeId())
     60       .getProto().getScopeId();
     61 }
     62 
     63 TEST(SchemaParser, Basic) {
     64   FakeFileReader reader;
     65   SchemaParser parser;
     66   parser.setDiskFilesystem(reader);
     67 
     68   reader.add("src/foo/bar.capnp",
     69       "@0x8123456789abcdef;\n"
     70       "struct Bar {\n"
     71       "  baz @0: import \"baz.capnp\".Baz;\n"
     72       "  corge @1: import \"../qux/corge.capnp\".Corge;\n"
     73       "  grault @2: import \"/grault.capnp\".Grault;\n"
     74       "  garply @3: import \"/garply.capnp\".Garply;\n"
     75       "}\n");
     76   reader.add("src/foo/baz.capnp",
     77       "@0x823456789abcdef1;\n"
     78       "struct Baz {}\n");
     79   reader.add("src/qux/corge.capnp",
     80       "@0x83456789abcdef12;\n"
     81       "struct Corge {}\n");
     82   reader.add(ABS("usr/include/grault.capnp"),
     83       "@0x8456789abcdef123;\n"
     84       "struct Grault {}\n");
     85   reader.add(ABS("opt/include/grault.capnp"),
     86       "@0x8000000000000001;\n"
     87       "struct WrongGrault {}\n");
     88   reader.add(ABS("usr/local/include/garply.capnp"),
     89       "@0x856789abcdef1234;\n"
     90       "struct Garply {}\n");
     91 
     92   kj::StringPtr importPath[] = {
     93     ABS("usr/include"), ABS("usr/local/include"), ABS("opt/include")
     94   };
     95 
     96   ParsedSchema barSchema = parser.parseDiskFile(
     97       "foo2/bar2.capnp", "src/foo/bar.capnp", importPath);
     98 
     99   auto barProto = barSchema.getProto();
    100   EXPECT_EQ(0x8123456789abcdefull, barProto.getId());
    101   EXPECT_EQ("foo2/bar2.capnp", barProto.getDisplayName());
    102 
    103   auto barStruct = barSchema.getNested("Bar");
    104   auto barFields = barStruct.asStruct().getFields();
    105   ASSERT_EQ(4u, barFields.size());
    106   EXPECT_EQ("baz", barFields[0].getProto().getName());
    107   EXPECT_EQ(0x823456789abcdef1ull, getFieldTypeFileId(barFields[0]));
    108   EXPECT_EQ("corge", barFields[1].getProto().getName());
    109   EXPECT_EQ(0x83456789abcdef12ull, getFieldTypeFileId(barFields[1]));
    110   EXPECT_EQ("grault", barFields[2].getProto().getName());
    111   EXPECT_EQ(0x8456789abcdef123ull, getFieldTypeFileId(barFields[2]));
    112   EXPECT_EQ("garply", barFields[3].getProto().getName());
    113   EXPECT_EQ(0x856789abcdef1234ull, getFieldTypeFileId(barFields[3]));
    114 
    115   auto barStructs = barSchema.getAllNested();
    116   ASSERT_EQ(1, barStructs.size());
    117   EXPECT_EQ("Bar", barStructs[0].getUnqualifiedName());
    118   barFields = barStructs[0].asStruct().getFields();
    119   ASSERT_EQ(4u, barFields.size());
    120   EXPECT_EQ("baz", barFields[0].getProto().getName());
    121   EXPECT_EQ(0x823456789abcdef1ull, getFieldTypeFileId(barFields[0]));
    122 
    123   auto bazSchema = parser.parseDiskFile(
    124       "not/used/because/already/loaded",
    125       "src/foo/baz.capnp", importPath);
    126   EXPECT_EQ(0x823456789abcdef1ull, bazSchema.getProto().getId());
    127   EXPECT_EQ("foo2/baz.capnp", bazSchema.getProto().getDisplayName());
    128   auto bazStruct = bazSchema.getNested("Baz").asStruct();
    129   EXPECT_EQ(bazStruct, barStruct.getDependency(bazStruct.getProto().getId()));
    130 
    131   auto corgeSchema = parser.parseDiskFile(
    132       "not/used/because/already/loaded",
    133       "src/qux/corge.capnp", importPath);
    134   EXPECT_EQ(0x83456789abcdef12ull, corgeSchema.getProto().getId());
    135   EXPECT_EQ("qux/corge.capnp", corgeSchema.getProto().getDisplayName());
    136   auto corgeStruct = corgeSchema.getNested("Corge").asStruct();
    137   EXPECT_EQ(corgeStruct, barStruct.getDependency(corgeStruct.getProto().getId()));
    138 
    139   auto graultSchema = parser.parseDiskFile(
    140       "not/used/because/already/loaded",
    141       ABS("usr/include/grault.capnp"), importPath);
    142   EXPECT_EQ(0x8456789abcdef123ull, graultSchema.getProto().getId());
    143   EXPECT_EQ("grault.capnp", graultSchema.getProto().getDisplayName());
    144   auto graultStruct = graultSchema.getNested("Grault").asStruct();
    145   EXPECT_EQ(graultStruct, barStruct.getDependency(graultStruct.getProto().getId()));
    146 
    147   // Try importing the other grault.capnp directly.  It'll get the display name we specify since
    148   // it wasn't imported before.
    149   auto wrongGraultSchema = parser.parseDiskFile(
    150       "weird/display/name.capnp",
    151       ABS("opt/include/grault.capnp"), importPath);
    152   EXPECT_EQ(0x8000000000000001ull, wrongGraultSchema.getProto().getId());
    153   EXPECT_EQ("weird/display/name.capnp", wrongGraultSchema.getProto().getDisplayName());
    154 }
    155 
    156 TEST(SchemaParser, Constants) {
    157   // This is actually a test of the full dynamic API stack for constants, because the schemas for
    158   // constants are not actually accessible from the generated code API, so the only way to ever
    159   // get a ConstSchema is by parsing it.
    160 
    161   FakeFileReader reader;
    162   SchemaParser parser;
    163   parser.setDiskFilesystem(reader);
    164 
    165   reader.add("const.capnp",
    166       "@0x8123456789abcdef;\n"
    167       "const uint32Const :UInt32 = 1234;\n"
    168       "const listConst :List(Float32) = [1.25, 2.5, 3e4];\n"
    169       "const structConst :Foo = (bar = 123, baz = \"qux\");\n"
    170       "struct Foo {\n"
    171       "  bar @0 :Int16;\n"
    172       "  baz @1 :Text;\n"
    173       "}\n"
    174       "const genericConst :TestGeneric(Text) = (value = \"text\");\n"
    175       "struct TestGeneric(T) {\n"
    176       "  value @0 :T;\n"
    177       "}\n");
    178 
    179   ParsedSchema fileSchema = parser.parseDiskFile(
    180       "const.capnp", "const.capnp", nullptr);
    181 
    182   EXPECT_EQ(1234, fileSchema.getNested("uint32Const").asConst().as<uint32_t>());
    183 
    184   auto list = fileSchema.getNested("listConst").asConst().as<DynamicList>();
    185   ASSERT_EQ(3u, list.size());
    186   EXPECT_EQ(1.25, list[0].as<float>());
    187   EXPECT_EQ(2.5, list[1].as<float>());
    188   EXPECT_EQ(3e4f, list[2].as<float>());
    189 
    190   auto structConst = fileSchema.getNested("structConst").asConst().as<DynamicStruct>();
    191   EXPECT_EQ(123, structConst.get("bar").as<int16_t>());
    192   EXPECT_EQ("qux", structConst.get("baz").as<Text>());
    193 
    194   auto genericConst = fileSchema.getNested("genericConst").asConst().as<DynamicStruct>();
    195   EXPECT_EQ("text", genericConst.get("value").as<Text>());
    196 }
    197 
    198 void expectSourceInfo(schema::Node::SourceInfo::Reader sourceInfo,
    199                    uint64_t expectedId, kj::StringPtr expectedComment,
    200                    std::initializer_list<const kj::StringPtr> expectedMembers) {
    201   KJ_EXPECT(sourceInfo.getId() == expectedId, sourceInfo, expectedId);
    202   KJ_EXPECT(sourceInfo.getDocComment() == expectedComment, sourceInfo, expectedComment);
    203 
    204   auto members = sourceInfo.getMembers();
    205   KJ_ASSERT(members.size() == expectedMembers.size());
    206   for (auto i: kj::indices(expectedMembers)) {
    207     KJ_EXPECT(members[i].getDocComment() == expectedMembers.begin()[i],
    208               members[i], expectedMembers.begin()[i]);
    209   }
    210 }
    211 
    212 TEST(SchemaParser, SourceInfo) {
    213   FakeFileReader reader;
    214   SchemaParser parser;
    215   parser.setDiskFilesystem(reader);
    216 
    217   reader.add("foo.capnp",
    218       "@0x84a2c6051e1061ed;\n"
    219       "# file doc comment\n"
    220       "\n"
    221       "struct Foo @0xc6527d0a670dc4c3 {\n"
    222       "  # struct doc comment\n"
    223       "  # second line\n"
    224       "\n"
    225       "  bar @0 :UInt32;\n"
    226       "  # field doc comment\n"
    227       "  baz :group {\n"
    228       "    # group doc comment\n"
    229       "    qux @1 :Text;\n"
    230       "    # group field doc comment\n"
    231       "  }\n"
    232       "}\n"
    233       "\n"
    234       "enum Corge @0xae08878f1a016f14 {\n"
    235       "  # enum doc comment\n"
    236       "  grault @0;\n"
    237       "  # enumerant doc comment\n"
    238       "  garply @1;\n"
    239       "}\n"
    240       "\n"
    241       "interface Waldo @0xc0f1b0aff62b761e {\n"
    242       "  # interface doc comment\n"
    243       "  fred @0 (plugh :Int32) -> (xyzzy :Text);\n"
    244       "  # method doc comment\n"
    245       "}\n"
    246       "\n"
    247       "struct Thud @0xcca9972702b730b4 {}\n"
    248       "# post-comment\n");
    249 
    250   ParsedSchema file = parser.parseDiskFile(
    251       "foo.capnp", "foo.capnp", nullptr);
    252   ParsedSchema foo = file.getNested("Foo");
    253 
    254   expectSourceInfo(file.getSourceInfo(), 0x84a2c6051e1061edull, "file doc comment\n", {});
    255 
    256   expectSourceInfo(foo.getSourceInfo(), 0xc6527d0a670dc4c3ull, "struct doc comment\nsecond line\n",
    257       { "field doc comment\n", "group doc comment\n" });
    258 
    259   auto group = foo.asStruct().getFieldByName("baz").getType().asStruct();
    260   expectSourceInfo(KJ_ASSERT_NONNULL(parser.getSourceInfo(group)),
    261       group.getProto().getId(), "group doc comment\n", { "group field doc comment\n" });
    262 
    263   ParsedSchema corge = file.getNested("Corge");
    264   expectSourceInfo(corge.getSourceInfo(), 0xae08878f1a016f14, "enum doc comment\n",
    265       { "enumerant doc comment\n", "" });
    266 
    267   ParsedSchema waldo = file.getNested("Waldo");
    268   expectSourceInfo(waldo.getSourceInfo(), 0xc0f1b0aff62b761e, "interface doc comment\n",
    269       { "method doc comment\n" });
    270 
    271   ParsedSchema thud = file.getNested("Thud");
    272   expectSourceInfo(thud.getSourceInfo(), 0xcca9972702b730b4, "post-comment\n", {});
    273 }
    274 
    275 }  // namespace
    276 }  // namespace capnp