Bitcoin Core 29.99.0
P2P Digital Currency
test.cpp
Go to the documentation of this file.
1// Copyright (c) The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <mp/test/foo.capnp.h>
6#include <mp/test/foo.capnp.proxy.h>
7
8#include <capnp/capability.h>
9#include <capnp/rpc.h>
10#include <cstring>
11#include <functional>
12#include <future>
13#include <iostream>
14#include <kj/async.h>
15#include <kj/async-io.h>
16#include <kj/common.h>
17#include <kj/debug.h>
18#include <kj/memory.h>
19#include <kj/test.h>
20#include <memory>
21#include <mp/proxy.h>
22#include <mp/proxy-io.h>
23#include <optional>
24#include <set>
25#include <stdexcept>
26#include <string>
27#include <string_view>
28#include <thread>
29#include <utility>
30#include <vector>
31
32namespace mp {
33namespace test {
34
50{
51public:
52 std::function<void()> server_disconnect;
53 std::function<void()> client_disconnect;
54 std::promise<std::unique_ptr<ProxyClient<messages::FooInterface>>> client_promise;
55 std::unique_ptr<ProxyClient<messages::FooInterface>> client;
59 std::thread thread;
60
61 TestSetup(bool client_owns_connection = true)
62 : thread{[&] {
63 EventLoop loop("mptest", [](bool raise, const std::string& log) {
64 std::cout << "LOG" << raise << ": " << log << "\n";
65 if (raise) throw std::runtime_error(log);
66 });
67 auto pipe = loop.m_io_context.provider->newTwoWayPipe();
68
69 auto server_connection =
70 std::make_unique<Connection>(loop, kj::mv(pipe.ends[0]), [&](Connection& connection) {
71 auto server_proxy = kj::heap<ProxyServer<messages::FooInterface>>(
72 std::make_shared<FooImplementation>(), connection);
73 server = server_proxy;
74 return capnp::Capability::Client(kj::mv(server_proxy));
75 });
76 server_disconnect = [&] { loop.sync([&] { server_connection.reset(); }); };
77 // Set handler to destroy the server when the client disconnects. This
78 // is ignored if server_disconnect() is called instead.
79 server_connection->onDisconnect([&] { server_connection.reset(); });
80
81 auto client_connection = std::make_unique<Connection>(loop, kj::mv(pipe.ends[1]));
82 auto client_proxy = std::make_unique<ProxyClient<messages::FooInterface>>(
83 client_connection->m_rpc_system->bootstrap(ServerVatId().vat_id).castAs<messages::FooInterface>(),
84 client_connection.get(), /* destroy_connection= */ client_owns_connection);
85 if (client_owns_connection) {
86 client_connection.release();
87 } else {
88 client_disconnect = [&] { loop.sync([&] { client_connection.reset(); }); };
89 }
90
91 client_promise.set_value(std::move(client_proxy));
92 loop.loop();
93 }}
94 {
95 client = client_promise.get_future().get();
96 }
97
99 {
100 // Test that client cleanup_fns are executed.
101 bool destroyed = false;
102 client->m_context.cleanup_fns.emplace_front([&destroyed] { destroyed = true; });
103 client.reset();
104 KJ_EXPECT(destroyed);
105
106 thread.join();
107 }
108};
109
110KJ_TEST("Call FooInterface methods")
111{
112 TestSetup setup;
114
115 KJ_EXPECT(foo->add(1, 2) == 3);
116
117 FooStruct in;
118 in.name = "name";
119 in.setint.insert(2);
120 in.setint.insert(1);
121 in.vbool.push_back(false);
122 in.vbool.push_back(true);
123 in.vbool.push_back(false);
124 FooStruct out = foo->pass(in);
125 KJ_EXPECT(in.name == out.name);
126 KJ_EXPECT(in.setint.size() == out.setint.size());
127 for (auto init{in.setint.begin()}, outit{out.setint.begin()}; init != in.setint.end() && outit != out.setint.end(); ++init, ++outit) {
128 KJ_EXPECT(*init == *outit);
129 }
130 KJ_EXPECT(in.vbool.size() == out.vbool.size());
131 for (size_t i = 0; i < in.vbool.size(); ++i) {
132 KJ_EXPECT(in.vbool[i] == out.vbool[i]);
133 }
134
135 FooStruct err;
136 try {
137 foo->raise(in);
138 } catch (const FooStruct& e) {
139 err = e;
140 }
141 KJ_EXPECT(in.name == err.name);
142
143 class Callback : public ExtendedCallback
144 {
145 public:
146 Callback(int expect, int ret) : m_expect(expect), m_ret(ret) {}
147 int call(int arg) override
148 {
149 KJ_EXPECT(arg == m_expect);
150 return m_ret;
151 }
152 int callExtended(int arg) override
153 {
154 KJ_EXPECT(arg == m_expect + 10);
155 return m_ret + 10;
156 }
157 int m_expect, m_ret;
158 };
159
160 foo->initThreadMap();
161 Callback callback(1, 2);
162 KJ_EXPECT(foo->callback(callback, 1) == 2);
163 KJ_EXPECT(foo->callbackUnique(std::make_unique<Callback>(3, 4), 3) == 4);
164 KJ_EXPECT(foo->callbackShared(std::make_shared<Callback>(5, 6), 5) == 6);
165 auto saved = std::make_shared<Callback>(7, 8);
166 KJ_EXPECT(saved.use_count() == 1);
167 foo->saveCallback(saved);
168 KJ_EXPECT(saved.use_count() == 2);
169 foo->callbackSaved(7);
170 KJ_EXPECT(foo->callbackSaved(7) == 8);
171 foo->saveCallback(nullptr);
172 KJ_EXPECT(saved.use_count() == 1);
173 KJ_EXPECT(foo->callbackExtended(callback, 11) == 12);
174
175 FooCustom custom_in;
176 custom_in.v1 = "v1";
177 custom_in.v2 = 5;
178 FooCustom custom_out = foo->passCustom(custom_in);
179 KJ_EXPECT(custom_in.v1 == custom_out.v1);
180 KJ_EXPECT(custom_in.v2 == custom_out.v2);
181
182 foo->passEmpty(FooEmpty{});
183
184 FooMessage message1;
185 message1.message = "init";
186 FooMessage message2{foo->passMessage(message1)};
187 KJ_EXPECT(message2.message == "init build read call build read");
188
189 FooMutable mut;
190 mut.message = "init";
191 foo->passMutable(mut);
192 KJ_EXPECT(mut.message == "init build pass call return read");
193
194 KJ_EXPECT(foo->passFn([]{ return 10; }) == 10);
195}
196
197KJ_TEST("Call IPC method after client connection is closed")
198{
199 TestSetup setup{/*client_owns_connection=*/false};
200 ProxyClient<messages::FooInterface>* foo = setup.client.get();
201 KJ_EXPECT(foo->add(1, 2) == 3);
202 setup.client_disconnect();
203
204 bool disconnected{false};
205 try {
206 foo->add(1, 2);
207 } catch (const std::runtime_error& e) {
208 KJ_EXPECT(std::string_view{e.what()} == "IPC client method called after disconnect.");
209 disconnected = true;
210 }
211 KJ_EXPECT(disconnected);
212}
213
214KJ_TEST("Calling IPC method after server connection is closed")
215{
216 TestSetup setup;
218 KJ_EXPECT(foo->add(1, 2) == 3);
219 setup.server_disconnect();
220
221 bool disconnected{false};
222 try {
223 foo->add(1, 2);
224 } catch (const std::runtime_error& e) {
225 KJ_EXPECT(std::string_view{e.what()} == "IPC client method call interrupted by disconnect.");
226 disconnected = true;
227 }
228 KJ_EXPECT(disconnected);
229}
230
231KJ_TEST("Calling IPC method and disconnecting during the call")
232{
233 TestSetup setup{/*client_owns_connection=*/false};
234 ProxyClient<messages::FooInterface>* foo = setup.client.get();
235 KJ_EXPECT(foo->add(1, 2) == 3);
236
237 // Set m_fn to initiate client disconnect when server is in the middle of
238 // handling the callFn call to make sure this case is handled cleanly.
239 setup.server->m_impl->m_fn = setup.client_disconnect;
240
241 bool disconnected{false};
242 try {
243 foo->callFn();
244 } catch (const std::runtime_error& e) {
245 KJ_EXPECT(std::string_view{e.what()} == "IPC client method call interrupted by disconnect.");
246 disconnected = true;
247 }
248 KJ_EXPECT(disconnected);
249}
250
251KJ_TEST("Calling IPC method, disconnecting and blocking during the call")
252{
253 // This test is similar to last test, except that instead of letting the IPC
254 // call return immediately after triggering a disconnect, make it disconnect
255 // & wait so server is forced to deal with having a disconnection and call
256 // in flight at the same time.
257 //
258 // Test uses callFnAsync() instead of callFn() to implement this. Both of
259 // these methods have the same implementation, but the callFnAsync() capnp
260 // method declaration takes an mp.Context argument so the method executes on
261 // an asynchronous thread instead of executing in the event loop thread, so
262 // it is able to block without deadlocking the event lock thread.
263 //
264 // This test adds important coverage because it causes the server Connection
265 // object to be destroyed before ProxyServer object, which is not a
266 // condition that usually happens because the m_rpc_system.reset() call in
267 // the ~Connection destructor usually would immediately free all remaining
268 // ProxyServer objects associated with the connection. Having an in-progress
269 // RPC call requires keeping the ProxyServer longer.
270
271 std::promise<void> signal;
272 TestSetup setup{/*client_owns_connection=*/false};
273 ProxyClient<messages::FooInterface>* foo = setup.client.get();
274 KJ_EXPECT(foo->add(1, 2) == 3);
275
276 foo->initThreadMap();
277 setup.server->m_impl->m_fn = [&] {
278 EventLoopRef loop{*setup.server->m_context.loop};
279 setup.client_disconnect();
280 signal.get_future().get();
281 };
282
283 bool disconnected{false};
284 try {
285 foo->callFnAsync();
286 } catch (const std::runtime_error& e) {
287 KJ_EXPECT(std::string_view{e.what()} == "IPC client method call interrupted by disconnect.");
288 disconnected = true;
289 }
290 KJ_EXPECT(disconnected);
291
292 // Now that the disconnect has been detected, set signal allowing the
293 // callFnAsync() IPC call to return. Since signalling may not wake up the
294 // thread right away, it is important for the signal variable to be declared
295 // *before* the TestSetup variable so is not destroyed while
296 // signal.get_future().get() is called.
297 signal.set_value();
298}
299
300} // namespace test
301} // namespace mp
int ret
Object holding network & rpc state associated with either an incoming server connection,...
Definition: proxy-io.h:321
Event loop implementation.
Definition: proxy-io.h:161
kj::AsyncIoContext m_io_context
Capnp IO context.
Definition: proxy-io.h:247
void * m_context
External context pointer.
Definition: proxy-io.h:262
Event loop smart pointer automatically managing m_num_clients.
Definition: proxy.h:51
Test setup class creating a two way connection between a ProxyServer<FooInterface> object and a Proxy...
Definition: test.cpp:50
std::promise< std::unique_ptr< ProxyClient< messages::FooInterface > > > client_promise
Definition: test.cpp:54
TestSetup(bool client_owns_connection=true)
Definition: test.cpp:61
std::function< void()> server_disconnect
Definition: test.cpp:52
std::function< void()> client_disconnect
Definition: test.cpp:53
std::thread thread
Thread variable should be after other struct members so the thread does not start until the other mem...
Definition: test.cpp:59
ProxyServer< messages::FooInterface > * server
Definition: test.cpp:56
std::unique_ptr< ProxyClient< messages::FooInterface > > client
Definition: test.cpp:55
KJ_TEST("Calling IPC method, disconnecting and blocking during the call")
Definition: test.cpp:251
Functions to serialize / deserialize common bitcoin types.
Definition: common-types.h:57
Mapping from capnp interface type to proxy client implementation (specializations are generated by pr...
Definition: proxy.h:25
Vat id for server side of connection.
Definition: proxy-io.h:390
std::string v1
Definition: foo.h:30
std::string message
Definition: foo.h:40
std::string message
Definition: foo.h:45
std::string name
Definition: foo.h:21
std::set< int > setint
Definition: foo.h:22
std::vector< bool > vbool
Definition: foo.h:23
#define expect(bit)