Skip to content

Commit b82a905

Browse files
committed
tests: Use IPC for template provider tests
The template provider tests now use proxy clients instead of direct access, allowing the test to exercise Cap'n proto serialization and deserialization paths. This also sets the stage for additional tests that check IPC disconnection scenarios
1 parent 8c53e2b commit b82a905

File tree

2 files changed

+93
-2
lines changed

2 files changed

+93
-2
lines changed

src/test/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@ PRIVATE
2727
sv2_messages_tests.cpp
2828
sv2_template_provider_tests.cpp
2929
)
30-
target_link_libraries(test_sv2 bitcoin_sv2)
30+
target_link_libraries(test_sv2 bitcoin_sv2 bitcoin_ipc)
31+
32+
if(TARGET Libmultiprocess::multiprocess)
33+
# test_sv2 directly includes generated IPC headers (init.capnp.h) which in turn
34+
# include libmultiprocess headers (mp/proxy.capnp.h). When using external
35+
# libmultiprocess, link the imported target to provide the necessary include paths.
36+
target_link_libraries(test_sv2 Libmultiprocess::multiprocess)
37+
endif()
3138

3239
function(add_boost_test source_file)
3340
if(NOT EXISTS ${source_file})

src/test/sv2_template_provider_tests.cpp

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
#include <addresstype.h>
22
#include <boost/test/unit_test.hpp>
3+
#include <interfaces/init.h>
4+
#include <interfaces/ipc.h>
35
#include <interfaces/mining.h>
6+
#include <ipc/capnp/init.capnp.h>
7+
#include <ipc/capnp/init.capnp.proxy.h>
48
#include <node/miner.h>
59
#include <node/transaction.h>
10+
#include <mp/proxy-io.h>
611
#include <sv2/messages.h>
712
#include <sv2/template_provider.h>
813
#include <test/util/net.h>
@@ -11,7 +16,9 @@
1116
#include <util/sock.h>
1217
#include <util/strencodings.h>
1318

19+
#include <future>
1420
#include <memory>
21+
#include <thread>
1522

1623
// For verbose debugging use:
1724
// build/src/test/test_sv2 --run_test=sv2_template_provider_tests --log_level=all -- -debug=sv2 -loglevel=sv2:trace -printtoconsole=1 | grep -v disabled
@@ -20,6 +27,82 @@ BOOST_FIXTURE_TEST_SUITE(sv2_template_provider_tests, TestChain100Setup)
2027

2128
static constexpr size_t SV2_NEW_TEMPLATE_MESSAGE_SIZE{91};
2229

30+
/**
31+
* A test implementation of the Init interface that provides a Mining
32+
* interface via the node context passed to its constructor.
33+
*/
34+
class TestInitImpl : public interfaces::Init {
35+
public:
36+
TestInitImpl(node::NodeContext& node) : m_node(node) {}
37+
std::unique_ptr<interfaces::Mining> makeMining() override { return interfaces::MakeMining(m_node); }
38+
private:
39+
node::NodeContext& m_node;
40+
};
41+
42+
/**
43+
* A class to set up an IPC server and client for testing.
44+
* The EventLoop runs in a separate thread.
45+
*/
46+
class IPCTestSetup {
47+
private:
48+
std::unique_ptr<mp::Connection> m_server_connection;
49+
std::unique_ptr<mp::Connection> m_client_connection;
50+
std::promise<std::unique_ptr<ipc::capnp::messages::Init::Client>> m_client;
51+
std::thread m_thread;
52+
53+
public:
54+
std::unique_ptr<interfaces::Init> m_init;
55+
56+
IPCTestSetup(node::NodeContext& node)
57+
: m_thread([&] {
58+
mp::EventLoop loop("test", [](bool raise, const std::string& log) {
59+
std::cout << "LOG" << raise << ": " << log << "\n";
60+
if (raise) throw std::runtime_error(log);
61+
});
62+
auto pipe{loop.m_io_context.provider->newTwoWayPipe()};
63+
64+
m_server_connection =
65+
std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[0]), [&](mp::Connection& connection) {
66+
auto server_proxy = kj::heap<mp::ProxyServer<ipc::capnp::messages::Init>>(
67+
std::make_shared<TestInitImpl>(node), connection
68+
);
69+
return capnp::Capability::Client(kj::mv(server_proxy));
70+
});
71+
m_server_connection->onDisconnect([&] {
72+
m_server_connection.reset();
73+
});
74+
m_client_connection = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[1]));
75+
m_client_connection->onDisconnect([&] {
76+
m_client_connection.reset();
77+
});
78+
m_client.set_value(std::make_unique<ipc::capnp::messages::Init::Client>(
79+
m_client_connection->m_rpc_system->bootstrap(mp::ServerVatId().vat_id).castAs<ipc::capnp::messages::Init>()));
80+
81+
loop.loop();
82+
})
83+
{
84+
auto client = m_client.get_future().get();
85+
// Create ProxyClient after EventLoop starts to ensure the initialization
86+
// "construct" request can be properly processed by the server.
87+
m_init = std::make_unique<mp::ProxyClient<ipc::capnp::messages::Init>>(
88+
std::move(*client), m_client_connection.get(), /* destroy_connection= */ true
89+
);
90+
// Release ownership of connection. ProxyClient manage its lifetime
91+
// because destroy_connection is set to true.
92+
m_client_connection.release();
93+
}
94+
95+
96+
~IPCTestSetup()
97+
{
98+
// Destroy the Init client.
99+
// The EventLoop will only stop after all
100+
// clients are destroyed.
101+
m_init.reset();
102+
m_thread.join();
103+
}
104+
};
105+
23106
/**
24107
* A class for testing the Template Provider. Each TPTester encapsulates a
25108
* Sv2TemplateProvider (the one being tested) as well as a Sv2Cipher
@@ -142,7 +225,8 @@ class TPTester {
142225

143226
BOOST_AUTO_TEST_CASE(client_tests)
144227
{
145-
auto mining{interfaces::MakeMining(m_node)};
228+
IPCTestSetup ipc_test_setup{m_node};
229+
auto mining{ipc_test_setup.m_init->makeMining()};
146230
TPTester tester{*mining};
147231

148232
tester.handshake();

0 commit comments

Comments
 (0)