// 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 "schema.capnp.h"

#ifdef CAPNP_CAPABILITY_H_
#error "schema.capnp should not depend on capability.h, because it contains no interfaces."
#endif

#include <capnp/test.capnp.h>

#ifndef CAPNP_CAPABILITY_H_
#error "test.capnp did not include capability.h."
#endif

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

namespace capnp {
namespace _ {
namespace {

TEST(Capability, Basic) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount = 0;
  test::TestInterface::Client client(kj::heap<TestInterfaceImpl>(callCount));

  auto request1 = client.fooRequest();
  request1.setI(123);
  request1.setJ(true);
  auto promise1 = request1.send();

  auto request2 = client.bazRequest();
  initTestMessage(request2.initS());
  auto promise2 = request2.send();

  bool barFailed = false;
  auto request3 = client.barRequest();
  auto promise3 = request3.send().then(
      [](Response<test::TestInterface::BarResults>&& response) {
        ADD_FAILURE() << "Expected bar() call to fail.";
      }, [&](kj::Exception&& e) {
        EXPECT_EQ(kj::Exception::Type::UNIMPLEMENTED, e.getType());
        barFailed = true;
      });

  EXPECT_EQ(0, callCount);

  auto response1 = promise1.wait(waitScope);

  EXPECT_EQ("foo", response1.getX());

  auto response2 = promise2.wait(waitScope);

  promise3.wait(waitScope);

  EXPECT_EQ(2, callCount);
  EXPECT_TRUE(barFailed);
}

TEST(Capability, Inheritance) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount = 0;
  test::TestExtends::Client client1(kj::heap<TestExtendsImpl>(callCount));
  test::TestInterface::Client client2 = client1;
  auto client = client2.castAs<test::TestExtends>();

  auto request1 = client.fooRequest();
  request1.setI(321);
  auto promise1 = request1.send();

  auto request2 = client.graultRequest();
  auto promise2 = request2.send();

  EXPECT_EQ(0, callCount);

  auto response2 = promise2.wait(waitScope);

  checkTestMessage(response2);

  auto response1 = promise1.wait(waitScope);

  EXPECT_EQ("bar", response1.getX());

  EXPECT_EQ(2, callCount);
}

TEST(Capability, Pipelining) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount = 0;
  int chainedCallCount = 0;
  test::TestPipeline::Client client(kj::heap<TestPipelineImpl>(callCount));

  auto request = client.getCapRequest();
  request.setN(234);
  request.setInCap(test::TestInterface::Client(kj::heap<TestInterfaceImpl>(chainedCallCount)));

  auto promise = request.send();

  auto pipelineRequest = promise.getOutBox().getCap().fooRequest();
  pipelineRequest.setI(321);
  auto pipelinePromise = pipelineRequest.send();

  auto pipelineRequest2 = promise.getOutBox().getCap().castAs<test::TestExtends>().graultRequest();
  auto pipelinePromise2 = pipelineRequest2.send();

  promise = nullptr;  // Just to be annoying, drop the original promise.

  EXPECT_EQ(0, callCount);
  EXPECT_EQ(0, chainedCallCount);

  auto response = pipelinePromise.wait(waitScope);
  EXPECT_EQ("bar", response.getX());

  auto response2 = pipelinePromise2.wait(waitScope);
  checkTestMessage(response2);

  EXPECT_EQ(3, callCount);
  EXPECT_EQ(1, chainedCallCount);
}

TEST(Capability, TailCall) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int calleeCallCount = 0;
  int callerCallCount = 0;

  test::TestTailCallee::Client callee(kj::heap<TestTailCalleeImpl>(calleeCallCount));
  test::TestTailCaller::Client caller(kj::heap<TestTailCallerImpl>(callerCallCount));

  auto request = caller.fooRequest();
  request.setI(456);
  request.setCallee(callee);

  auto promise = request.send();

  auto dependentCall0 = promise.getC().getCallSequenceRequest().send();

  auto response = promise.wait(waitScope);
  EXPECT_EQ(456, response.getI());
  EXPECT_EQ(456, response.getI());

  auto dependentCall1 = promise.getC().getCallSequenceRequest().send();

  auto dependentCall2 = response.getC().getCallSequenceRequest().send();

  EXPECT_EQ(0, dependentCall0.wait(waitScope).getN());
  EXPECT_EQ(1, dependentCall1.wait(waitScope).getN());
  EXPECT_EQ(2, dependentCall2.wait(waitScope).getN());

  EXPECT_EQ(1, calleeCallCount);
  EXPECT_EQ(1, callerCallCount);
}

TEST(Capability, AsyncCancelation) {
  // Tests allowCancellation().

  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  auto paf = kj::newPromiseAndFulfiller<void>();
  bool destroyed = false;
  auto destructionPromise = paf.promise.then([&]() { destroyed = true; }).eagerlyEvaluate(nullptr);

  int callCount = 0;
  int handleCount = 0;

  test::TestMoreStuff::Client client(kj::heap<TestMoreStuffImpl>(callCount, handleCount));

  kj::Promise<void> promise = nullptr;

  bool returned = false;
  {
    auto request = client.expectCancelRequest();
    request.setCap(test::TestInterface::Client(kj::heap<TestCapDestructor>(kj::mv(paf.fulfiller))));
    promise = request.send().then(
        [&](Response<test::TestMoreStuff::ExpectCancelResults>&& response) {
      returned = true;
    }).eagerlyEvaluate(nullptr);
  }
  kj::evalLater([]() {}).wait(waitScope);
  kj::evalLater([]() {}).wait(waitScope);

  // We can detect that the method was canceled because it will drop the cap.
  EXPECT_FALSE(destroyed);
  EXPECT_FALSE(returned);

  promise = nullptr;  // request cancellation
  destructionPromise.wait(waitScope);

  EXPECT_TRUE(destroyed);
  EXPECT_FALSE(returned);
}

// =======================================================================================

TEST(Capability, DynamicClient) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount = 0;
  DynamicCapability::Client client =
      test::TestInterface::Client(kj::heap<TestInterfaceImpl>(callCount));

  auto request1 = client.newRequest("foo");
  request1.set("i", 123);
  request1.set("j", true);
  auto promise1 = request1.send();

  auto request2 = client.newRequest("baz");
  initDynamicTestMessage(request2.init("s").as<DynamicStruct>());
  auto promise2 = request2.send();

  bool barFailed = false;
  auto request3 = client.newRequest("bar");
  auto promise3 = request3.send().then(
      [](Response<DynamicStruct>&& response) {
        ADD_FAILURE() << "Expected bar() call to fail.";
      }, [&](kj::Exception&& e) {
        EXPECT_EQ(kj::Exception::Type::UNIMPLEMENTED, e.getType());
        barFailed = true;
      });

  EXPECT_EQ(0, callCount);

  auto response1 = promise1.wait(waitScope);

  EXPECT_EQ("foo", response1.get("x").as<Text>());

  auto response2 = promise2.wait(waitScope);

  promise3.wait(waitScope);

  EXPECT_EQ(2, callCount);
  EXPECT_TRUE(barFailed);
}

TEST(Capability, DynamicClientInheritance) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount = 0;

  DynamicCapability::Client client1 =
      test::TestExtends::Client(kj::heap<TestExtendsImpl>(callCount));
  EXPECT_EQ(Schema::from<test::TestExtends>(), client1.getSchema());
  EXPECT_NE(Schema::from<test::TestInterface>(), client1.getSchema());

  DynamicCapability::Client client2 = client1.upcast(Schema::from<test::TestInterface>());
  EXPECT_EQ(Schema::from<test::TestInterface>(), client2.getSchema());

  EXPECT_ANY_THROW(client2.upcast(Schema::from<test::TestExtends>()));
  auto client = client2.castAs<DynamicCapability>(Schema::from<test::TestExtends>());

  auto request1 = client.newRequest("foo");
  request1.set("i", 321);
  auto promise1 = request1.send();

  auto request2 = client.newRequest("grault");
  auto promise2 = request2.send();

  EXPECT_EQ(0, callCount);

  auto response2 = promise2.wait(waitScope);

  checkDynamicTestMessage(response2.as<DynamicStruct>());

  auto response1 = promise1.wait(waitScope);

  EXPECT_EQ("bar", response1.get("x").as<Text>());

  EXPECT_EQ(2, callCount);
}

TEST(Capability, DynamicClientPipelining) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount = 0;
  int chainedCallCount = 0;
  DynamicCapability::Client client =
      test::TestPipeline::Client(kj::heap<TestPipelineImpl>(callCount));

  auto request = client.newRequest("getCap");
  request.set("n", 234);
  request.set("inCap", test::TestInterface::Client(kj::heap<TestInterfaceImpl>(chainedCallCount)));

  auto promise = request.send();

  auto outCap = promise.get("outBox").releaseAs<DynamicStruct>()
                       .get("cap").releaseAs<DynamicCapability>();
  auto pipelineRequest = outCap.newRequest("foo");
  pipelineRequest.set("i", 321);
  auto pipelinePromise = pipelineRequest.send();

  auto pipelineRequest2 = outCap.castAs<test::TestExtends>().graultRequest();
  auto pipelinePromise2 = pipelineRequest2.send();

  promise = nullptr;  // Just to be annoying, drop the original promise.

  EXPECT_EQ(0, callCount);
  EXPECT_EQ(0, chainedCallCount);

  auto response = pipelinePromise.wait(waitScope);
  EXPECT_EQ("bar", response.get("x").as<Text>());

  auto response2 = pipelinePromise2.wait(waitScope);
  checkTestMessage(response2);

  EXPECT_EQ(3, callCount);
  EXPECT_EQ(1, chainedCallCount);
}

TEST(Capability, DynamicClientPipelineAnyCap) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount = 0;
  int chainedCallCount = 0;
  DynamicCapability::Client client =
      test::TestPipeline::Client(kj::heap<TestPipelineImpl>(callCount));

  auto request = client.newRequest("getAnyCap");
  request.set("n", 234);
  request.set("inCap", test::TestInterface::Client(kj::heap<TestInterfaceImpl>(chainedCallCount)));

  auto promise = request.send();

  auto outAnyCap = promise.get("outBox").releaseAs<DynamicStruct>()
                          .get("cap").releaseAs<DynamicCapability>();

  EXPECT_EQ(Schema::from<Capability>(), outAnyCap.getSchema());
  auto outCap = outAnyCap.castAs<DynamicCapability>(Schema::from<test::TestInterface>());

  auto pipelineRequest = outCap.newRequest("foo");
  pipelineRequest.set("i", 321);
  auto pipelinePromise = pipelineRequest.send();

  auto pipelineRequest2 = outCap.castAs<test::TestExtends>().graultRequest();
  auto pipelinePromise2 = pipelineRequest2.send();

  promise = nullptr;  // Just to be annoying, drop the original promise.

  EXPECT_EQ(0, callCount);
  EXPECT_EQ(0, chainedCallCount);

  auto response = pipelinePromise.wait(waitScope);
  EXPECT_EQ("bar", response.get("x").as<Text>());

  auto response2 = pipelinePromise2.wait(waitScope);
  checkTestMessage(response2);

  EXPECT_EQ(3, callCount);
  EXPECT_EQ(1, chainedCallCount);
}

// =======================================================================================

class TestInterfaceDynamicImpl final: public DynamicCapability::Server {
public:
  TestInterfaceDynamicImpl(int& callCount)
      : DynamicCapability::Server(Schema::from<test::TestInterface>()),
        callCount(callCount) {}

  int& callCount;

  kj::Promise<void> call(InterfaceSchema::Method method,
                         CallContext<DynamicStruct, DynamicStruct> context) {
    auto methodName = method.getProto().getName();
    if (methodName == "foo") {
      ++callCount;
      auto params = context.getParams();
      EXPECT_EQ(123, params.get("i").as<int>());
      EXPECT_TRUE(params.get("j").as<bool>());
      context.getResults().set("x", "foo");
      return kj::READY_NOW;
    } else if (methodName == "baz") {
      ++callCount;
      auto params = context.getParams();
      checkDynamicTestMessage(params.get("s").as<DynamicStruct>());
      context.releaseParams();
      EXPECT_ANY_THROW(context.getParams());
      return kj::READY_NOW;
    } else {
      KJ_UNIMPLEMENTED("Method not implemented", methodName) { break; }
      return kj::READY_NOW;
    }
  }
};

TEST(Capability, DynamicServer) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount = 0;
  test::TestInterface::Client client =
      DynamicCapability::Client(kj::heap<TestInterfaceDynamicImpl>(callCount))
          .castAs<test::TestInterface>();

  auto request1 = client.fooRequest();
  request1.setI(123);
  request1.setJ(true);
  auto promise1 = request1.send();

  auto request2 = client.bazRequest();
  initTestMessage(request2.initS());
  auto promise2 = request2.send();

  bool barFailed = false;
  auto request3 = client.barRequest();
  auto promise3 = request3.send().then(
      [](Response<test::TestInterface::BarResults>&& response) {
        ADD_FAILURE() << "Expected bar() call to fail.";
      }, [&](kj::Exception&& e) {
        EXPECT_EQ(kj::Exception::Type::UNIMPLEMENTED, e.getType());
        barFailed = true;
      });

  EXPECT_EQ(0, callCount);

  auto response1 = promise1.wait(waitScope);

  EXPECT_EQ("foo", response1.getX());

  auto response2 = promise2.wait(waitScope);

  promise3.wait(waitScope);

  EXPECT_EQ(2, callCount);
  EXPECT_TRUE(barFailed);
}

class TestExtendsDynamicImpl final: public DynamicCapability::Server {
public:
  TestExtendsDynamicImpl(int& callCount)
      : DynamicCapability::Server(Schema::from<test::TestExtends>()),
        callCount(callCount) {}

  int& callCount;

  kj::Promise<void> call(InterfaceSchema::Method method,
                         CallContext<DynamicStruct, DynamicStruct> context) {
    auto methodName = method.getProto().getName();
    if (methodName == "foo") {
      ++callCount;
      auto params = context.getParams();
      EXPECT_EQ(321, params.get("i").as<int>());
      EXPECT_FALSE(params.get("j").as<bool>());
      context.getResults().set("x", "bar");
      return kj::READY_NOW;
    } else if (methodName == "grault") {
      ++callCount;
      context.releaseParams();
      initDynamicTestMessage(context.getResults());
      return kj::READY_NOW;
    } else {
      KJ_FAIL_ASSERT("Method not implemented", methodName);
    }
  }
};

TEST(Capability, DynamicServerInheritance) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount = 0;
  test::TestExtends::Client client1 =
      DynamicCapability::Client(kj::heap<TestExtendsDynamicImpl>(callCount))
          .castAs<test::TestExtends>();
  test::TestInterface::Client client2 = client1;
  auto client = client2.castAs<test::TestExtends>();

  auto request1 = client.fooRequest();
  request1.setI(321);
  auto promise1 = request1.send();

  auto request2 = client.graultRequest();
  auto promise2 = request2.send();

  EXPECT_EQ(0, callCount);

  auto response2 = promise2.wait(waitScope);

  checkTestMessage(response2);

  auto response1 = promise1.wait(waitScope);

  EXPECT_EQ("bar", response1.getX());

  EXPECT_EQ(2, callCount);
}

class TestPipelineDynamicImpl final: public DynamicCapability::Server {
public:
  TestPipelineDynamicImpl(int& callCount)
      : DynamicCapability::Server(Schema::from<test::TestPipeline>()),
        callCount(callCount) {}

  int& callCount;

  kj::Promise<void> call(InterfaceSchema::Method method,
                         CallContext<DynamicStruct, DynamicStruct> context) {
    auto methodName = method.getProto().getName();
    if (methodName == "getCap") {
      ++callCount;

      auto params = context.getParams();
      EXPECT_EQ(234, params.get("n").as<uint32_t>());

      auto cap = params.get("inCap").as<DynamicCapability>();
      context.releaseParams();

      auto request = cap.newRequest("foo");
      request.set("i", 123);
      request.set("j", true);

      return request.send().then(
          [this,KJ_CPCAP(context)](capnp::Response<DynamicStruct>&& response) mutable {
            EXPECT_EQ("foo", response.get("x").as<Text>());

            auto result = context.getResults();
            result.set("s", "bar");

            auto box = result.init("outBox").as<DynamicStruct>();

            // Too lazy to write a whole separate test for each of these cases...  so just make
            // sure they both compile here, and only actually test the latter.
            box.set("cap", kj::heap<TestExtendsDynamicImpl>(callCount));
#if __GNUG__ && !__clang__ && __GNUG__ == 4 && __GNUC_MINOR__ == 9
            // The last line in this block tickles a bug in Debian G++ 4.9.2 that is not present
            // in 4.8.x nor in 4.9.4:
            //     https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=781060
            //
            // Unfortunatley 4.9.2 is present on many Debian Jessie systems..
            //
            // For the moment, we can get away with skipping the last line as the previous line
            // will set things up in a way that allows the test to complete successfully.
            return;
#endif
            box.set("cap", kj::heap<TestExtendsImpl>(callCount));
          });
    } else {
      KJ_FAIL_ASSERT("Method not implemented", methodName);
    }
  }
};

TEST(Capability, DynamicServerPipelining) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount = 0;
  int chainedCallCount = 0;
  test::TestPipeline::Client client =
      DynamicCapability::Client(kj::heap<TestPipelineDynamicImpl>(callCount))
          .castAs<test::TestPipeline>();

  auto request = client.getCapRequest();
  request.setN(234);
  request.setInCap(test::TestInterface::Client(kj::heap<TestInterfaceImpl>(chainedCallCount)));

  auto promise = request.send();

  auto pipelineRequest = promise.getOutBox().getCap().fooRequest();
  pipelineRequest.setI(321);
  auto pipelinePromise = pipelineRequest.send();

  auto pipelineRequest2 = promise.getOutBox().getCap().castAs<test::TestExtends>().graultRequest();
  auto pipelinePromise2 = pipelineRequest2.send();

  promise = nullptr;  // Just to be annoying, drop the original promise.

  EXPECT_EQ(0, callCount);
  EXPECT_EQ(0, chainedCallCount);

  auto response = pipelinePromise.wait(waitScope);
  EXPECT_EQ("bar", response.getX());

  auto response2 = pipelinePromise2.wait(waitScope);
  checkTestMessage(response2);

  EXPECT_EQ(3, callCount);
  EXPECT_EQ(1, chainedCallCount);
}

class TestTailCallerDynamicImpl final: public DynamicCapability::Server {
public:
  TestTailCallerDynamicImpl(int& callCount)
      : DynamicCapability::Server(Schema::from<test::TestTailCaller>()),
        callCount(callCount) {}

  int& callCount;

  kj::Promise<void> call(InterfaceSchema::Method method,
                         CallContext<DynamicStruct, DynamicStruct> context) {
    auto methodName = method.getProto().getName();
    if (methodName == "foo") {
      ++callCount;

      auto params = context.getParams();
      auto tailRequest = params.get("callee").as<DynamicCapability>().newRequest("foo");
      tailRequest.set("i", params.get("i"));
      tailRequest.set("t", "from TestTailCaller");
      return context.tailCall(kj::mv(tailRequest));
    } else {
      KJ_FAIL_ASSERT("Method not implemented", methodName);
    }
  }
};

TEST(Capability, DynamicServerTailCall) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int calleeCallCount = 0;
  int callerCallCount = 0;

  test::TestTailCallee::Client callee(kj::heap<TestTailCalleeImpl>(calleeCallCount));
  test::TestTailCaller::Client caller =
      DynamicCapability::Client(kj::heap<TestTailCallerDynamicImpl>(callerCallCount))
          .castAs<test::TestTailCaller>();

  auto request = caller.fooRequest();
  request.setI(456);
  request.setCallee(callee);

  auto promise = request.send();

  auto dependentCall0 = promise.getC().getCallSequenceRequest().send();

  auto response = promise.wait(waitScope);
  EXPECT_EQ(456, response.getI());
  EXPECT_EQ(456, response.getI());

  auto dependentCall1 = promise.getC().getCallSequenceRequest().send();

  auto dependentCall2 = response.getC().getCallSequenceRequest().send();

  EXPECT_EQ(0, dependentCall0.wait(waitScope).getN());
  EXPECT_EQ(1, dependentCall1.wait(waitScope).getN());
  EXPECT_EQ(2, dependentCall2.wait(waitScope).getN());

  EXPECT_EQ(1, calleeCallCount);
  EXPECT_EQ(1, callerCallCount);
}

// =======================================================================================

void verifyClient(test::TestInterface::Client client, const int& callCount,
                  kj::WaitScope& waitScope) {
  int origCount = callCount;
  auto request = client.fooRequest();
  request.setI(123);
  request.setJ(true);
  auto response = request.send().wait(waitScope);
  EXPECT_EQ("foo", response.getX());
  EXPECT_EQ(origCount + 1, callCount);
}

void verifyClient(DynamicCapability::Client client, const int& callCount,
                  kj::WaitScope& waitScope) {
  int origCount = callCount;
  auto request = client.newRequest("foo");
  request.set("i", 123);
  request.set("j", true);
  auto response = request.send().wait(waitScope);
  EXPECT_EQ("foo", response.get("x").as<Text>());
  EXPECT_EQ(origCount + 1, callCount);
}

TEST(Capability, AnyPointersAndOrphans) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount1 = 0;
  int callCount2 = 0;

  // We use a TestPipeline instance here merely as a way to conveniently obtain an imbued message
  // instance.
  test::TestPipeline::Client baseClient(nullptr);
  test::TestInterface::Client client1(kj::heap<TestInterfaceImpl>(callCount1));
  test::TestInterface::Client client2(kj::heap<TestInterfaceImpl>(callCount2));

  auto request = baseClient.testPointersRequest();
  request.setCap(client1);

  EXPECT_TRUE(request.hasCap());

  Orphan<test::TestInterface> orphan = request.disownCap();
  EXPECT_FALSE(orphan == nullptr);

  EXPECT_FALSE(request.hasCap());

  verifyClient(orphan.get(), callCount1, waitScope);
  verifyClient(orphan.getReader(), callCount1, waitScope);

  request.getObj().adopt(kj::mv(orphan));
  EXPECT_TRUE(orphan == nullptr);

  verifyClient(request.getObj().getAs<test::TestInterface>(), callCount1, waitScope);
  verifyClient(request.asReader().getObj().getAs<test::TestInterface>(), callCount1, waitScope);
  verifyClient(request.getObj().getAs<DynamicCapability>(
      Schema::from<test::TestInterface>()), callCount1, waitScope);
  verifyClient(request.asReader().getObj().getAs<DynamicCapability>(
      Schema::from<test::TestInterface>()), callCount1, waitScope);

  request.getObj().clear();
  EXPECT_FALSE(request.hasObj());

  request.getObj().setAs<test::TestInterface>(client2);
  verifyClient(request.getObj().getAs<test::TestInterface>(), callCount2, waitScope);

  Orphan<DynamicCapability> dynamicOrphan = request.getObj().disownAs<DynamicCapability>(
      Schema::from<test::TestInterface>());
  verifyClient(dynamicOrphan.get(), callCount2, waitScope);
  verifyClient(dynamicOrphan.getReader(), callCount2, waitScope);

  Orphan<DynamicValue> dynamicValueOrphan = kj::mv(dynamicOrphan);
  verifyClient(dynamicValueOrphan.get().as<DynamicCapability>(), callCount2, waitScope);

  orphan = dynamicValueOrphan.releaseAs<test::TestInterface>();
  EXPECT_FALSE(orphan == nullptr);
  verifyClient(orphan.get(), callCount2, waitScope);

  request.adoptCap(kj::mv(orphan));
  EXPECT_TRUE(orphan == nullptr);

  verifyClient(request.getCap(), callCount2, waitScope);

  Orphan<DynamicCapability> dynamicOrphan2 = request.disownCap();
  verifyClient(dynamicOrphan2.get(), callCount2, waitScope);
  verifyClient(dynamicOrphan2.getReader(), callCount2, waitScope);
}

TEST(Capability, Lists) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  int callCount1 = 0;
  int callCount2 = 0;
  int callCount3 = 0;
  test::TestPipeline::Client baseClient(kj::heap<TestPipelineImpl>(callCount1));
  test::TestInterface::Client client1(kj::heap<TestInterfaceImpl>(callCount1));
  test::TestInterface::Client client2(kj::heap<TestInterfaceImpl>(callCount2));
  test::TestInterface::Client client3(kj::heap<TestInterfaceImpl>(callCount3));

  auto request = baseClient.testPointersRequest();

  auto list = request.initList(3);
  list.set(0, client1);
  list.set(1, client2);
  list.set(2, client3);

  verifyClient(list[0], callCount1, waitScope);
  verifyClient(list[1], callCount2, waitScope);
  verifyClient(list[2], callCount3, waitScope);

  auto listReader = request.asReader().getList();
  verifyClient(listReader[0], callCount1, waitScope);
  verifyClient(listReader[1], callCount2, waitScope);
  verifyClient(listReader[2], callCount3, waitScope);

  auto dynamicList = toDynamic(list);
  verifyClient(dynamicList[0].as<DynamicCapability>(), callCount1, waitScope);
  verifyClient(dynamicList[1].as<DynamicCapability>(), callCount2, waitScope);
  verifyClient(dynamicList[2].as<DynamicCapability>(), callCount3, waitScope);

  auto dynamicListReader = toDynamic(listReader);
  verifyClient(dynamicListReader[0].as<DynamicCapability>(), callCount1, waitScope);
  verifyClient(dynamicListReader[1].as<DynamicCapability>(), callCount2, waitScope);
  verifyClient(dynamicListReader[2].as<DynamicCapability>(), callCount3, waitScope);
}

TEST(Capability, KeywordMethods) {
  // Verify that keywords are only munged where necessary.

  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);
  bool called = false;

  class TestKeywordMethodsImpl final: public test::TestKeywordMethods::Server {
  public:
    TestKeywordMethodsImpl(bool& called): called(called) {}

    kj::Promise<void> delete_(DeleteContext context) override {
      called = true;
      return kj::READY_NOW;
    }

  private:
    bool& called;
  };

  test::TestKeywordMethods::Client client = kj::heap<TestKeywordMethodsImpl>(called);
  client.deleteRequest().send().wait(waitScope);

  EXPECT_TRUE(called);
}

TEST(Capability, Generics) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  typedef test::TestGenerics<TestAllTypes>::Interface<List<uint32_t>> Interface;
  Interface::Client client = nullptr;

  auto request = client.callRequest();
  request.setBaz("hello");
  initTestMessage(request.initInnerBound().initFoo());
  initTestMessage(request.initInnerUnbound().getFoo().initAs<TestAllTypes>());

  auto promise = request.send().then([](capnp::Response<Interface::CallResults>&& response) {
    // This doesn't actually execute; we're just checking that it compiles.
    List<uint32_t>::Reader qux = response.getQux();
    qux.size();
    checkTestMessage(response.getGen().getFoo());
  }, [](kj::Exception&& e) {
    // Ignore exception (which we'll always get because we're calling a null capability).
  });

  promise.wait(waitScope);

  // Check that asGeneric<>() compiles.
  test::TestGenerics<TestAllTypes>::Interface<>::Client castClient = client.asGeneric<>();
  test::TestGenerics<TestAllTypes>::Interface<TestAllTypes>::Client castClient2 =
      client.asGeneric<TestAllTypes>();
  test::TestGenerics<>::Interface<List<uint32_t>>::Client castClient3 = client.asTestGenericsGeneric<>();
}

TEST(Capability, Generics2) {
  MallocMessageBuilder builder;
  auto root = builder.getRoot<test::TestUseGenerics>();

  root.initCap().setFoo(test::TestInterface::Client(nullptr));
}

TEST(Capability, ImplicitParams) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  typedef test::TestImplicitMethodParams Interface;
  Interface::Client client = nullptr;

  capnp::Request<Interface::CallParams<Text, TestAllTypes>,
                 test::TestGenerics<Text, TestAllTypes>> request =
      client.callRequest<Text, TestAllTypes>();
  request.setFoo("hello");
  initTestMessage(request.initBar());

  auto promise = request.send()
      .then([](capnp::Response<test::TestGenerics<Text, TestAllTypes>>&& response) {
    // This doesn't actually execute; we're just checking that it compiles.
    Text::Reader text = response.getFoo();
    text.size();
    checkTestMessage(response.getRev().getFoo());
  }, [](kj::Exception&& e) {});

  promise.wait(waitScope);
}

TEST(Capability, CapabilityServerSet) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  CapabilityServerSet<test::TestInterface> set1, set2;

  int callCount = 0;
  test::TestInterface::Client clientStandalone(kj::heap<TestInterfaceImpl>(callCount));
  test::TestInterface::Client clientNull = nullptr;

  auto ownServer1 = kj::heap<TestInterfaceImpl>(callCount);
  auto& server1 = *ownServer1;
  test::TestInterface::Client client1 = set1.add(kj::mv(ownServer1));

  auto ownServer2 = kj::heap<TestInterfaceImpl>(callCount);
  auto& server2 = *ownServer2;
  test::TestInterface::Client client2 = set2.add(kj::mv(ownServer2));

  // Getting the local server using the correct set works.
  EXPECT_EQ(&server1, &KJ_ASSERT_NONNULL(set1.getLocalServer(client1).wait(waitScope)));
  EXPECT_EQ(&server2, &KJ_ASSERT_NONNULL(set2.getLocalServer(client2).wait(waitScope)));

  // Getting the local server using the wrong set doesn't work.
  EXPECT_TRUE(set1.getLocalServer(client2).wait(waitScope) == nullptr);
  EXPECT_TRUE(set2.getLocalServer(client1).wait(waitScope) == nullptr);
  EXPECT_TRUE(set1.getLocalServer(clientStandalone).wait(waitScope) == nullptr);
  EXPECT_TRUE(set1.getLocalServer(clientNull).wait(waitScope) == nullptr);

  // A promise client waits to be resolved.
  auto paf = kj::newPromiseAndFulfiller<test::TestInterface::Client>();
  test::TestInterface::Client clientPromise = kj::mv(paf.promise);

  auto errorPaf = kj::newPromiseAndFulfiller<test::TestInterface::Client>();
  test::TestInterface::Client errorPromise = kj::mv(errorPaf.promise);

  bool resolved1 = false, resolved2 = false, resolved3 = false;
  auto promise1 = set1.getLocalServer(clientPromise)
      .then([&](kj::Maybe<test::TestInterface::Server&> server) {
    resolved1 = true;
    EXPECT_EQ(&server1, &KJ_ASSERT_NONNULL(server));
  });
  auto promise2 = set2.getLocalServer(clientPromise)
      .then([&](kj::Maybe<test::TestInterface::Server&> server) {
    resolved2 = true;
    EXPECT_TRUE(server == nullptr);
  });
  auto promise3 = set1.getLocalServer(errorPromise)
      .then([&](kj::Maybe<test::TestInterface::Server&> server) {
    KJ_FAIL_EXPECT("getLocalServer() on error promise should have thrown");
  }, [&](kj::Exception&& e) {
    resolved3 = true;
    KJ_EXPECT(e.getDescription().endsWith("foo"), e.getDescription());
  });

  kj::evalLater([](){}).wait(waitScope);
  kj::evalLater([](){}).wait(waitScope);
  kj::evalLater([](){}).wait(waitScope);
  kj::evalLater([](){}).wait(waitScope);

  EXPECT_FALSE(resolved1);
  EXPECT_FALSE(resolved2);
  EXPECT_FALSE(resolved3);

  paf.fulfiller->fulfill(kj::cp(client1));
  errorPaf.fulfiller->reject(KJ_EXCEPTION(FAILED, "foo"));

  promise1.wait(waitScope);
  promise2.wait(waitScope);
  promise3.wait(waitScope);

  EXPECT_TRUE(resolved1);
  EXPECT_TRUE(resolved2);
  EXPECT_TRUE(resolved3);
}

class TestThisCap final: public test::TestInterface::Server {
public:
  TestThisCap(int& callCount): callCount(callCount) {}
  ~TestThisCap() noexcept(false) { callCount = -1; }

  test::TestInterface::Client getSelf() {
    return thisCap();
  }

protected:
  kj::Promise<void> bar(BarContext context) {
    ++callCount;
    return kj::READY_NOW;
  }

private:
  int& callCount;
};

TEST(Capability, ThisCap) {
  int callCount = 0;
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  auto server = kj::heap<TestThisCap>(callCount);
  TestThisCap* serverPtr = server;

  test::TestInterface::Client client = kj::mv(server);
  client.barRequest().send().wait(waitScope);
  EXPECT_EQ(1, callCount);

  test::TestInterface::Client client2 = serverPtr->getSelf();
  EXPECT_EQ(1, callCount);
  client2.barRequest().send().wait(waitScope);
  EXPECT_EQ(2, callCount);

  client = nullptr;

  EXPECT_EQ(2, callCount);
  client2.barRequest().send().wait(waitScope);
  EXPECT_EQ(3, callCount);

  client2 = nullptr;

  EXPECT_EQ(-1, callCount);
}

TEST(Capability, TransferCap) {
  kj::EventLoop loop;
  kj::WaitScope waitScope(loop);

  MallocMessageBuilder message;
  auto root = message.initRoot<test::TestTransferCap>();

  auto orphan = message.getOrphanage().newOrphan<test::TestTransferCap::Element>();
  auto e = orphan.get();
  e.setText("foo");
  e.setCap(KJ_EXCEPTION(FAILED, "whatever"));

  root.initList(1).adoptWithCaveats(0, kj::mv(orphan));

  // This line used to throw due to capability pointers being incorrectly transferred.
  auto cap = root.getList()[0].getCap();

  cap.whenResolved().then([]() {
    KJ_FAIL_EXPECT("didn't throw?");
  }, [](kj::Exception&&) {
    // success
  }).wait(waitScope);
}

}  // namespace
}  // namespace _
}  // namespace capnp
