// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// 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:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// 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.

#include "test-util.h"
#include <kj/debug.h>
#include <kj/compat/gtest.h>

namespace capnp {
namespace _ {  // private
namespace {

template <typename Builder>
void genericInitTestMessage(Builder builder) {
  builder.setVoidField(VOID);
  builder.setVoidField();  // Means the same as above.
  builder.setBoolField(true);
  builder.setInt8Field(-123);
  builder.setInt16Field(-12345);
  builder.setInt32Field(-12345678);
  builder.setInt64Field(-123456789012345ll);
  builder.setUInt8Field(234u);
  builder.setUInt16Field(45678u);
  builder.setUInt32Field(3456789012u);
  builder.setUInt64Field(12345678901234567890ull);
  builder.setFloat32Field(1234.5);
  builder.setFloat64Field(-123e45);
  builder.setTextField("foo");
  builder.setDataField(data("bar"));
  {
    auto subBuilder = builder.initStructField();
    subBuilder.setVoidField(VOID);
    subBuilder.setBoolField(true);
    subBuilder.setInt8Field(-12);
    subBuilder.setInt16Field(3456);
    subBuilder.setInt32Field(-78901234);
    subBuilder.setInt64Field(56789012345678ll);
    subBuilder.setUInt8Field(90u);
    subBuilder.setUInt16Field(1234u);
    subBuilder.setUInt32Field(56789012u);
    subBuilder.setUInt64Field(345678901234567890ull);
    subBuilder.setFloat32Field(-1.25e-10f);
    subBuilder.setFloat64Field(345);
    subBuilder.setTextField("baz");
    subBuilder.setDataField(data("qux"));
    {
      auto subSubBuilder = subBuilder.initStructField();
      subSubBuilder.setTextField("nested");
      subSubBuilder.initStructField().setTextField("really nested");
    }
    subBuilder.setEnumField(TestEnum::BAZ);

    subBuilder.setVoidList({VOID, VOID, VOID});
    subBuilder.setBoolList({false, true, false, true, true});
    subBuilder.setInt8List({12, -34, -0x80, 0x7f});
    subBuilder.setInt16List({1234, -5678, -0x8000, 0x7fff});
    // gcc warns on -0x800... and the only work-around I could find was to do -0x7ff...-1.
    subBuilder.setInt32List({12345678, -90123456, -0x7fffffff - 1, 0x7fffffff});
    subBuilder.setInt64List({123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
    subBuilder.setUInt8List({12u, 34u, 0u, 0xffu});
    subBuilder.setUInt16List({1234u, 5678u, 0u, 0xffffu});
    subBuilder.setUInt32List({12345678u, 90123456u, 0u, 0xffffffffu});
    subBuilder.setUInt64List({123456789012345ull, 678901234567890ull, 0ull, 0xffffffffffffffffull});
    subBuilder.setFloat32List({0, 1234567, 1e37f, -1e37f, 1e-37f, -1e-37f});
    subBuilder.setFloat64List({0, 123456789012345, 1e306, -1e306, 1e-306, -1e-306});
    subBuilder.setTextList({"quux", "corge", "grault"});
    subBuilder.setDataList({data("garply"), data("waldo"), data("fred")});
    {
      auto listBuilder = subBuilder.initStructList(3);
      listBuilder[0].setTextField("x structlist 1");
      listBuilder[1].setTextField("x structlist 2");
      listBuilder[2].setTextField("x structlist 3");
    }
    subBuilder.setEnumList({TestEnum::QUX, TestEnum::BAR, TestEnum::GRAULT});
  }
  builder.setEnumField(TestEnum::CORGE);

  builder.initVoidList(6);
  builder.setBoolList({true, false, false, true});
  builder.setInt8List({111, -111});
  builder.setInt16List({11111, -11111});
  builder.setInt32List({111111111, -111111111});
  builder.setInt64List({1111111111111111111ll, -1111111111111111111ll});
  builder.setUInt8List({111u, 222u});
  builder.setUInt16List({33333u, 44444u});
  builder.setUInt32List({3333333333u});
  builder.setUInt64List({11111111111111111111ull});
  builder.setFloat32List({5555.5, kj::inf(), -kj::inf(), kj::nan()});
  builder.setFloat64List({7777.75, kj::inf(), -kj::inf(), kj::nan()});
  builder.setTextList({"plugh", "xyzzy", "thud"});
  builder.setDataList({data("oops"), data("exhausted"), data("rfc3092")});
  {
    auto listBuilder = builder.initStructList(3);
    listBuilder[0].setTextField("structlist 1");
    listBuilder[1].setTextField("structlist 2");
    listBuilder[2].setTextField("structlist 3");
  }
  builder.setEnumList({TestEnum::FOO, TestEnum::GARPLY});
}

#if !CAPNP_LITE

void dynamicInitTestMessage(DynamicStruct::Builder builder) {
  builder.set("voidField", VOID);
  builder.set("boolField", true);
  builder.set("int8Field", -123);
  builder.set("int16Field", -12345);
  builder.set("int32Field", -12345678);
  builder.set("int64Field", -123456789012345ll);
  builder.set("uInt8Field", 234u);
  builder.set("uInt16Field", 45678u);
  builder.set("uInt32Field", 3456789012u);
  builder.set("uInt64Field", 12345678901234567890ull);
  builder.set("float32Field", 1234.5);
  builder.set("float64Field", -123e45);
  builder.set("textField", "foo");
  builder.set("dataField", data("bar"));
  {
    auto subBuilder = builder.init("structField").as<DynamicStruct>();
    subBuilder.set("voidField", VOID);
    subBuilder.set("boolField", true);
    subBuilder.set("int8Field", -12);
    subBuilder.set("int16Field", 3456);
    subBuilder.set("int32Field", -78901234);
    subBuilder.set("int64Field", 56789012345678ll);
    subBuilder.set("uInt8Field", 90u);
    subBuilder.set("uInt16Field", 1234u);
    subBuilder.set("uInt32Field", 56789012u);
    subBuilder.set("uInt64Field", 345678901234567890ull);
    subBuilder.set("float32Field", -1.25e-10);
    subBuilder.set("float64Field", 345);
    subBuilder.set("textField", "baz");
    subBuilder.set("dataField", data("qux"));
    {
      auto subSubBuilder = subBuilder.init("structField").as<DynamicStruct>();
      subSubBuilder.set("textField", "nested");
      subSubBuilder.init("structField").as<DynamicStruct>().set("textField", "really nested");
    }
    subBuilder.set("enumField", "baz");

    subBuilder.set("voidList", {VOID, VOID, VOID});
    subBuilder.set("boolList", {false, true, false, true, true});
    subBuilder.set("int8List", {12, -34, -0x80, 0x7f});
    subBuilder.set("int16List", {1234, -5678, -0x8000, 0x7fff});
    // gcc warns on -0x800... and the only work-around I could find was to do -0x7ff...-1.
    subBuilder.set("int32List", {12345678, -90123456, -0x7fffffff - 1, 0x7fffffff});
    subBuilder.set("int64List", {123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
    subBuilder.set("uInt8List", {12u, 34u, 0u, 0xffu});
    subBuilder.set("uInt16List", {1234u, 5678u, 0u, 0xffffu});
    subBuilder.set("uInt32List", {12345678u, 90123456u, 0u, 0xffffffffu});
    subBuilder.set("uInt64List", {123456789012345ull, 678901234567890ull, 0ull, 0xffffffffffffffffull});
    subBuilder.set("float32List", {0, 1234567, 1e37, -1e37, 1e-37, -1e-37});
    subBuilder.set("float64List", {0, 123456789012345, 1e306, -1e306, 1e-306, -1e-306});
    subBuilder.set("textList", {"quux", "corge", "grault"});
    subBuilder.set("dataList", {data("garply"), data("waldo"), data("fred")});
    {
      auto listBuilder = subBuilder.init("structList", 3).as<DynamicList>();
      listBuilder[0].as<DynamicStruct>().set("textField", "x structlist 1");
      listBuilder[1].as<DynamicStruct>().set("textField", "x structlist 2");
      listBuilder[2].as<DynamicStruct>().set("textField", "x structlist 3");
    }
    subBuilder.set("enumList", {"qux", "bar", "grault"});
  }
  builder.set("enumField", "corge");

  builder.init("voidList", 6);
  builder.set("boolList", {true, false, false, true});
  builder.set("int8List", {111, -111});
  builder.set("int16List", {11111, -11111});
  builder.set("int32List", {111111111, -111111111});
  builder.set("int64List", {1111111111111111111ll, -1111111111111111111ll});
  builder.set("uInt8List", {111u, 222u});
  builder.set("uInt16List", {33333u, 44444u});
  builder.set("uInt32List", {3333333333u});
  builder.set("uInt64List", {11111111111111111111ull});
  builder.set("float32List", {5555.5, kj::inf(), -kj::inf(), kj::nan()});
  builder.set("float64List", {7777.75, kj::inf(), -kj::inf(), kj::nan()});
  builder.set("textList", {"plugh", "xyzzy", "thud"});
  builder.set("dataList", {data("oops"), data("exhausted"), data("rfc3092")});
  {
    auto listBuilder = builder.init("structList", 3).as<DynamicList>();
    listBuilder[0].as<DynamicStruct>().set("textField", "structlist 1");
    listBuilder[1].as<DynamicStruct>().set("textField", "structlist 2");
    listBuilder[2].as<DynamicStruct>().set("textField", "structlist 3");
  }
  builder.set("enumList", {"foo", "garply"});
}

#endif  // !CAPNP_LITE

inline bool isNaN(float f) { return f != f; }
inline bool isNaN(double f) { return f != f; }

template <typename Reader>
void genericCheckTestMessage(Reader reader) {
  EXPECT_EQ(VOID, reader.getVoidField());
  EXPECT_EQ(true, reader.getBoolField());
  EXPECT_EQ(-123, reader.getInt8Field());
  EXPECT_EQ(-12345, reader.getInt16Field());
  EXPECT_EQ(-12345678, reader.getInt32Field());
  EXPECT_EQ(-123456789012345ll, reader.getInt64Field());
  EXPECT_EQ(234u, reader.getUInt8Field());
  EXPECT_EQ(45678u, reader.getUInt16Field());
  EXPECT_EQ(3456789012u, reader.getUInt32Field());
  EXPECT_EQ(12345678901234567890ull, reader.getUInt64Field());
  EXPECT_FLOAT_EQ(1234.5f, reader.getFloat32Field());
  EXPECT_DOUBLE_EQ(-123e45, reader.getFloat64Field());
  EXPECT_EQ("foo", reader.getTextField());
  EXPECT_EQ(data("bar"), reader.getDataField());
  {
    auto subReader = reader.getStructField();
    EXPECT_EQ(VOID, subReader.getVoidField());
    EXPECT_EQ(true, subReader.getBoolField());
    EXPECT_EQ(-12, subReader.getInt8Field());
    EXPECT_EQ(3456, subReader.getInt16Field());
    EXPECT_EQ(-78901234, subReader.getInt32Field());
    EXPECT_EQ(56789012345678ll, subReader.getInt64Field());
    EXPECT_EQ(90u, subReader.getUInt8Field());
    EXPECT_EQ(1234u, subReader.getUInt16Field());
    EXPECT_EQ(56789012u, subReader.getUInt32Field());
    EXPECT_EQ(345678901234567890ull, subReader.getUInt64Field());
    EXPECT_FLOAT_EQ(-1.25e-10f, subReader.getFloat32Field());
    EXPECT_DOUBLE_EQ(345, subReader.getFloat64Field());
    EXPECT_EQ("baz", subReader.getTextField());
    EXPECT_EQ(data("qux"), subReader.getDataField());
    {
      auto subSubReader = subReader.getStructField();
      EXPECT_EQ("nested", subSubReader.getTextField());
      EXPECT_EQ("really nested", subSubReader.getStructField().getTextField());
    }
    EXPECT_EQ(TestEnum::BAZ, subReader.getEnumField());

    checkList(subReader.getVoidList(), {VOID, VOID, VOID});
    checkList(subReader.getBoolList(), {false, true, false, true, true});
    checkList(subReader.getInt8List(), {12, -34, -0x80, 0x7f});
    checkList(subReader.getInt16List(), {1234, -5678, -0x8000, 0x7fff});
    // gcc warns on -0x800... and the only work-around I could find was to do -0x7ff...-1.
    checkList(subReader.getInt32List(), {12345678, -90123456, -0x7fffffff - 1, 0x7fffffff});
    checkList(subReader.getInt64List(), {123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
    checkList(subReader.getUInt8List(), {12u, 34u, 0u, 0xffu});
    checkList(subReader.getUInt16List(), {1234u, 5678u, 0u, 0xffffu});
    checkList(subReader.getUInt32List(), {12345678u, 90123456u, 0u, 0xffffffffu});
    checkList(subReader.getUInt64List(), {123456789012345ull, 678901234567890ull, 0ull, 0xffffffffffffffffull});
    checkList(subReader.getFloat32List(), {0.0f, 1234567.0f, 1e37f, -1e37f, 1e-37f, -1e-37f});
    checkList(subReader.getFloat64List(), {0.0, 123456789012345.0, 1e306, -1e306, 1e-306, -1e-306});
    checkList(subReader.getTextList(), {"quux", "corge", "grault"});
    checkList(subReader.getDataList(), {data("garply"), data("waldo"), data("fred")});
    {
      auto listReader = subReader.getStructList();
      ASSERT_EQ(3u, listReader.size());
      EXPECT_EQ("x structlist 1", listReader[0].getTextField());
      EXPECT_EQ("x structlist 2", listReader[1].getTextField());
      EXPECT_EQ("x structlist 3", listReader[2].getTextField());
    }
    checkList(subReader.getEnumList(), {TestEnum::QUX, TestEnum::BAR, TestEnum::GRAULT});
  }
  EXPECT_EQ(TestEnum::CORGE, reader.getEnumField());

  EXPECT_EQ(6u, reader.getVoidList().size());
  checkList(reader.getBoolList(), {true, false, false, true});
  checkList(reader.getInt8List(), {111, -111});
  checkList(reader.getInt16List(), {11111, -11111});
  checkList(reader.getInt32List(), {111111111, -111111111});
  checkList(reader.getInt64List(), {1111111111111111111ll, -1111111111111111111ll});
  checkList(reader.getUInt8List(), {111u, 222u});
  checkList(reader.getUInt16List(), {33333u, 44444u});
  checkList(reader.getUInt32List(), {3333333333u});
  checkList(reader.getUInt64List(), {11111111111111111111ull});
  {
    auto listReader = reader.getFloat32List();
    ASSERT_EQ(4u, listReader.size());
    EXPECT_EQ(5555.5f, listReader[0]);
    EXPECT_EQ(kj::inf(), listReader[1]);
    EXPECT_EQ(-kj::inf(), listReader[2]);
    EXPECT_TRUE(isNaN(listReader[3]));
  }
  {
    auto listReader = reader.getFloat64List();
    ASSERT_EQ(4u, listReader.size());
    EXPECT_EQ(7777.75, listReader[0]);
    EXPECT_EQ(kj::inf(), listReader[1]);
    EXPECT_EQ(-kj::inf(), listReader[2]);
    EXPECT_TRUE(isNaN(listReader[3]));
  }
  checkList(reader.getTextList(), {"plugh", "xyzzy", "thud"});
  checkList(reader.getDataList(), {data("oops"), data("exhausted"), data("rfc3092")});
  {
    auto listReader = reader.getStructList();
    ASSERT_EQ(3u, listReader.size());
    EXPECT_EQ("structlist 1", listReader[0].getTextField());
    EXPECT_EQ("structlist 2", listReader[1].getTextField());
    EXPECT_EQ("structlist 3", listReader[2].getTextField());
  }
  checkList(reader.getEnumList(), {TestEnum::FOO, TestEnum::GARPLY});
}

#if !CAPNP_LITE

// Hack because as<>() is a template-parameter-dependent lookup everywhere below...
#define as template as

Text::Reader name(DynamicEnum e) {
  KJ_IF_MAYBE(schema, e.getEnumerant()) {
    return schema->getProto().getName();
  } else {
    return "(unknown enumerant)";
  }
}

template <typename T>
void checkEnumList(T reader, std::initializer_list<const char*> expected) {
  auto list = reader.as<DynamicList>();
  ASSERT_EQ(expected.size(), list.size());
  for (uint i = 0; i < expected.size(); i++) {
    EXPECT_EQ(expected.begin()[i], name(list[i].as<DynamicEnum>()));
  }
}

template <typename Reader>
void dynamicCheckTestMessage(Reader reader) {
  EXPECT_EQ(VOID, reader.get("voidField").as<Void>());
  EXPECT_EQ(true, reader.get("boolField").as<bool>());
  EXPECT_EQ(-123, reader.get("int8Field").as<int8_t>());
  EXPECT_EQ(-12345, reader.get("int16Field").as<int16_t>());
  EXPECT_EQ(-12345678, reader.get("int32Field").as<int32_t>());
  EXPECT_EQ(-123456789012345ll, reader.get("int64Field").as<int64_t>());
  EXPECT_EQ(234u, reader.get("uInt8Field").as<uint8_t>());
  EXPECT_EQ(45678u, reader.get("uInt16Field").as<uint16_t>());
  EXPECT_EQ(3456789012u, reader.get("uInt32Field").as<uint32_t>());
  EXPECT_EQ(12345678901234567890ull, reader.get("uInt64Field").as<uint64_t>());
  EXPECT_FLOAT_EQ(1234.5f, reader.get("float32Field").as<float>());
  EXPECT_DOUBLE_EQ(-123e45, reader.get("float64Field").as<double>());
  EXPECT_EQ("foo", reader.get("textField").as<Text>());
  EXPECT_EQ(data("bar"), reader.get("dataField").as<Data>());
  {
    auto subReader = reader.get("structField").as<DynamicStruct>();
    EXPECT_EQ(VOID, subReader.get("voidField").as<Void>());
    EXPECT_EQ(true, subReader.get("boolField").as<bool>());
    EXPECT_EQ(-12, subReader.get("int8Field").as<int8_t>());
    EXPECT_EQ(3456, subReader.get("int16Field").as<int16_t>());
    EXPECT_EQ(-78901234, subReader.get("int32Field").as<int32_t>());
    EXPECT_EQ(56789012345678ll, subReader.get("int64Field").as<int64_t>());
    EXPECT_EQ(90u, subReader.get("uInt8Field").as<uint8_t>());
    EXPECT_EQ(1234u, subReader.get("uInt16Field").as<uint16_t>());
    EXPECT_EQ(56789012u, subReader.get("uInt32Field").as<uint32_t>());
    EXPECT_EQ(345678901234567890ull, subReader.get("uInt64Field").as<uint64_t>());
    EXPECT_FLOAT_EQ(-1.25e-10f, subReader.get("float32Field").as<float>());
    EXPECT_DOUBLE_EQ(345, subReader.get("float64Field").as<double>());
    EXPECT_EQ("baz", subReader.get("textField").as<Text>());
    EXPECT_EQ(data("qux"), subReader.get("dataField").as<Data>());
    {
      auto subSubReader = subReader.get("structField").as<DynamicStruct>();
      EXPECT_EQ("nested", subSubReader.get("textField").as<Text>());
      EXPECT_EQ("really nested", subSubReader.get("structField").as<DynamicStruct>()
                                             .get("textField").as<Text>());
    }
    EXPECT_EQ("baz", name(subReader.get("enumField").as<DynamicEnum>()));

    checkList<Void>(subReader.get("voidList"), {VOID, VOID, VOID});
    checkList<bool>(subReader.get("boolList"), {false, true, false, true, true});
    checkList<int8_t>(subReader.get("int8List"), {12, -34, -0x80, 0x7f});
    checkList<int16_t>(subReader.get("int16List"), {1234, -5678, -0x8000, 0x7fff});
    // gcc warns on -0x800... and the only work-around I could find was to do -0x7ff...-1.
    checkList<int32_t>(subReader.get("int32List"), {12345678, -90123456, -0x7fffffff-1, 0x7fffffff});
    checkList<int64_t>(subReader.get("int64List"), {123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
    checkList<uint8_t>(subReader.get("uInt8List"), {12u, 34u, 0u, 0xffu});
    checkList<uint16_t>(subReader.get("uInt16List"), {1234u, 5678u, 0u, 0xffffu});
    checkList<uint32_t>(subReader.get("uInt32List"), {12345678u, 90123456u, 0u, 0xffffffffu});
    checkList<uint64_t>(subReader.get("uInt64List"), {123456789012345ull, 678901234567890ull, 0ull, 0xffffffffffffffffull});
    checkList<float>(subReader.get("float32List"), {0.0f, 1234567.0f, 1e37f, -1e37f, 1e-37f, -1e-37f});
    checkList<double>(subReader.get("float64List"), {0.0, 123456789012345.0, 1e306, -1e306, 1e-306, -1e-306});
    checkList<Text>(subReader.get("textList"), {"quux", "corge", "grault"});
    checkList<Data>(subReader.get("dataList"), {data("garply"), data("waldo"), data("fred")});
    {
      auto listReader = subReader.get("structList").as<DynamicList>();
      ASSERT_EQ(3u, listReader.size());
      EXPECT_EQ("x structlist 1", listReader[0].as<DynamicStruct>().get("textField").as<Text>());
      EXPECT_EQ("x structlist 2", listReader[1].as<DynamicStruct>().get("textField").as<Text>());
      EXPECT_EQ("x structlist 3", listReader[2].as<DynamicStruct>().get("textField").as<Text>());
    }
    checkEnumList(subReader.get("enumList"), {"qux", "bar", "grault"});
  }
  EXPECT_EQ("corge", name(reader.get("enumField").as<DynamicEnum>()));

  EXPECT_EQ(6u, reader.get("voidList").as<DynamicList>().size());
  checkList<bool>(reader.get("boolList"), {true, false, false, true});
  checkList<int8_t>(reader.get("int8List"), {111, -111});
  checkList<int16_t>(reader.get("int16List"), {11111, -11111});
  checkList<int32_t>(reader.get("int32List"), {111111111, -111111111});
  checkList<int64_t>(reader.get("int64List"), {1111111111111111111ll, -1111111111111111111ll});
  checkList<uint8_t>(reader.get("uInt8List"), {111u, 222u});
  checkList<uint16_t>(reader.get("uInt16List"), {33333u, 44444u});
  checkList<uint32_t>(reader.get("uInt32List"), {3333333333u});
  checkList<uint64_t>(reader.get("uInt64List"), {11111111111111111111ull});
  {
    auto listReader = reader.get("float32List").as<DynamicList>();
    ASSERT_EQ(4u, listReader.size());
    EXPECT_EQ(5555.5f, listReader[0].as<float>());
    EXPECT_EQ(kj::inf(), listReader[1].as<float>());
    EXPECT_EQ(-kj::inf(), listReader[2].as<float>());
    EXPECT_TRUE(isNaN(listReader[3].as<float>()));
  }
  {
    auto listReader = reader.get("float64List").as<DynamicList>();
    ASSERT_EQ(4u, listReader.size());
    EXPECT_EQ(7777.75, listReader[0].as<double>());
    EXPECT_EQ(kj::inf(), listReader[1].as<double>());
    EXPECT_EQ(-kj::inf(), listReader[2].as<double>());
    EXPECT_TRUE(isNaN(listReader[3].as<double>()));
  }
  checkList<Text>(reader.get("textList"), {"plugh", "xyzzy", "thud"});
  checkList<Data>(reader.get("dataList"), {data("oops"), data("exhausted"), data("rfc3092")});
  {
    auto listReader = reader.get("structList").as<DynamicList>();
    ASSERT_EQ(3u, listReader.size());
    EXPECT_EQ("structlist 1", listReader[0].as<DynamicStruct>().get("textField").as<Text>());
    EXPECT_EQ("structlist 2", listReader[1].as<DynamicStruct>().get("textField").as<Text>());
    EXPECT_EQ("structlist 3", listReader[2].as<DynamicStruct>().get("textField").as<Text>());
  }
  checkEnumList(reader.get("enumList"), {"foo", "garply"});
}

#undef as

#endif  // !CAPNP_LITE

template <typename Reader>
void genericCheckTestMessageAllZero(Reader reader) {
  EXPECT_EQ(VOID, reader.getVoidField());
  EXPECT_EQ(false, reader.getBoolField());
  EXPECT_EQ(0, reader.getInt8Field());
  EXPECT_EQ(0, reader.getInt16Field());
  EXPECT_EQ(0, reader.getInt32Field());
  EXPECT_EQ(0, reader.getInt64Field());
  EXPECT_EQ(0u, reader.getUInt8Field());
  EXPECT_EQ(0u, reader.getUInt16Field());
  EXPECT_EQ(0u, reader.getUInt32Field());
  EXPECT_EQ(0u, reader.getUInt64Field());
  EXPECT_FLOAT_EQ(0, reader.getFloat32Field());
  EXPECT_DOUBLE_EQ(0, reader.getFloat64Field());
  EXPECT_EQ("", reader.getTextField());
  EXPECT_EQ(data(""), reader.getDataField());
  {
    auto subReader = reader.getStructField();
    EXPECT_EQ(VOID, subReader.getVoidField());
    EXPECT_EQ(false, subReader.getBoolField());
    EXPECT_EQ(0, subReader.getInt8Field());
    EXPECT_EQ(0, subReader.getInt16Field());
    EXPECT_EQ(0, subReader.getInt32Field());
    EXPECT_EQ(0, subReader.getInt64Field());
    EXPECT_EQ(0u, subReader.getUInt8Field());
    EXPECT_EQ(0u, subReader.getUInt16Field());
    EXPECT_EQ(0u, subReader.getUInt32Field());
    EXPECT_EQ(0u, subReader.getUInt64Field());
    EXPECT_FLOAT_EQ(0, subReader.getFloat32Field());
    EXPECT_DOUBLE_EQ(0, subReader.getFloat64Field());
    EXPECT_EQ("", subReader.getTextField());
    EXPECT_EQ(data(""), subReader.getDataField());
    {
      auto subSubReader = subReader.getStructField();
      EXPECT_EQ("", subSubReader.getTextField());
      EXPECT_EQ("", subSubReader.getStructField().getTextField());
    }

    EXPECT_EQ(0u, subReader.getVoidList().size());
    EXPECT_EQ(0u, subReader.getBoolList().size());
    EXPECT_EQ(0u, subReader.getInt8List().size());
    EXPECT_EQ(0u, subReader.getInt16List().size());
    EXPECT_EQ(0u, subReader.getInt32List().size());
    EXPECT_EQ(0u, subReader.getInt64List().size());
    EXPECT_EQ(0u, subReader.getUInt8List().size());
    EXPECT_EQ(0u, subReader.getUInt16List().size());
    EXPECT_EQ(0u, subReader.getUInt32List().size());
    EXPECT_EQ(0u, subReader.getUInt64List().size());
    EXPECT_EQ(0u, subReader.getFloat32List().size());
    EXPECT_EQ(0u, subReader.getFloat64List().size());
    EXPECT_EQ(0u, subReader.getTextList().size());
    EXPECT_EQ(0u, subReader.getDataList().size());
    EXPECT_EQ(0u, subReader.getStructList().size());
  }

  EXPECT_EQ(0u, reader.getVoidList().size());
  EXPECT_EQ(0u, reader.getBoolList().size());
  EXPECT_EQ(0u, reader.getInt8List().size());
  EXPECT_EQ(0u, reader.getInt16List().size());
  EXPECT_EQ(0u, reader.getInt32List().size());
  EXPECT_EQ(0u, reader.getInt64List().size());
  EXPECT_EQ(0u, reader.getUInt8List().size());
  EXPECT_EQ(0u, reader.getUInt16List().size());
  EXPECT_EQ(0u, reader.getUInt32List().size());
  EXPECT_EQ(0u, reader.getUInt64List().size());
  EXPECT_EQ(0u, reader.getFloat32List().size());
  EXPECT_EQ(0u, reader.getFloat64List().size());
  EXPECT_EQ(0u, reader.getTextList().size());
  EXPECT_EQ(0u, reader.getDataList().size());
  EXPECT_EQ(0u, reader.getStructList().size());
}

#if !CAPNP_LITE

// Hack because as<>() is a template-parameter-dependent lookup everywhere below...
#define as template as

template <typename Reader>
void dynamicCheckTestMessageAllZero(Reader reader) {
  EXPECT_EQ(VOID, reader.get("voidField").as<Void>());
  EXPECT_EQ(false, reader.get("boolField").as<bool>());
  EXPECT_EQ(0, reader.get("int8Field").as<int8_t>());
  EXPECT_EQ(0, reader.get("int16Field").as<int16_t>());
  EXPECT_EQ(0, reader.get("int32Field").as<int32_t>());
  EXPECT_EQ(0, reader.get("int64Field").as<int64_t>());
  EXPECT_EQ(0u, reader.get("uInt8Field").as<uint8_t>());
  EXPECT_EQ(0u, reader.get("uInt16Field").as<uint16_t>());
  EXPECT_EQ(0u, reader.get("uInt32Field").as<uint32_t>());
  EXPECT_EQ(0u, reader.get("uInt64Field").as<uint64_t>());
  EXPECT_FLOAT_EQ(0, reader.get("float32Field").as<float>());
  EXPECT_DOUBLE_EQ(0, reader.get("float64Field").as<double>());
  EXPECT_EQ("", reader.get("textField").as<Text>());
  EXPECT_EQ(data(""), reader.get("dataField").as<Data>());
  {
    auto subReader = reader.get("structField").as<DynamicStruct>();
    EXPECT_EQ(VOID, subReader.get("voidField").as<Void>());
    EXPECT_EQ(false, subReader.get("boolField").as<bool>());
    EXPECT_EQ(0, subReader.get("int8Field").as<int8_t>());
    EXPECT_EQ(0, subReader.get("int16Field").as<int16_t>());
    EXPECT_EQ(0, subReader.get("int32Field").as<int32_t>());
    EXPECT_EQ(0, subReader.get("int64Field").as<int64_t>());
    EXPECT_EQ(0u, subReader.get("uInt8Field").as<uint8_t>());
    EXPECT_EQ(0u, subReader.get("uInt16Field").as<uint16_t>());
    EXPECT_EQ(0u, subReader.get("uInt32Field").as<uint32_t>());
    EXPECT_EQ(0u, subReader.get("uInt64Field").as<uint64_t>());
    EXPECT_FLOAT_EQ(0, subReader.get("float32Field").as<float>());
    EXPECT_DOUBLE_EQ(0, subReader.get("float64Field").as<double>());
    EXPECT_EQ("", subReader.get("textField").as<Text>());
    EXPECT_EQ(data(""), subReader.get("dataField").as<Data>());
    {
      auto subSubReader = subReader.get("structField").as<DynamicStruct>();
      EXPECT_EQ("", subSubReader.get("textField").as<Text>());
      EXPECT_EQ("", subSubReader.get("structField").as<DynamicStruct>()
                                .get("textField").as<Text>());
    }

    EXPECT_EQ(0u, subReader.get("voidList").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("boolList").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("int8List").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("int16List").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("int32List").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("int64List").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("uInt8List").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("uInt16List").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("uInt32List").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("uInt64List").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("float32List").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("float64List").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("textList").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("dataList").as<DynamicList>().size());
    EXPECT_EQ(0u, subReader.get("structList").as<DynamicList>().size());
  }

  EXPECT_EQ(0u, reader.get("voidList").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("boolList").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("int8List").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("int16List").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("int32List").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("int64List").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("uInt8List").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("uInt16List").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("uInt32List").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("uInt64List").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("float32List").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("float64List").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("textList").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("dataList").as<DynamicList>().size());
  EXPECT_EQ(0u, reader.get("structList").as<DynamicList>().size());
}

#undef as

#endif  // !CAPNP_LITE

template <typename Builder>
void genericInitListDefaults(Builder builder) {
  auto lists = builder.initLists();

  lists.initList0(2);
  lists.initList1(4);
  lists.initList8(2);
  lists.initList16(2);
  lists.initList32(2);
  lists.initList64(2);
  lists.initListP(2);

  lists.getList0()[0].setF(VOID);
  lists.getList0()[1].setF(VOID);
  lists.getList1()[0].setF(true);
  lists.getList1()[1].setF(false);
  lists.getList1()[2].setF(true);
  lists.getList1()[3].setF(true);
  lists.getList8()[0].setF(123u);
  lists.getList8()[1].setF(45u);
  lists.getList16()[0].setF(12345u);
  lists.getList16()[1].setF(6789u);
  lists.getList32()[0].setF(123456789u);
  lists.getList32()[1].setF(234567890u);
  lists.getList64()[0].setF(1234567890123456u);
  lists.getList64()[1].setF(2345678901234567u);
  lists.getListP()[0].setF("foo");
  lists.getListP()[1].setF("bar");

  {
    auto l = lists.initInt32ListList(3);
    l.set(0, {1, 2, 3});
    l.set(1, {4, 5});
    l.set(2, {12341234});
  }

  {
    auto l = lists.initTextListList(3);
    l.set(0, {"foo", "bar"});
    l.set(1, {"baz"});
    l.set(2, {"qux", "corge"});
  }

  {
    auto l = lists.initStructListList(2);
    auto e = l.init(0, 2);
    e[0].setInt32Field(123);
    e[1].setInt32Field(456);
    e = l.init(1, 1);
    e[0].setInt32Field(789);
  }
}

#if !CAPNP_LITE

void dynamicInitListDefaults(DynamicStruct::Builder builder) {
  auto lists = builder.init("lists").as<DynamicStruct>();

  lists.init("list0", 2);
  lists.init("list1", 4);
  lists.init("list8", 2);
  lists.init("list16", 2);
  lists.init("list32", 2);
  lists.init("list64", 2);
  lists.init("listP", 2);

  lists.get("list0").as<DynamicList>()[0].as<DynamicStruct>().set("f", VOID);
  lists.get("list0").as<DynamicList>()[1].as<DynamicStruct>().set("f", VOID);
  lists.get("list1").as<DynamicList>()[0].as<DynamicStruct>().set("f", true);
  lists.get("list1").as<DynamicList>()[1].as<DynamicStruct>().set("f", false);
  lists.get("list1").as<DynamicList>()[2].as<DynamicStruct>().set("f", true);
  lists.get("list1").as<DynamicList>()[3].as<DynamicStruct>().set("f", true);
  lists.get("list8").as<DynamicList>()[0].as<DynamicStruct>().set("f", 123u);
  lists.get("list8").as<DynamicList>()[1].as<DynamicStruct>().set("f", 45u);
  lists.get("list16").as<DynamicList>()[0].as<DynamicStruct>().set("f", 12345u);
  lists.get("list16").as<DynamicList>()[1].as<DynamicStruct>().set("f", 6789u);
  lists.get("list32").as<DynamicList>()[0].as<DynamicStruct>().set("f", 123456789u);
  lists.get("list32").as<DynamicList>()[1].as<DynamicStruct>().set("f", 234567890u);
  lists.get("list64").as<DynamicList>()[0].as<DynamicStruct>().set("f", 1234567890123456u);
  lists.get("list64").as<DynamicList>()[1].as<DynamicStruct>().set("f", 2345678901234567u);
  lists.get("listP").as<DynamicList>()[0].as<DynamicStruct>().set("f", "foo");
  lists.get("listP").as<DynamicList>()[1].as<DynamicStruct>().set("f", "bar");

  {
    auto l = lists.init("int32ListList", 3).as<DynamicList>();
    l.init(0, 3).as<DynamicList>().copyFrom({1, 2, 3});
    l.init(1, 2).as<DynamicList>().copyFrom({4, 5});
    l.init(2, 1).as<DynamicList>().copyFrom({12341234});
  }

  {
    auto l = lists.init("textListList", 3).as<DynamicList>();
    l.init(0, 2).as<DynamicList>().copyFrom({"foo", "bar"});
    l.init(1, 1).as<DynamicList>().copyFrom({"baz"});
    l.init(2, 2).as<DynamicList>().copyFrom({"qux", "corge"});
  }

  {
    auto l = lists.init("structListList", 2).as<DynamicList>();
    auto e = l.init(0, 2).as<DynamicList>();
    e[0].as<TestAllTypes>().setInt32Field(123);
    e[1].as<TestAllTypes>().setInt32Field(456);
    e = l.init(1, 1).as<DynamicList>();
    e[0].as<TestAllTypes>().setInt32Field(789);
  }
}

#endif  // !CAPNP_LITE

template <typename Reader>
void genericCheckListDefaults(Reader reader) {
  auto lists = reader.getLists();

  ASSERT_EQ(2u, lists.getList0().size());
  ASSERT_EQ(4u, lists.getList1().size());
  ASSERT_EQ(2u, lists.getList8().size());
  ASSERT_EQ(2u, lists.getList16().size());
  ASSERT_EQ(2u, lists.getList32().size());
  ASSERT_EQ(2u, lists.getList64().size());
  ASSERT_EQ(2u, lists.getListP().size());

  EXPECT_EQ(VOID, lists.getList0()[0].getF());
  EXPECT_EQ(VOID, lists.getList0()[1].getF());
  EXPECT_TRUE(lists.getList1()[0].getF());
  EXPECT_FALSE(lists.getList1()[1].getF());
  EXPECT_TRUE(lists.getList1()[2].getF());
  EXPECT_TRUE(lists.getList1()[3].getF());
  EXPECT_EQ(123u, lists.getList8()[0].getF());
  EXPECT_EQ(45u, lists.getList8()[1].getF());
  EXPECT_EQ(12345u, lists.getList16()[0].getF());
  EXPECT_EQ(6789u, lists.getList16()[1].getF());
  EXPECT_EQ(123456789u, lists.getList32()[0].getF());
  EXPECT_EQ(234567890u, lists.getList32()[1].getF());
  EXPECT_EQ(1234567890123456u, lists.getList64()[0].getF());
  EXPECT_EQ(2345678901234567u, lists.getList64()[1].getF());
  EXPECT_EQ("foo", lists.getListP()[0].getF());
  EXPECT_EQ("bar", lists.getListP()[1].getF());

  {
    auto l = lists.getInt32ListList();
    ASSERT_EQ(3u, l.size());
    checkList(l[0], {1, 2, 3});
    checkList(l[1], {4, 5});
    checkList(l[2], {12341234});
  }

  {
    auto l = lists.getTextListList();
    ASSERT_EQ(3u, l.size());
    checkList(l[0], {"foo", "bar"});
    checkList(l[1], {"baz"});
    checkList(l[2], {"qux", "corge"});
  }

  {
    auto l = lists.getStructListList();
    ASSERT_EQ(2u, l.size());
    auto e = l[0];
    ASSERT_EQ(2u, e.size());
    EXPECT_EQ(123, e[0].getInt32Field());
    EXPECT_EQ(456, e[1].getInt32Field());
    e = l[1];
    ASSERT_EQ(1u, e.size());
    EXPECT_EQ(789, e[0].getInt32Field());
  }
}

#if !CAPNP_LITE

// Hack because as<>() is a template-parameter-dependent lookup everywhere below...
#define as template as

template <typename Reader>
void dynamicCheckListDefaults(Reader reader) {
  auto lists = reader.get("lists").as<DynamicStruct>();

  ASSERT_EQ(2u, lists.get("list0").as<DynamicList>().size());
  ASSERT_EQ(4u, lists.get("list1").as<DynamicList>().size());
  ASSERT_EQ(2u, lists.get("list8").as<DynamicList>().size());
  ASSERT_EQ(2u, lists.get("list16").as<DynamicList>().size());
  ASSERT_EQ(2u, lists.get("list32").as<DynamicList>().size());
  ASSERT_EQ(2u, lists.get("list64").as<DynamicList>().size());
  ASSERT_EQ(2u, lists.get("listP").as<DynamicList>().size());

  EXPECT_EQ(VOID, lists.get("list0").as<DynamicList>()[0].as<DynamicStruct>().get("f").as<Void>());
  EXPECT_EQ(VOID, lists.get("list0").as<DynamicList>()[1].as<DynamicStruct>().get("f").as<Void>());
  EXPECT_TRUE(lists.get("list1").as<DynamicList>()[0].as<DynamicStruct>().get("f").as<bool>());
  EXPECT_FALSE(lists.get("list1").as<DynamicList>()[1].as<DynamicStruct>().get("f").as<bool>());
  EXPECT_TRUE(lists.get("list1").as<DynamicList>()[2].as<DynamicStruct>().get("f").as<bool>());
  EXPECT_TRUE(lists.get("list1").as<DynamicList>()[3].as<DynamicStruct>().get("f").as<bool>());
  EXPECT_EQ(123u, lists.get("list8").as<DynamicList>()[0].as<DynamicStruct>().get("f").as<uint8_t>());
  EXPECT_EQ(45u, lists.get("list8").as<DynamicList>()[1].as<DynamicStruct>().get("f").as<uint8_t>());
  EXPECT_EQ(12345u, lists.get("list16").as<DynamicList>()[0].as<DynamicStruct>().get("f").as<uint16_t>());
  EXPECT_EQ(6789u, lists.get("list16").as<DynamicList>()[1].as<DynamicStruct>().get("f").as<uint16_t>());
  EXPECT_EQ(123456789u, lists.get("list32").as<DynamicList>()[0].as<DynamicStruct>().get("f").as<uint32_t>());
  EXPECT_EQ(234567890u, lists.get("list32").as<DynamicList>()[1].as<DynamicStruct>().get("f").as<uint32_t>());
  EXPECT_EQ(1234567890123456u, lists.get("list64").as<DynamicList>()[0].as<DynamicStruct>().get("f").as<uint64_t>());
  EXPECT_EQ(2345678901234567u, lists.get("list64").as<DynamicList>()[1].as<DynamicStruct>().get("f").as<uint64_t>());
  EXPECT_EQ("foo", lists.get("listP").as<DynamicList>()[0].as<DynamicStruct>().get("f").as<Text>());
  EXPECT_EQ("bar", lists.get("listP").as<DynamicList>()[1].as<DynamicStruct>().get("f").as<Text>());

  {
    auto l = lists.get("int32ListList").as<DynamicList>();
    ASSERT_EQ(3u, l.size());
    checkList<int32_t>(l[0], {1, 2, 3});
    checkList<int32_t>(l[1], {4, 5});
    checkList<int32_t>(l[2], {12341234});
  }

  {
    auto l = lists.get("textListList").as<DynamicList>();
    ASSERT_EQ(3u, l.size());
    checkList<Text>(l[0], {"foo", "bar"});
    checkList<Text>(l[1], {"baz"});
    checkList<Text>(l[2], {"qux", "corge"});
  }

  {
    auto l = lists.get("structListList").as<DynamicList>();
    ASSERT_EQ(2u, l.size());
    auto e = l[0].as<DynamicList>();
    ASSERT_EQ(2u, e.size());
    EXPECT_EQ(123, e[0].as<TestAllTypes>().getInt32Field());
    EXPECT_EQ(456, e[1].as<TestAllTypes>().getInt32Field());
    e = l[1].as<DynamicList>();
    ASSERT_EQ(1u, e.size());
    EXPECT_EQ(789, e[0].as<TestAllTypes>().getInt32Field());
  }
}

#undef as

#endif  // !CAPNP_LITE

}  // namespace

void initTestMessage(TestAllTypes::Builder builder) { genericInitTestMessage(builder); }
void initTestMessage(TestDefaults::Builder builder) { genericInitTestMessage(builder); }
void initTestMessage(TestListDefaults::Builder builder) { genericInitListDefaults(builder); }

void checkTestMessage(TestAllTypes::Builder builder) { genericCheckTestMessage(builder); }
void checkTestMessage(TestDefaults::Builder builder) { genericCheckTestMessage(builder); }
void checkTestMessage(TestListDefaults::Builder builder) { genericCheckListDefaults(builder); }

void checkTestMessage(TestAllTypes::Reader reader) { genericCheckTestMessage(reader); }
void checkTestMessage(TestDefaults::Reader reader) { genericCheckTestMessage(reader); }
void checkTestMessage(TestListDefaults::Reader reader) { genericCheckListDefaults(reader); }

void checkTestMessageAllZero(TestAllTypes::Builder builder) {
  genericCheckTestMessageAllZero(builder);
}
void checkTestMessageAllZero(TestAllTypes::Reader reader) {
  genericCheckTestMessageAllZero(reader);
}

#if !CAPNP_LITE

void initDynamicTestMessage(DynamicStruct::Builder builder) {
  dynamicInitTestMessage(builder);
}
void initDynamicTestLists(DynamicStruct::Builder builder) {
  dynamicInitListDefaults(builder);
}
void checkDynamicTestMessage(DynamicStruct::Builder builder) {
  dynamicCheckTestMessage(builder);
}
void checkDynamicTestLists(DynamicStruct::Builder builder) {
  dynamicCheckListDefaults(builder);
}
void checkDynamicTestMessage(DynamicStruct::Reader reader) {
  dynamicCheckTestMessage(reader);
}
void checkDynamicTestLists(DynamicStruct::Reader reader) {
  dynamicCheckListDefaults(reader);
}
void checkDynamicTestMessageAllZero(DynamicStruct::Builder builder) {
  dynamicCheckTestMessageAllZero(builder);
}
void checkDynamicTestMessageAllZero(DynamicStruct::Reader reader) {
  dynamicCheckTestMessageAllZero(reader);
}

#endif  // !CAPNP_LITE

// =======================================================================================
// Interface implementations.

#if !CAPNP_LITE

TestInterfaceImpl::TestInterfaceImpl(int& callCount): callCount(callCount) {}

kj::Promise<void> TestInterfaceImpl::foo(FooContext context) {
  ++callCount;
  auto params = context.getParams();
  auto result = context.getResults();
  EXPECT_EQ(123, params.getI());
  EXPECT_TRUE(params.getJ());
  result.setX("foo");
  return kj::READY_NOW;
}

kj::Promise<void> TestInterfaceImpl::baz(BazContext context) {
  ++callCount;
  auto params = context.getParams();
  checkTestMessage(params.getS());
  context.releaseParams();
  EXPECT_ANY_THROW(context.getParams());

  return kj::READY_NOW;
}

TestExtendsImpl::TestExtendsImpl(int& callCount): callCount(callCount) {}

kj::Promise<void> TestExtendsImpl::foo(FooContext context) {
  ++callCount;
  auto params = context.getParams();
  auto result = context.getResults();
  EXPECT_EQ(321, params.getI());
  EXPECT_FALSE(params.getJ());
  result.setX("bar");
  return kj::READY_NOW;
}

kj::Promise<void> TestExtendsImpl::grault(GraultContext context) {
  ++callCount;
  context.releaseParams();

  initTestMessage(context.getResults());

  return kj::READY_NOW;
}

TestPipelineImpl::TestPipelineImpl(int& callCount): callCount(callCount) {}

kj::Promise<void> TestPipelineImpl::getCap(GetCapContext context) {
  ++callCount;

  auto params = context.getParams();
  EXPECT_EQ(234, params.getN());

  auto cap = params.getInCap();
  context.releaseParams();

  auto request = cap.fooRequest();
  request.setI(123);
  request.setJ(true);

  return request.send().then(
      [this,context](Response<test::TestInterface::FooResults>&& response) mutable {
        EXPECT_EQ("foo", response.getX());

        auto result = context.getResults();
        result.setS("bar");
        result.initOutBox().setCap(kj::heap<TestExtendsImpl>(callCount));
      });
}

kj::Promise<void> TestCallOrderImpl::getCallSequence(GetCallSequenceContext context) {
  auto result = context.getResults();
  result.setN(count++);
  return kj::READY_NOW;
}

TestTailCallerImpl::TestTailCallerImpl(int& callCount): callCount(callCount) {}

kj::Promise<void> TestTailCallerImpl::foo(FooContext context) {
  ++callCount;

  auto params = context.getParams();
  auto tailRequest = params.getCallee().fooRequest();
  tailRequest.setI(params.getI());
  tailRequest.setT("from TestTailCaller");
  return context.tailCall(kj::mv(tailRequest));
}

TestTailCalleeImpl::TestTailCalleeImpl(int& callCount): callCount(callCount) {}

kj::Promise<void> TestTailCalleeImpl::foo(FooContext context) {
  ++callCount;

  auto params = context.getParams();
  auto results = context.getResults();

  results.setI(params.getI());
  results.setT(params.getT());
  results.setC(kj::heap<TestCallOrderImpl>());

  return kj::READY_NOW;
}

TestMoreStuffImpl::TestMoreStuffImpl(int& callCount, int& handleCount)
    : callCount(callCount), handleCount(handleCount) {}

kj::Promise<void> TestMoreStuffImpl::getCallSequence(GetCallSequenceContext context) {
  auto result = context.getResults();
  result.setN(callCount++);
  return kj::READY_NOW;
}

kj::Promise<void> TestMoreStuffImpl::callFoo(CallFooContext context) {
  ++callCount;

  auto params = context.getParams();
  auto cap = params.getCap();

  auto request = cap.fooRequest();
  request.setI(123);
  request.setJ(true);

  return request.send().then(
      [context](Response<test::TestInterface::FooResults>&& response) mutable {
        EXPECT_EQ("foo", response.getX());
        context.getResults().setS("bar");
      });
}

kj::Promise<void> TestMoreStuffImpl::callFooWhenResolved(CallFooWhenResolvedContext context) {
  ++callCount;

  auto params = context.getParams();
  auto cap = params.getCap();

  return cap.whenResolved().then([cap,context]() mutable {
    auto request = cap.fooRequest();
    request.setI(123);
    request.setJ(true);

    return request.send().then(
        [context](Response<test::TestInterface::FooResults>&& response) mutable {
          EXPECT_EQ("foo", response.getX());
          context.getResults().setS("bar");
        });
  });
}

kj::Promise<void> TestMoreStuffImpl::neverReturn(NeverReturnContext context) {
  ++callCount;

  // Attach `cap` to the promise to make sure it is released.
  auto promise = kj::Promise<void>(kj::NEVER_DONE).attach(context.getParams().getCap());

  // Also attach `cap` to the result struct to make sure that is released.
  context.getResults().setCapCopy(context.getParams().getCap());

  context.allowCancellation();
  return kj::mv(promise);
}

kj::Promise<void> TestMoreStuffImpl::hold(HoldContext context) {
  ++callCount;

  auto params = context.getParams();
  clientToHold = params.getCap();
  return kj::READY_NOW;
}

kj::Promise<void> TestMoreStuffImpl::callHeld(CallHeldContext context) {
  ++callCount;

  auto request = clientToHold.fooRequest();
  request.setI(123);
  request.setJ(true);

  return request.send().then(
      [context](Response<test::TestInterface::FooResults>&& response) mutable {
        EXPECT_EQ("foo", response.getX());
        context.getResults().setS("bar");
      });
}

kj::Promise<void> TestMoreStuffImpl::getHeld(GetHeldContext context) {
  ++callCount;
  auto result = context.getResults();
  result.setCap(clientToHold);
  return kj::READY_NOW;
}

kj::Promise<void> TestMoreStuffImpl::echo(EchoContext context) {
  ++callCount;
  auto params = context.getParams();
  auto result = context.getResults();
  result.setCap(params.getCap());
  return kj::READY_NOW;
}

kj::Promise<void> TestMoreStuffImpl::expectCancel(ExpectCancelContext context) {
  auto cap = context.getParams().getCap();
  context.allowCancellation();
  return loop(0, cap, context);
}

kj::Promise<void> TestMoreStuffImpl::loop(uint depth, test::TestInterface::Client cap,
                                          ExpectCancelContext context) {
  if (depth > 100) {
    ADD_FAILURE() << "Looped too long, giving up.";
    return kj::READY_NOW;
  } else {
    return kj::evalLater([=]() mutable {
      return loop(depth + 1, cap, context);
    });
  }
}

class HandleImpl final: public test::TestHandle::Server {
public:
  HandleImpl(int& count): count(count) { ++count; }
  ~HandleImpl() { --count; }

private:
  int& count;
};

kj::Promise<void> TestMoreStuffImpl::getHandle(GetHandleContext context) {
  context.getResults().setHandle(kj::heap<HandleImpl>(handleCount));
  return kj::READY_NOW;
}

kj::Promise<void> TestMoreStuffImpl::getNull(GetNullContext context) {
  return kj::READY_NOW;
}

#endif  // !CAPNP_LITE

}  // namespace _ (private)
}  // namespace capnp
