blob: 6a934a94c83ad5fd9529a001149dd6f74b7f908c [file] [log] [blame]
// 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_INCLUDED
#error "schema.capnp should not depend on capability.h, because it contains no interfaces."
#endif
#include <capnp/test.capnp.h>
#ifndef CAPNP_CAPABILITY_H_INCLUDED
#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, CapabilityList) {
kj::EventLoop loop;
kj::WaitScope waitScope(loop);
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestListOfAny>();
auto initCapList = root.initCapList(2);
int callCount0 = 0;
int callCount1 = 0;
initCapList.set(0, kj::heap<TestInterfaceImpl>(callCount0));
initCapList.set(1, kj::heap<TestInterfaceImpl>(callCount1));
auto capList = root.getCapList();
auto cap0 = capList[0].castAs<test::TestInterface>();
auto cap1 = capList[1].castAs<test::TestInterface>();
EXPECT_EQ(2u, root.getCapList().size());
auto request0 = cap0.fooRequest();
request0.setI(123);
request0.setJ(true);
EXPECT_EQ("foo", request0.send().wait(waitScope).getX());
auto request1 = cap1.fooRequest();
request1.setI(123);
request1.setJ(true);
EXPECT_EQ("foo", request1.send().wait(waitScope).getX());
EXPECT_EQ(1, callCount0);
EXPECT_EQ(1, callCount1);
}
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);
}
KJ_TEST("use pipeline after dropping response") {
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();
test::TestPipeline::GetCapResults::Pipeline pipeline = kj::mv(promise);
{
auto response = promise.wait(waitScope);
KJ_EXPECT(response.getS() == "bar");
}
auto pipelineRequest = pipeline.getOutBox().getCap().fooRequest();
pipelineRequest.setI(321);
auto pipelinePromise = pipelineRequest.send();
auto pipelineRequest2 = pipeline.getOutBox().getCap().castAs<test::TestExtends>().graultRequest();
auto pipelinePromise2 = pipelineRequest2.send();
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);
}
KJ_TEST("context.setPipeline") {
kj::EventLoop loop;
kj::WaitScope waitScope(loop);
int callCount = 0;
test::TestPipeline::Client client(kj::heap<TestPipelineImpl>(callCount));
auto promise = client.getCapPipelineOnlyRequest().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();
EXPECT_EQ(0, callCount);
auto response = pipelinePromise.wait(waitScope);
EXPECT_EQ("bar", response.getX());
auto response2 = pipelinePromise2.wait(waitScope);
checkTestMessage(response2);
EXPECT_EQ(3, callCount);
// The original promise never completed.
KJ_EXPECT(!promise.poll(waitScope));
}
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
//
// Unfortunately 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);
}
KJ_TEST("Promise<RemotePromise<T>> automatically reduces to RemotePromise<T>") {
kj::EventLoop loop;
kj::WaitScope waitScope(loop);
int callCount = 0;
test::TestInterface::Client client(kj::heap<TestInterfaceImpl>(callCount));
RemotePromise<test::TestInterface::FooResults> promise = kj::evalLater([&]() {
auto request = client.fooRequest();
request.setI(123);
request.setJ(true);
return request.send();
});
EXPECT_EQ(0, callCount);
auto response = promise.wait(waitScope);
EXPECT_EQ("foo", response.getX());
EXPECT_EQ(1, callCount);
}
KJ_TEST("Promise<RemotePromise<T>> automatically reduces to RemotePromise<T> with pipelining") {
kj::EventLoop loop;
kj::WaitScope waitScope(loop);
int callCount = 0;
int chainedCallCount = 0;
test::TestPipeline::Client client(kj::heap<TestPipelineImpl>(callCount));
auto promise = kj::evalLater([&]() {
auto request = client.getCapRequest();
request.setN(234);
request.setInCap(test::TestInterface::Client(kj::heap<TestInterfaceImpl>(chainedCallCount)));
return request.send();
});
auto pipelineRequest = promise.getOutBox().getCap().fooRequest();
pipelineRequest.setI(321);
auto pipelinePromise = pipelineRequest.send();
EXPECT_EQ(0, callCount);
EXPECT_EQ(0, chainedCallCount);
auto response = pipelinePromise.wait(waitScope);
EXPECT_EQ("bar", response.getX());
EXPECT_EQ(2, callCount);
EXPECT_EQ(1, chainedCallCount);
}
KJ_TEST("clone() with caps") {
int dummy = 0;
MallocMessageBuilder builder(2048);
auto root = builder.getRoot<AnyPointer>().initAs<List<test::TestInterface>>(3);
root.set(0, kj::heap<TestInterfaceImpl>(dummy));
root.set(1, kj::heap<TestInterfaceImpl>(dummy));
root.set(2, kj::heap<TestInterfaceImpl>(dummy));
auto copyPtr = clone(root.asReader());
auto& copy = *copyPtr;
KJ_ASSERT(copy.size() == 3);
KJ_EXPECT(ClientHook::from(copy[0]).get() == ClientHook::from(root[0]).get());
KJ_EXPECT(ClientHook::from(copy[1]).get() == ClientHook::from(root[1]).get());
KJ_EXPECT(ClientHook::from(copy[2]).get() == ClientHook::from(root[2]).get());
KJ_EXPECT(ClientHook::from(copy[0]).get() != ClientHook::from(root[1]).get());
KJ_EXPECT(ClientHook::from(copy[1]).get() != ClientHook::from(root[2]).get());
KJ_EXPECT(ClientHook::from(copy[2]).get() != ClientHook::from(root[0]).get());
}
KJ_TEST("Streaming calls block subsequent calls") {
kj::EventLoop loop;
kj::WaitScope waitScope(loop);
auto ownServer = kj::heap<TestStreamingImpl>();
auto& server = *ownServer;
test::TestStreaming::Client cap = kj::mv(ownServer);
kj::Promise<void> promise1 = nullptr, promise2 = nullptr, promise3 = nullptr;
{
auto req = cap.doStreamIRequest();
req.setI(123);
promise1 = req.send();
}
{
auto req = cap.doStreamJRequest();
req.setJ(321);
promise2 = req.send();
}
{
auto req = cap.doStreamIRequest();
req.setI(456);
promise3 = req.send();
}
auto promise4 = cap.finishStreamRequest().send();
KJ_EXPECT(server.iSum == 0);
KJ_EXPECT(server.jSum == 0);
KJ_EXPECT(!promise1.poll(waitScope));
KJ_EXPECT(!promise2.poll(waitScope));
KJ_EXPECT(!promise3.poll(waitScope));
KJ_EXPECT(!promise4.poll(waitScope));
KJ_EXPECT(server.iSum == 123);
KJ_EXPECT(server.jSum == 0);
KJ_ASSERT_NONNULL(server.fulfiller)->fulfill();
KJ_EXPECT(promise1.poll(waitScope));
KJ_EXPECT(!promise2.poll(waitScope));
KJ_EXPECT(!promise3.poll(waitScope));
KJ_EXPECT(!promise4.poll(waitScope));
KJ_EXPECT(server.iSum == 123);
KJ_EXPECT(server.jSum == 321);
KJ_ASSERT_NONNULL(server.fulfiller)->fulfill();
KJ_EXPECT(promise1.poll(waitScope));
KJ_EXPECT(promise2.poll(waitScope));
KJ_EXPECT(!promise3.poll(waitScope));
KJ_EXPECT(!promise4.poll(waitScope));
KJ_EXPECT(server.iSum == 579);
KJ_EXPECT(server.jSum == 321);
KJ_ASSERT_NONNULL(server.fulfiller)->fulfill();
KJ_EXPECT(promise1.poll(waitScope));
KJ_EXPECT(promise2.poll(waitScope));
KJ_EXPECT(promise3.poll(waitScope));
KJ_EXPECT(promise4.poll(waitScope));
auto result = promise4.wait(waitScope);
KJ_EXPECT(result.getTotalI() == 579);
KJ_EXPECT(result.getTotalJ() == 321);
}
KJ_TEST("Streaming calls can be canceled") {
kj::EventLoop loop;
kj::WaitScope waitScope(loop);
auto ownServer = kj::heap<TestStreamingImpl>();
auto& server = *ownServer;
test::TestStreaming::Client cap = kj::mv(ownServer);
kj::Promise<void> promise1 = nullptr, promise2 = nullptr, promise3 = nullptr;
{
auto req = cap.doStreamIRequest();
req.setI(123);
promise1 = req.send();
}
{
auto req = cap.doStreamJRequest();
req.setJ(321);
promise2 = req.send();
}
{
auto req = cap.doStreamIRequest();
req.setI(456);
promise3 = req.send();
}
auto promise4 = cap.finishStreamRequest().send();
// Cancel the streaming calls.
promise1 = nullptr;
promise2 = nullptr;
promise3 = nullptr;
KJ_EXPECT(server.iSum == 0);
KJ_EXPECT(server.jSum == 0);
KJ_EXPECT(!promise4.poll(waitScope));
KJ_EXPECT(server.iSum == 123);
KJ_EXPECT(server.jSum == 0);
KJ_ASSERT_NONNULL(server.fulfiller)->fulfill();
KJ_EXPECT(!promise4.poll(waitScope));
// The call to doStreamJ() opted into cancellation so the next call to doStreamI() happens
// immediately.
KJ_EXPECT(server.iSum == 579);
KJ_EXPECT(server.jSum == 321);
KJ_ASSERT_NONNULL(server.fulfiller)->fulfill();
KJ_EXPECT(promise4.poll(waitScope));
auto result = promise4.wait(waitScope);
KJ_EXPECT(result.getTotalI() == 579);
KJ_EXPECT(result.getTotalJ() == 321);
}
KJ_TEST("Streaming call throwing cascades to following calls") {
kj::EventLoop loop;
kj::WaitScope waitScope(loop);
auto ownServer = kj::heap<TestStreamingImpl>();
auto& server = *ownServer;
test::TestStreaming::Client cap = kj::mv(ownServer);
server.jShouldThrow = true;
kj::Promise<void> promise1 = nullptr, promise2 = nullptr, promise3 = nullptr;
{
auto req = cap.doStreamIRequest();
req.setI(123);
promise1 = req.send();
}
{
auto req = cap.doStreamJRequest();
req.setJ(321);
promise2 = req.send();
}
{
auto req = cap.doStreamIRequest();
req.setI(456);
promise3 = req.send();
}
auto promise4 = cap.finishStreamRequest().send();
KJ_EXPECT(server.iSum == 0);
KJ_EXPECT(server.jSum == 0);
KJ_EXPECT(!promise1.poll(waitScope));
KJ_EXPECT(!promise2.poll(waitScope));
KJ_EXPECT(!promise3.poll(waitScope));
KJ_EXPECT(!promise4.poll(waitScope));
KJ_EXPECT(server.iSum == 123);
KJ_EXPECT(server.jSum == 0);
KJ_ASSERT_NONNULL(server.fulfiller)->fulfill();
KJ_EXPECT(promise1.poll(waitScope));
KJ_EXPECT(promise2.poll(waitScope));
KJ_EXPECT(promise3.poll(waitScope));
KJ_EXPECT(promise4.poll(waitScope));
KJ_EXPECT(server.iSum == 123);
KJ_EXPECT(server.jSum == 321);
KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("throw requested", promise2.wait(waitScope));
KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("throw requested", promise3.wait(waitScope));
KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("throw requested", promise4.ignoreResult().wait(waitScope));
}
} // namespace
} // namespace _
} // namespace capnp