/ src / test / ipc_test.cpp
ipc_test.cpp
 1  // Copyright (c) 2023 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 <logging.h>
 6  #include <mp/proxy-types.h>
 7  #include <test/ipc_test.capnp.h>
 8  #include <test/ipc_test.capnp.proxy.h>
 9  #include <test/ipc_test.h>
10  
11  #include <future>
12  #include <kj/common.h>
13  #include <kj/memory.h>
14  #include <kj/test.h>
15  
16  #include <boost/test/unit_test.hpp>
17  
18  //! Unit test that tests execution of IPC calls without actually creating a
19  //! separate process. This test is primarily intended to verify behavior of type
20  //! conversion code that converts C++ objects to Cap'n Proto messages and vice
21  //! versa.
22  //!
23  //! The test creates a thread which creates a FooImplementation object (defined
24  //! in ipc_test.h) and a two-way pipe accepting IPC requests which call methods
25  //! on the object through FooInterface (defined in ipc_test.capnp).
26  void IpcTest()
27  {
28      // Setup: create FooImplemention object and listen for FooInterface requests
29      std::promise<std::unique_ptr<mp::ProxyClient<gen::FooInterface>>> foo_promise;
30      std::function<void()> disconnect_client;
31      std::thread thread([&]() {
32          mp::EventLoop loop("IpcTest", [](bool raise, const std::string& log) { LogPrintf("LOG%i: %s\n", raise, log); });
33          auto pipe = loop.m_io_context.provider->newTwoWayPipe();
34  
35          auto connection_client = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[0]));
36          auto foo_client = std::make_unique<mp::ProxyClient<gen::FooInterface>>(
37              connection_client->m_rpc_system.bootstrap(mp::ServerVatId().vat_id).castAs<gen::FooInterface>(),
38              connection_client.get(), /* destroy_connection= */ false);
39          foo_promise.set_value(std::move(foo_client));
40          disconnect_client = [&] { loop.sync([&] { connection_client.reset(); }); };
41  
42          auto connection_server = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[1]), [&](mp::Connection& connection) {
43              auto foo_server = kj::heap<mp::ProxyServer<gen::FooInterface>>(std::make_shared<FooImplementation>(), connection);
44              return capnp::Capability::Client(kj::mv(foo_server));
45          });
46          connection_server->onDisconnect([&] { connection_server.reset(); });
47          loop.loop();
48      });
49      std::unique_ptr<mp::ProxyClient<gen::FooInterface>> foo{foo_promise.get_future().get()};
50  
51      // Test: make sure arguments were sent and return value is received
52      BOOST_CHECK_EQUAL(foo->add(1, 2), 3);
53  
54      COutPoint txout1{Txid::FromUint256(uint256{100}), 200};
55      COutPoint txout2{foo->passOutPoint(txout1)};
56      BOOST_CHECK(txout1 == txout2);
57  
58      UniValue uni1{UniValue::VOBJ};
59      uni1.pushKV("i", 1);
60      uni1.pushKV("s", "two");
61      UniValue uni2{foo->passUniValue(uni1)};
62      BOOST_CHECK_EQUAL(uni1.write(), uni2.write());
63  
64      // Test cleanup: disconnect pipe and join thread
65      disconnect_client();
66      thread.join();
67  }