schema-test.c++ 16.4 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 23
#define CAPNP_TESTING_CAPNP 1

24
#include "schema.h"
25
#include <kj/compat/gtest.h>
26 27
#include "test-util.h"

28 29 30 31 32 33 34 35 36
// TODO(cleanup): Auto-generate stringification functions for union discriminants.
namespace capnp {
namespace schema {
inline kj::String KJ_STRINGIFY(Type::Which which) {
  return kj::str(static_cast<uint16_t>(which));
}
}  // namespace schema
}  // namespace capnp

37
namespace capnp {
38
namespace _ {  // private
39 40 41 42 43 44 45 46 47 48 49
namespace {

TEST(Schema, Structs) {
  StructSchema schema = Schema::from<TestAllTypes>();

  EXPECT_EQ(typeId<TestAllTypes>(), schema.getProto().getId());

  EXPECT_TRUE(schema.getDependency(typeId<TestEnum>()) == Schema::from<TestEnum>());
  EXPECT_TRUE(schema.getDependency(typeId<TestEnum>()) != schema);
  EXPECT_TRUE(schema.getDependency(typeId<TestAllTypes>()) == Schema::from<TestAllTypes>());
  EXPECT_TRUE(schema.getDependency(typeId<TestAllTypes>()) == schema);
50
  EXPECT_NONFATAL_FAILURE(schema.getDependency(typeId<TestDefaults>()));
51 52

  EXPECT_TRUE(schema.asStruct() == schema);
53 54
  EXPECT_NONFATAL_FAILURE(schema.asEnum());
  EXPECT_NONFATAL_FAILURE(schema.asInterface());
55

56 57 58 59
  ASSERT_EQ(schema.getFields().size(), schema.getProto().getStruct().getFields().size());
  StructSchema::Field field = schema.getFields()[0];
  EXPECT_EQ("voidField", field.getProto().getName());
  EXPECT_TRUE(field.getContainingStruct() == schema);
60

61 62 63
  StructSchema::Field lookup = schema.getFieldByName("voidField");
  EXPECT_TRUE(lookup == field);
  EXPECT_TRUE(lookup != schema.getFields()[1]);
64 65 66 67 68 69
  EXPECT_FALSE(lookup.getProto().getSlot().getHadExplicitDefault());

  EXPECT_FALSE(schema.getFieldByName("int32Field").getProto().getSlot().getHadExplicitDefault());

  EXPECT_TRUE(Schema::from<TestDefaults>().getFieldByName("int32Field")
      .getProto().getSlot().getHadExplicitDefault());
70

71
  EXPECT_TRUE(schema.findFieldByName("noSuchField") == nullptr);
72

73 74 75 76 77
  EXPECT_TRUE(schema.findFieldByName("int32Field") != nullptr);
  EXPECT_TRUE(schema.findFieldByName("float32List") != nullptr);
  EXPECT_TRUE(schema.findFieldByName("dataList") != nullptr);
  EXPECT_TRUE(schema.findFieldByName("textField") != nullptr);
  EXPECT_TRUE(schema.findFieldByName("structField") != nullptr);
78 79 80 81 82 83 84
}

TEST(Schema, FieldLookupOutOfOrder) {
  // Tests that name lookup works correctly when the fields are defined out-of-order in the schema
  // file.
  auto schema = Schema::from<test::TestOutOfOrder>().asStruct();

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
  EXPECT_EQ("qux", schema.getFields()[0].getProto().getName());
  EXPECT_EQ("grault", schema.getFields()[1].getProto().getName());
  EXPECT_EQ("bar", schema.getFields()[2].getProto().getName());
  EXPECT_EQ("foo", schema.getFields()[3].getProto().getName());
  EXPECT_EQ("corge", schema.getFields()[4].getProto().getName());
  EXPECT_EQ("waldo", schema.getFields()[5].getProto().getName());
  EXPECT_EQ("quux", schema.getFields()[6].getProto().getName());
  EXPECT_EQ("garply", schema.getFields()[7].getProto().getName());
  EXPECT_EQ("baz", schema.getFields()[8].getProto().getName());

  EXPECT_EQ(3, schema.getFieldByName("foo").getProto().getOrdinal().getExplicit());
  EXPECT_EQ(2, schema.getFieldByName("bar").getProto().getOrdinal().getExplicit());
  EXPECT_EQ(8, schema.getFieldByName("baz").getProto().getOrdinal().getExplicit());
  EXPECT_EQ(0, schema.getFieldByName("qux").getProto().getOrdinal().getExplicit());
  EXPECT_EQ(6, schema.getFieldByName("quux").getProto().getOrdinal().getExplicit());
  EXPECT_EQ(4, schema.getFieldByName("corge").getProto().getOrdinal().getExplicit());
  EXPECT_EQ(1, schema.getFieldByName("grault").getProto().getOrdinal().getExplicit());
  EXPECT_EQ(7, schema.getFieldByName("garply").getProto().getOrdinal().getExplicit());
  EXPECT_EQ(5, schema.getFieldByName("waldo").getProto().getOrdinal().getExplicit());
104 105 106 107 108
}

TEST(Schema, Unions) {
  auto schema = Schema::from<TestUnion>().asStruct();

109 110
  EXPECT_TRUE(schema.findFieldByName("bit0") != nullptr);
  EXPECT_TRUE(schema.findFieldByName("u1f0s8") == nullptr);
111

112
  auto union1 = schema.getFieldByName("union1");
113
  auto union1g = schema.getDependency(union1.getProto().getGroup().getTypeId()).asStruct();
114 115
  EXPECT_EQ(schema, union1g.getDependency(union1g.getProto().getScopeId()));
  EXPECT_TRUE(union1g.findFieldByName("bin0") == nullptr);
116

117
  auto u1f0s8 = union1g.getFieldByName("u1f0s8");
118
  EXPECT_EQ("u1f0s8", u1f0s8.getProto().getName());
119
  EXPECT_TRUE(u1f0s8.getContainingStruct() == union1g);
120

121 122 123 124
  EXPECT_TRUE(union1g.findFieldByName("u1f1s8") != nullptr);
  EXPECT_TRUE(union1g.findFieldByName("u1f0s32") != nullptr);
  EXPECT_TRUE(union1g.findFieldByName("u1f0sp") != nullptr);
  EXPECT_TRUE(union1g.findFieldByName("u1f1s1") != nullptr);
125

126 127 128
  EXPECT_TRUE(union1g.findFieldByName("u0f0s1") == nullptr);
  EXPECT_TRUE(union1g.findFieldByName("u2f0s8") == nullptr);
  EXPECT_TRUE(union1g.findFieldByName("noSuchField") == nullptr);
129 130 131 132 133 134 135
}

TEST(Schema, Enums) {
  EnumSchema schema = Schema::from<TestEnum>();

  EXPECT_EQ(typeId<TestEnum>(), schema.getProto().getId());

136 137
  EXPECT_NONFATAL_FAILURE(schema.getDependency(typeId<TestAllTypes>()));
  EXPECT_NONFATAL_FAILURE(schema.getDependency(typeId<TestEnum>()));
138

139 140
  EXPECT_NONFATAL_FAILURE(schema.asStruct());
  EXPECT_NONFATAL_FAILURE(schema.asInterface());
141 142
  EXPECT_TRUE(schema.asEnum() == schema);

143
  ASSERT_EQ(schema.getEnumerants().size(),
144
            schema.getProto().getEnum().getEnumerants().size());
145
  EnumSchema::Enumerant enumerant = schema.getEnumerants()[0];
146 147 148
  EXPECT_EQ("foo", enumerant.getProto().getName());
  EXPECT_TRUE(enumerant.getContainingEnum() == schema);

149 150 151
  EnumSchema::Enumerant lookup = schema.getEnumerantByName("foo");
  EXPECT_TRUE(lookup == enumerant);
  EXPECT_TRUE(lookup != schema.getEnumerants()[1]);
152 153 154 155 156 157 158 159 160 161 162 163

  EXPECT_TRUE(schema.findEnumerantByName("noSuchEnumerant") == nullptr);

  EXPECT_TRUE(schema.findEnumerantByName("bar") != nullptr);
  EXPECT_TRUE(schema.findEnumerantByName("qux") != nullptr);
  EXPECT_TRUE(schema.findEnumerantByName("corge") != nullptr);
  EXPECT_TRUE(schema.findEnumerantByName("grault") != nullptr);
}

// TODO(someday):  Test interface schemas when interfaces are implemented.

TEST(Schema, Lists) {
Kenton Varda's avatar
Kenton Varda committed
164 165 166 167 168 169 170 171 172 173 174 175 176 177
  EXPECT_EQ(schema::Type::VOID, Schema::from<List<Void>>().whichElementType());
  EXPECT_EQ(schema::Type::BOOL, Schema::from<List<bool>>().whichElementType());
  EXPECT_EQ(schema::Type::INT8, Schema::from<List<int8_t>>().whichElementType());
  EXPECT_EQ(schema::Type::INT16, Schema::from<List<int16_t>>().whichElementType());
  EXPECT_EQ(schema::Type::INT32, Schema::from<List<int32_t>>().whichElementType());
  EXPECT_EQ(schema::Type::INT64, Schema::from<List<int64_t>>().whichElementType());
  EXPECT_EQ(schema::Type::UINT8, Schema::from<List<uint8_t>>().whichElementType());
  EXPECT_EQ(schema::Type::UINT16, Schema::from<List<uint16_t>>().whichElementType());
  EXPECT_EQ(schema::Type::UINT32, Schema::from<List<uint32_t>>().whichElementType());
  EXPECT_EQ(schema::Type::UINT64, Schema::from<List<uint64_t>>().whichElementType());
  EXPECT_EQ(schema::Type::FLOAT32, Schema::from<List<float>>().whichElementType());
  EXPECT_EQ(schema::Type::FLOAT64, Schema::from<List<double>>().whichElementType());
  EXPECT_EQ(schema::Type::TEXT, Schema::from<List<Text>>().whichElementType());
  EXPECT_EQ(schema::Type::DATA, Schema::from<List<Data>>().whichElementType());
178

179 180 181 182
  EXPECT_NONFATAL_FAILURE(Schema::from<List<uint16_t>>().getStructElementType());
  EXPECT_NONFATAL_FAILURE(Schema::from<List<uint16_t>>().getEnumElementType());
  EXPECT_NONFATAL_FAILURE(Schema::from<List<uint16_t>>().getInterfaceElementType());
  EXPECT_NONFATAL_FAILURE(Schema::from<List<uint16_t>>().getListElementType());
183 184 185

  {
    ListSchema schema = Schema::from<List<TestAllTypes>>();
Kenton Varda's avatar
Kenton Varda committed
186
    EXPECT_EQ(schema::Type::STRUCT, schema.whichElementType());
187
    EXPECT_TRUE(schema.getStructElementType() == Schema::from<TestAllTypes>());
188 189 190
    EXPECT_NONFATAL_FAILURE(schema.getEnumElementType());
    EXPECT_NONFATAL_FAILURE(schema.getInterfaceElementType());
    EXPECT_NONFATAL_FAILURE(schema.getListElementType());
191 192 193 194
  }

  {
    ListSchema schema = Schema::from<List<TestEnum>>();
Kenton Varda's avatar
Kenton Varda committed
195
    EXPECT_EQ(schema::Type::ENUM, schema.whichElementType());
196
    EXPECT_TRUE(schema.getEnumElementType() == Schema::from<TestEnum>());
197 198 199
    EXPECT_NONFATAL_FAILURE(schema.getStructElementType());
    EXPECT_NONFATAL_FAILURE(schema.getInterfaceElementType());
    EXPECT_NONFATAL_FAILURE(schema.getListElementType());
200 201 202 203 204 205
  }

  // TODO(someday):  Test interfaces.

  {
    ListSchema schema = Schema::from<List<List<int32_t>>>();
Kenton Varda's avatar
Kenton Varda committed
206
    EXPECT_EQ(schema::Type::LIST, schema.whichElementType());
207 208 209
    EXPECT_NONFATAL_FAILURE(schema.getStructElementType());
    EXPECT_NONFATAL_FAILURE(schema.getEnumElementType());
    EXPECT_NONFATAL_FAILURE(schema.getInterfaceElementType());
210 211

    ListSchema inner = schema.getListElementType();
Kenton Varda's avatar
Kenton Varda committed
212
    EXPECT_EQ(schema::Type::INT32, inner.whichElementType());
213 214 215 216
  }

  {
    ListSchema schema = Schema::from<List<List<TestAllTypes>>>();
Kenton Varda's avatar
Kenton Varda committed
217
    EXPECT_EQ(schema::Type::LIST, schema.whichElementType());
218 219 220
    EXPECT_NONFATAL_FAILURE(schema.getStructElementType());
    EXPECT_NONFATAL_FAILURE(schema.getEnumElementType());
    EXPECT_NONFATAL_FAILURE(schema.getInterfaceElementType());
221 222

    ListSchema inner = schema.getListElementType();
Kenton Varda's avatar
Kenton Varda committed
223
    EXPECT_EQ(schema::Type::STRUCT, inner.whichElementType());
224 225 226 227 228
    EXPECT_TRUE(inner.getStructElementType() == Schema::from<TestAllTypes>());
  }

  {
    ListSchema schema = Schema::from<List<List<TestEnum>>>();
Kenton Varda's avatar
Kenton Varda committed
229
    EXPECT_EQ(schema::Type::LIST, schema.whichElementType());
230 231 232
    EXPECT_NONFATAL_FAILURE(schema.getStructElementType());
    EXPECT_NONFATAL_FAILURE(schema.getEnumElementType());
    EXPECT_NONFATAL_FAILURE(schema.getInterfaceElementType());
233 234

    ListSchema inner = schema.getListElementType();
Kenton Varda's avatar
Kenton Varda committed
235
    EXPECT_EQ(schema::Type::ENUM, inner.whichElementType());
236 237 238 239 240
    EXPECT_TRUE(inner.getEnumElementType() == Schema::from<TestEnum>());
  }

  {
    auto context = Schema::from<TestAllTypes>();
241
    auto type = context.getFieldByName("enumList").getProto().getSlot().getType();
242

243
    ListSchema schema = ListSchema::of(type.getList().getElementType(), context);
Kenton Varda's avatar
Kenton Varda committed
244
    EXPECT_EQ(schema::Type::ENUM, schema.whichElementType());
245
    EXPECT_TRUE(schema.getEnumElementType() == Schema::from<TestEnum>());
246 247 248
    EXPECT_NONFATAL_FAILURE(schema.getStructElementType());
    EXPECT_NONFATAL_FAILURE(schema.getInterfaceElementType());
    EXPECT_NONFATAL_FAILURE(schema.getListElementType());
249 250 251
  }
}

252 253 254
TEST(Schema, UnnamedUnion) {
  StructSchema schema = Schema::from<test::TestUnnamedUnion>();

255 256 257 258 259 260
  EXPECT_TRUE(schema.findFieldByName("") == nullptr);

  EXPECT_TRUE(schema.findFieldByName("foo") != nullptr);
  EXPECT_TRUE(schema.findFieldByName("bar") != nullptr);
  EXPECT_TRUE(schema.findFieldByName("before") != nullptr);
  EXPECT_TRUE(schema.findFieldByName("after") != nullptr);
261 262
}

263 264 265 266 267 268 269 270 271 272 273 274
TEST(Schema, NullSchemas) {
  EXPECT_EQ(0xff, (uint)Schema().getProto().which());
  EXPECT_TRUE(StructSchema().getProto().isStruct());
  EXPECT_TRUE(EnumSchema().getProto().isEnum());
  EXPECT_TRUE(InterfaceSchema().getProto().isInterface());
  EXPECT_TRUE(ConstSchema().getProto().isConst());

  EXPECT_EQ("(null schema)", Schema().getProto().getDisplayName());
  EXPECT_EQ("(null struct schema)", StructSchema().getProto().getDisplayName());
  EXPECT_EQ("(null enum schema)", EnumSchema().getProto().getDisplayName());
  EXPECT_EQ("(null interface schema)", InterfaceSchema().getProto().getDisplayName());
  EXPECT_EQ("(null const schema)", ConstSchema().getProto().getDisplayName());
275 276 277

  EXPECT_TRUE(Schema::from<Capability>() == InterfaceSchema());
  EXPECT_EQ(InterfaceSchema().getProto().getId(), typeId<Capability>());
278 279
}

280 281 282 283 284 285 286 287
TEST(Schema, Interfaces) {
  InterfaceSchema schema = Schema::from<test::TestMoreStuff>();

  EXPECT_EQ(typeId<test::TestMoreStuff>(), schema.getProto().getId());

  EXPECT_TRUE(schema.getDependency(typeId<test::TestCallOrder>()) ==
              Schema::from<test::TestCallOrder>());
  EXPECT_TRUE(schema.getDependency(typeId<test::TestCallOrder>()) != schema);
288
  EXPECT_NONFATAL_FAILURE(schema.getDependency(typeId<TestDefaults>()));
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

  EXPECT_TRUE(schema.asInterface() == schema);
  EXPECT_NONFATAL_FAILURE(schema.asStruct());
  EXPECT_NONFATAL_FAILURE(schema.asEnum());

  ASSERT_EQ(schema.getMethods().size(), schema.getProto().getInterface().getMethods().size());
  InterfaceSchema::Method method = schema.getMethods()[0];
  EXPECT_EQ("callFoo", method.getProto().getName());
  EXPECT_TRUE(method.getContainingInterface() == schema);

  InterfaceSchema::Method lookup = schema.getMethodByName("callFoo");
  EXPECT_TRUE(lookup == method);
  EXPECT_TRUE(lookup != schema.getMethods()[1]);

  EXPECT_TRUE(Schema::from<TestDefaults>().getFieldByName("int32Field")
      .getProto().getSlot().getHadExplicitDefault());

  EXPECT_TRUE(schema.findMethodByName("noSuchMethod") == nullptr);

  EXPECT_TRUE(schema.findMethodByName("callFooWhenResolved") != nullptr);
  EXPECT_TRUE(schema.findMethodByName("neverReturn") != nullptr);
  EXPECT_TRUE(schema.findMethodByName("hold") != nullptr);
  EXPECT_TRUE(schema.findMethodByName("callHeld") != nullptr);
  EXPECT_TRUE(schema.findMethodByName("getHeld") != nullptr);

  auto params = schema.getDependency(schema.getMethodByName("methodWithDefaults")
      .getProto().getParamStructType()).asStruct();
  EXPECT_FALSE(params.getFieldByName("a").getProto().getSlot().getHadExplicitDefault());
  EXPECT_TRUE(params.getFieldByName("b").getProto().getSlot().getHadExplicitDefault());
  EXPECT_TRUE(params.getFieldByName("c").getProto().getSlot().getHadExplicitDefault());
}

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
TEST(Schema, Generics) {
  StructSchema allTypes = Schema::from<TestAllTypes>();
  StructSchema tap = Schema::from<test::TestAnyPointer>();
  StructSchema schema = Schema::from<test::TestUseGenerics>();

  StructSchema branded;

  {
    StructSchema::Field basic = schema.getFieldByName("basic");
    branded = basic.getType().asStruct();

    StructSchema::Field foo = branded.getFieldByName("foo");
    EXPECT_TRUE(foo.getType().asStruct() == allTypes);
    EXPECT_TRUE(foo.getType().asStruct() != tap);

    StructSchema instance2 = branded.getFieldByName("rev").getType().asStruct();
    StructSchema::Field foo2 = instance2.getFieldByName("foo");
    EXPECT_TRUE(foo2.getType().asStruct() == tap);
    EXPECT_TRUE(foo2.getType().asStruct() != allTypes);
  }

  {
    StructSchema inner2 = schema.getFieldByName("inner2").getType().asStruct();

    StructSchema bound = inner2.getFieldByName("innerBound").getType().asStruct();
    Type boundFoo = bound.getFieldByName("foo").getType();
    EXPECT_FALSE(boundFoo.isAnyPointer());
    EXPECT_TRUE(boundFoo.asStruct() == allTypes);

    StructSchema unbound = inner2.getFieldByName("innerUnbound").getType().asStruct();
    Type unboundFoo = unbound.getFieldByName("foo").getType();
    EXPECT_TRUE(unboundFoo.isAnyPointer());
  }

  {
    InterfaceSchema cap = schema.getFieldByName("genericCap").getType().asInterface();
    InterfaceSchema::Method method = cap.getMethodByName("call");

    StructSchema inner2 = method.getParamType();
    StructSchema bound = inner2.getFieldByName("innerBound").getType().asStruct();
    Type boundFoo = bound.getFieldByName("foo").getType();
    EXPECT_FALSE(boundFoo.isAnyPointer());
    EXPECT_TRUE(boundFoo.asStruct() == allTypes);
    EXPECT_TRUE(inner2.getFieldByName("baz").getType().isText());

    StructSchema results = method.getResultType();
    EXPECT_TRUE(results.getFieldByName("qux").getType().isData());

    EXPECT_TRUE(results.getFieldByName("gen").getType().asStruct() == branded);
  }
}

373
}  // namespace
374
}  // namespace _ (private)
375
}  // namespace capnp