schema-parser-test.c++ 7.34 KB
Newer Older
Kenton Varda's avatar
Kenton Varda committed
1 2
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
21 22

#include "schema-parser.h"
23
#include <kj/compat/gtest.h>
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
#include "test-util.h"
#include <kj/debug.h>
#include <map>

namespace capnp {
namespace {

class FakeFileReader final: public SchemaFile::FileReader {
public:
  void add(kj::StringPtr name, kj::StringPtr content) {
    files[name] = content;
  }

  bool exists(kj::StringPtr path) const override {
    return files.count(path) > 0;
  }

  kj::Array<const char> read(kj::StringPtr path) const override {
    auto iter = files.find(path);
    KJ_ASSERT(iter != files.end(), "FakeFileReader has no such file.", path);
    auto result = kj::heapArray<char>(iter->second.size());
    memcpy(result.begin(), iter->second.begin(), iter->second.size());
    return kj::mv(result);
  }

private:
  std::map<kj::StringPtr, kj::StringPtr> files;
};

53 54
static uint64_t getFieldTypeFileId(StructSchema::Field field) {
  return field.getContainingStruct()
55
      .getDependency(field.getProto().getSlot().getType().getStruct().getTypeId())
56 57 58
      .getProto().getScopeId();
}

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
TEST(SchemaParser, Basic) {
  SchemaParser parser;
  FakeFileReader reader;

  reader.add("src/foo/bar.capnp",
      "@0x8123456789abcdef;\n"
      "struct Bar {\n"
      "  baz @0: import \"baz.capnp\".Baz;\n"
      "  corge @1: import \"../qux/corge.capnp\".Corge;\n"
      "  grault @2: import \"/grault.capnp\".Grault;\n"
      "  garply @3: import \"/garply.capnp\".Garply;\n"
      "}\n");
  reader.add("src/foo/baz.capnp",
      "@0x823456789abcdef1;\n"
      "struct Baz {}\n");
  reader.add("src/qux/corge.capnp",
      "@0x83456789abcdef12;\n"
      "struct Corge {}\n");
  reader.add("/usr/include/grault.capnp",
      "@0x8456789abcdef123;\n"
      "struct Grault {}\n");
  reader.add("/opt/include/grault.capnp",
      "@0x8000000000000001;\n"
      "struct WrongGrault {}\n");
  reader.add("/usr/local/include/garply.capnp",
      "@0x856789abcdef1234;\n"
      "struct Garply {}\n");

  kj::StringPtr importPath[] = {
    "/usr/include", "/usr/local/include", "/opt/include"
  };

  ParsedSchema barSchema = parser.parseFile(SchemaFile::newDiskFile(
92
      "foo2/bar2.capnp", "src/foo/bar.capnp", importPath, reader));
93 94 95 96 97 98

  auto barProto = barSchema.getProto();
  EXPECT_EQ(0x8123456789abcdefull, barProto.getId());
  EXPECT_EQ("foo2/bar2.capnp", barProto.getDisplayName());

  auto barStruct = barSchema.getNested("Bar");
99
  auto barFields = barStruct.asStruct().getFields();
Kenton Varda's avatar
Kenton Varda committed
100
  ASSERT_EQ(4u, barFields.size());
101 102 103 104 105 106 107 108
  EXPECT_EQ("baz", barFields[0].getProto().getName());
  EXPECT_EQ(0x823456789abcdef1ull, getFieldTypeFileId(barFields[0]));
  EXPECT_EQ("corge", barFields[1].getProto().getName());
  EXPECT_EQ(0x83456789abcdef12ull, getFieldTypeFileId(barFields[1]));
  EXPECT_EQ("grault", barFields[2].getProto().getName());
  EXPECT_EQ(0x8456789abcdef123ull, getFieldTypeFileId(barFields[2]));
  EXPECT_EQ("garply", barFields[3].getProto().getName());
  EXPECT_EQ(0x856789abcdef1234ull, getFieldTypeFileId(barFields[3]));
109 110

  auto bazSchema = parser.parseFile(SchemaFile::newDiskFile(
111 112
      "not/used/because/already/loaded",
      "src/foo/baz.capnp", importPath, reader));
113 114 115 116 117 118
  EXPECT_EQ(0x823456789abcdef1ull, bazSchema.getProto().getId());
  EXPECT_EQ("foo2/baz.capnp", bazSchema.getProto().getDisplayName());
  auto bazStruct = bazSchema.getNested("Baz").asStruct();
  EXPECT_EQ(bazStruct, barStruct.getDependency(bazStruct.getProto().getId()));

  auto corgeSchema = parser.parseFile(SchemaFile::newDiskFile(
119 120
      "not/used/because/already/loaded",
      "src/qux/corge.capnp", importPath, reader));
121 122 123 124 125 126
  EXPECT_EQ(0x83456789abcdef12ull, corgeSchema.getProto().getId());
  EXPECT_EQ("qux/corge.capnp", corgeSchema.getProto().getDisplayName());
  auto corgeStruct = corgeSchema.getNested("Corge").asStruct();
  EXPECT_EQ(corgeStruct, barStruct.getDependency(corgeStruct.getProto().getId()));

  auto graultSchema = parser.parseFile(SchemaFile::newDiskFile(
127 128
      "not/used/because/already/loaded",
      "/usr/include/grault.capnp", importPath, reader));
129 130 131 132 133
  EXPECT_EQ(0x8456789abcdef123ull, graultSchema.getProto().getId());
  EXPECT_EQ("grault.capnp", graultSchema.getProto().getDisplayName());
  auto graultStruct = graultSchema.getNested("Grault").asStruct();
  EXPECT_EQ(graultStruct, barStruct.getDependency(graultStruct.getProto().getId()));

134 135
  // Try importing the other grault.capnp directly.  It'll get the display name we specify since
  // it wasn't imported before.
136
  auto wrongGraultSchema = parser.parseFile(SchemaFile::newDiskFile(
137 138
      "weird/display/name.capnp",
      "/opt/include/grault.capnp", importPath, reader));
139 140 141 142
  EXPECT_EQ(0x8000000000000001ull, wrongGraultSchema.getProto().getId());
  EXPECT_EQ("weird/display/name.capnp", wrongGraultSchema.getProto().getDisplayName());
}

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
TEST(SchemaParser, Constants) {
  // This is actually a test of the full dynamic API stack for constants, because the schemas for
  // constants are not actually accessible from the generated code API, so the only way to ever
  // get a ConstSchema is by parsing it.

  SchemaParser parser;
  FakeFileReader reader;

  reader.add("const.capnp",
      "@0x8123456789abcdef;\n"
      "const uint32Const :UInt32 = 1234;\n"
      "const listConst :List(Float32) = [1.25, 2.5, 3e4];\n"
      "const structConst :Foo = (bar = 123, baz = \"qux\");\n"
      "struct Foo {\n"
      "  bar @0 :Int16;\n"
      "  baz @1 :Text;\n"
159 160 161 162
      "}\n"
      "const genericConst :TestGeneric(Text) = (value = \"text\");\n"
      "struct TestGeneric(T) {\n"
      "  value @0 :T;\n"
163 164
      "}\n");

165
  ParsedSchema fileSchema = parser.parseFile(SchemaFile::newDiskFile(
166 167
      "const.capnp", "const.capnp", nullptr, reader));

168
  EXPECT_EQ(1234, fileSchema.getNested("uint32Const").asConst().as<uint32_t>());
169

170
  auto list = fileSchema.getNested("listConst").asConst().as<DynamicList>();
171 172 173 174 175
  ASSERT_EQ(3u, list.size());
  EXPECT_EQ(1.25, list[0].as<float>());
  EXPECT_EQ(2.5, list[1].as<float>());
  EXPECT_EQ(3e4f, list[2].as<float>());

176
  auto structConst = fileSchema.getNested("structConst").asConst().as<DynamicStruct>();
177 178
  EXPECT_EQ(123, structConst.get("bar").as<int16_t>());
  EXPECT_EQ("qux", structConst.get("baz").as<Text>());
179 180 181

  auto genericConst = fileSchema.getNested("genericConst").asConst().as<DynamicStruct>();
  EXPECT_EQ("text", genericConst.get("value").as<Text>());
182 183
}

184 185
}  // namespace
}  // namespace capnp