Bitcoin Core 31.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 <atomic>
9#include <capnp/capability.h>
10#include <capnp/rpc.h>
11#include <cassert>
12#include <condition_variable>
13#include <cstdint>
14#include <cstring>
15#include <functional>
16#include <future>
17#include <kj/async.h>
18#include <kj/async-io.h>
19#include <kj/common.h>
20#include <kj/debug.h>
21#include <kj/memory.h>
22#include <kj/test.h>
23#include <memory>
24#include <mp/proxy.h>
25#include <mp/proxy.capnp.h>
26#include <mp/proxy-io.h>
27#include <mp/util.h>
28#include <mp/version.h>
29#include <optional>
30#include <set>
31#include <stdexcept>
32#include <string>
33#include <string_view>
34#include <thread>
35#include <type_traits>
36#include <utility>
37#include <vector>
38
39namespace mp {
40namespace test {
41
45static_assert(std::is_integral_v<decltype(kMP_MAJOR_VERSION)>, "MP_MAJOR_VERSION must be an integral constant");
46static_assert(std::is_integral_v<decltype(kMP_MINOR_VERSION)>, "MP_MINOR_VERSION must be an integral constant");
47
63{
64public:
65 std::function<void()> server_disconnect;
66 std::function<void()> client_disconnect;
67 std::promise<std::unique_ptr<ProxyClient<messages::FooInterface>>> client_promise;
68 std::unique_ptr<ProxyClient<messages::FooInterface>> client;
72 std::thread thread;
73
74 TestSetup(bool client_owns_connection = true)
75 : thread{[&] {
76 EventLoop loop("mptest", [](mp::LogMessage log) {
77 // Info logs are not printed by default, but will be shown with `mptest --verbose`
78 KJ_LOG(INFO, log.level, log.message);
79 if (log.level == mp::Log::Raise) throw std::runtime_error(log.message);
80 });
81 auto pipe = loop.m_io_context.provider->newTwoWayPipe();
82
83 auto server_connection =
84 std::make_unique<Connection>(loop, kj::mv(pipe.ends[0]), [&](Connection& connection) {
85 auto server_proxy = kj::heap<ProxyServer<messages::FooInterface>>(
86 std::make_shared<FooImplementation>(), connection);
87 server = server_proxy;
88 return capnp::Capability::Client(kj::mv(server_proxy));
89 });
90 server_disconnect = [&] { loop.sync([&] { server_connection.reset(); }); };
91 // Set handler to destroy the server when the client disconnects. This
92 // is ignored if server_disconnect() is called instead.
93 server_connection->onDisconnect([&] { server_connection.reset(); });
94
95 auto client_connection = std::make_unique<Connection>(loop, kj::mv(pipe.ends[1]));
96 auto client_proxy = std::make_unique<ProxyClient<messages::FooInterface>>(
97 client_connection->m_rpc_system->bootstrap(ServerVatId().vat_id).castAs<messages::FooInterface>(),
98 client_connection.get(), /* destroy_connection= */ client_owns_connection);
99 if (client_owns_connection) {
100 (void)client_connection.release();
101 } else {
102 client_disconnect = [&] { loop.sync([&] { client_connection.reset(); }); };
103 }
104
105 client_promise.set_value(std::move(client_proxy));
106 loop.loop();
107 }}
108 {
109 client = client_promise.get_future().get();
110 }
111
113 {
114 // Test that client cleanup_fns are executed.
115 bool destroyed = false;
116 client->m_context.cleanup_fns.emplace_front([&destroyed] { destroyed = true; });
117 client.reset();
118 KJ_EXPECT(destroyed);
119
120 thread.join();
121 }
122};
123
124KJ_TEST("Call FooInterface methods")
125{
127 ProxyClient<messages::FooInterface>* foo = setup.client.get();
128
129 KJ_EXPECT(foo->add(1, 2) == 3);
130 int ret;
131 foo->addOut(3, 4, ret);
132 KJ_EXPECT(ret == 7);
133 foo->addInOut(3, ret);
134 KJ_EXPECT(ret == 10);
135
136 FooStruct in;
137 in.name = "name";
138 in.setint.insert(2);
139 in.setint.insert(1);
140 in.vbool.push_back(false);
141 in.vbool.push_back(true);
142 in.vbool.push_back(false);
143 FooStruct out = foo->pass(in);
144 KJ_EXPECT(in.name == out.name);
145 KJ_EXPECT(in.setint.size() == out.setint.size());
146 for (auto init{in.setint.begin()}, outit{out.setint.begin()}; init != in.setint.end() && outit != out.setint.end(); ++init, ++outit) {
147 KJ_EXPECT(*init == *outit);
148 }
149 KJ_EXPECT(in.vbool.size() == out.vbool.size());
150 for (size_t i = 0; i < in.vbool.size(); ++i) {
151 KJ_EXPECT(in.vbool[i] == out.vbool[i]);
152 }
153
154 FooStruct err;
155 try {
156 foo->raise(in);
157 } catch (const FooStruct& e) {
158 err = e;
159 }
160 KJ_EXPECT(in.name == err.name);
161
162 class Callback : public ExtendedCallback
163 {
164 public:
165 Callback(int expect, int ret) : m_expect(expect), m_ret(ret) {}
166 int call(int arg) override
167 {
168 KJ_EXPECT(arg == m_expect);
169 return m_ret;
170 }
171 int callExtended(int arg) override
172 {
173 KJ_EXPECT(arg == m_expect + 10);
174 return m_ret + 10;
175 }
176 int m_expect, m_ret;
177 };
178
179 foo->initThreadMap();
180 Callback callback(1, 2);
181 KJ_EXPECT(foo->callback(callback, 1) == 2);
182 KJ_EXPECT(foo->callbackUnique(std::make_unique<Callback>(3, 4), 3) == 4);
183 KJ_EXPECT(foo->callbackShared(std::make_shared<Callback>(5, 6), 5) == 6);
184 auto saved = std::make_shared<Callback>(7, 8);
185 KJ_EXPECT(saved.use_count() == 1);
186 foo->saveCallback(saved);
187 KJ_EXPECT(saved.use_count() == 2);
188 foo->callbackSaved(7);
189 KJ_EXPECT(foo->callbackSaved(7) == 8);
190 foo->saveCallback(nullptr);
191 KJ_EXPECT(saved.use_count() == 1);
192 KJ_EXPECT(foo->callbackExtended(callback, 11) == 12);
193
194 FooCustom custom_in;
195 custom_in.v1 = "v1";
196 custom_in.v2 = 5;
197 FooCustom custom_out = foo->passCustom(custom_in);
198 KJ_EXPECT(custom_in.v1 == custom_out.v1);
199 KJ_EXPECT(custom_in.v2 == custom_out.v2);
200
201 foo->passEmpty(FooEmpty{});
202
203 FooMessage message1;
204 message1.message = "init";
205 FooMessage message2{foo->passMessage(message1)};
206 KJ_EXPECT(message2.message == "init build read call build read");
207
208 FooMutable mut;
209 mut.message = "init";
210 foo->passMutable(mut);
211 KJ_EXPECT(mut.message == "init build pass call return read");
212
213 KJ_EXPECT(foo->passFn([]{ return 10; }) == 10);
214}
215
216KJ_TEST("Call IPC method after client connection is closed")
217{
218 TestSetup setup{/*client_owns_connection=*/false};
219 ProxyClient<messages::FooInterface>* foo = setup.client.get();
220 KJ_EXPECT(foo->add(1, 2) == 3);
221 setup.client_disconnect();
222
223 bool disconnected{false};
224 try {
225 foo->add(1, 2);
226 } catch (const std::runtime_error& e) {
227 KJ_EXPECT(std::string_view{e.what()} == "IPC client method called after disconnect.");
228 disconnected = true;
229 }
230 KJ_EXPECT(disconnected);
231}
232
233KJ_TEST("Calling IPC method after server connection is closed")
234{
236 ProxyClient<messages::FooInterface>* foo = setup.client.get();
237 KJ_EXPECT(foo->add(1, 2) == 3);
238 setup.server_disconnect();
239
240 bool disconnected{false};
241 try {
242 foo->add(1, 2);
243 } catch (const std::runtime_error& e) {
244 KJ_EXPECT(std::string_view{e.what()} == "IPC client method call interrupted by disconnect.");
245 disconnected = true;
246 }
247 KJ_EXPECT(disconnected);
248}
249
250KJ_TEST("Calling IPC method and disconnecting during the call")
251{
252 TestSetup setup{/*client_owns_connection=*/false};
253 ProxyClient<messages::FooInterface>* foo = setup.client.get();
254 KJ_EXPECT(foo->add(1, 2) == 3);
255
256 // Set m_fn to initiate client disconnect when server is in the middle of
257 // handling the callFn call to make sure this case is handled cleanly.
258 setup.server->m_impl->m_fn = setup.client_disconnect;
259
260 bool disconnected{false};
261 try {
262 foo->callFn();
263 } catch (const std::runtime_error& e) {
264 KJ_EXPECT(std::string_view{e.what()} == "IPC client method call interrupted by disconnect.");
265 disconnected = true;
266 }
267 KJ_EXPECT(disconnected);
268}
269
270KJ_TEST("Calling IPC method, disconnecting and blocking during the call")
271{
272 // This test is similar to last test, except that instead of letting the IPC
273 // call return immediately after triggering a disconnect, make it disconnect
274 // & wait so server is forced to deal with having a disconnection and call
275 // in flight at the same time.
276 //
277 // Test uses callFnAsync() instead of callFn() to implement this. Both of
278 // these methods have the same implementation, but the callFnAsync() capnp
279 // method declaration takes an mp.Context argument so the method executes on
280 // an asynchronous thread instead of executing in the event loop thread, so
281 // it is able to block without deadlocking the event lock thread.
282 //
283 // This test adds important coverage because it causes the server Connection
284 // object to be destroyed before ProxyServer object, which is not a
285 // condition that usually happens because the m_rpc_system.reset() call in
286 // the ~Connection destructor usually would immediately free all remaining
287 // ProxyServer objects associated with the connection. Having an in-progress
288 // RPC call requires keeping the ProxyServer longer.
289
290 std::promise<void> signal;
291 TestSetup setup{/*client_owns_connection=*/false};
292 ProxyClient<messages::FooInterface>* foo = setup.client.get();
293 KJ_EXPECT(foo->add(1, 2) == 3);
294
295 foo->initThreadMap();
296 setup.server->m_impl->m_fn = [&] {
297 EventLoopRef loop{*setup.server->m_context.loop};
298 setup.client_disconnect();
299 signal.get_future().get();
300 };
301
302 bool disconnected{false};
303 try {
304 foo->callFnAsync();
305 } catch (const std::runtime_error& e) {
306 KJ_EXPECT(std::string_view{e.what()} == "IPC client method call interrupted by disconnect.");
307 disconnected = true;
308 }
309 KJ_EXPECT(disconnected);
310
311 // Now that the disconnect has been detected, set signal allowing the
312 // callFnAsync() IPC call to return. Since signalling may not wake up the
313 // thread right away, it is important for the signal variable to be declared
314 // *before* the TestSetup variable so is not destroyed while
315 // signal.get_future().get() is called.
316 signal.set_value();
317}
318
319KJ_TEST("Make simultaneous IPC calls on single remote thread")
320{
322 ProxyClient<messages::FooInterface>* foo = setup.client.get();
323 std::promise<void> signal;
324
325 foo->initThreadMap();
326 // Use callFnAsync() to get the client to set up the request_thread
327 // that will be used for the test.
328 setup.server->m_impl->m_fn = [&] {};
329 foo->callFnAsync();
331 Thread::Client *callback_thread, *request_thread;
332 foo->m_context.loop->sync([&] {
333 Lock lock(tc.waiter->m_mutex);
334 callback_thread = &tc.callback_threads.at(foo->m_context.connection)->m_client;
335 request_thread = &tc.request_threads.at(foo->m_context.connection)->m_client;
336 });
337
338 // Call callIntFnAsync 3 times with n=100, 200, 300
339 std::atomic<int> expected = 100;
340
341 setup.server->m_impl->m_int_fn = [&](int n) {
342 assert(n == expected);
343 expected += 100;
344 return n;
345 };
346
347 auto client{foo->m_client};
348 std::atomic<size_t> running{3};
349 foo->m_context.loop->sync([&]
350 {
351 for (size_t i = 0; i < running; i++)
352 {
353 auto request{client.callIntFnAsyncRequest()};
354 auto context{request.initContext()};
355 context.setCallbackThread(*callback_thread);
356 context.setThread(*request_thread);
357 request.setArg(100 * (i+1));
358 foo->m_context.loop->m_task_set->add(request.send().then(
359 [&running, &tc, i](auto&& results) {
360 assert(results.getResult() == static_cast<int32_t>(100 * (i+1)));
361 running -= 1;
362 tc.waiter->m_cv.notify_all();
363 }));
364 }
365 });
366 {
367 Lock lock(tc.waiter->m_mutex);
368 tc.waiter->wait(lock, [&running] { return running == 0; });
369 }
370 KJ_EXPECT(expected == 400);
371}
372
373} // namespace test
374} // namespace mp
int ret
Object holding network & rpc state associated with either an incoming server connection,...
Definition: proxy-io.h:407
Event loop implementation.
Definition: proxy-io.h:239
kj::AsyncIoContext m_io_context
Capnp IO context.
Definition: proxy-io.h:327
Event loop smart pointer automatically managing m_num_clients.
Definition: proxy.h:51
Definition: util.h:171
Test setup class creating a two way connection between a ProxyServer<FooInterface> object and a Proxy...
Definition: test.cpp:63
std::promise< std::unique_ptr< ProxyClient< messages::FooInterface > > > client_promise
Definition: test.cpp:67
TestSetup(bool client_owns_connection=true)
Definition: test.cpp:74
std::function< void()> server_disconnect
Definition: test.cpp:65
std::function< void()> client_disconnect
Definition: test.cpp:66
std::thread thread
Thread variable should be after other struct members so the thread does not start until the other mem...
Definition: test.cpp:72
ProxyServer< messages::FooInterface > * server
Definition: test.cpp:69
std::unique_ptr< ProxyClient< messages::FooInterface > > client
Definition: test.cpp:68
constexpr auto kMP_MAJOR_VERSION
Check version.h header values.
Definition: test.cpp:43
constexpr auto kMP_MINOR_VERSION
Definition: test.cpp:44
Functions to serialize / deserialize common bitcoin types.
Definition: common-types.h:57
thread_local ThreadContext g_thread_context
Definition: proxy.cpp:41
KJ_TEST("SpawnProcess does not run callback in child")
Definition: spawn_tests.cpp:44
Log level
The severity level of this message.
Definition: proxy-io.h:144
std::string message
Message to be logged.
Definition: proxy-io.h:141
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:484
The thread_local ThreadContext g_thread_context struct provides information about individual threads ...
Definition: proxy-io.h:659
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
static int setup(void)
Definition: tests.c:7808
#define expect(bit)
assert(!tx.IsCoinBase())
Major and minor version numbers.
#define MP_MAJOR_VERSION
Major version number.
Definition: version.h:27
#define MP_MINOR_VERSION
Minor version number.
Definition: version.h:32