7#include <mp/test/foo.capnp.h>
8#include <mp/test/foo.capnp.proxy.h>
11#include <condition_variable>
29#include <sys/socket.h>
38constexpr auto FAILURE_TIMEOUT = std::chrono::seconds{30};
48 std::string dir_template = (std::filesystem::temp_directory_path() /
"mptest-listener-XXXXXX").
string();
49 char* dir = mkdtemp(dir_template.data());
50 KJ_REQUIRE(dir !=
nullptr);
52 m_path = m_dir +
"/socket";
54 m_fd = socket(AF_UNIX, SOCK_STREAM, 0);
55 KJ_REQUIRE(m_fd >= 0);
58 addr.sun_family = AF_UNIX;
59 KJ_REQUIRE(m_path.size() <
sizeof(addr.sun_path));
60 std::strncpy(addr.sun_path, m_path.c_str(),
sizeof(addr.sun_path) - 1);
61 KJ_REQUIRE(bind(m_fd,
reinterpret_cast<sockaddr*
>(&addr),
sizeof(addr)) == 0);
62 KJ_REQUIRE(listen(m_fd, SOMAXCONN) == 0);
67 if (m_fd >= 0) close(m_fd);
68 if (!m_path.empty()) unlink(m_path.c_str());
69 if (!m_dir.empty()) rmdir(m_dir.c_str());
80 int MakeConnectedSocket()
const
82 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
86 addr.sun_family = AF_UNIX;
87 KJ_REQUIRE(m_path.size() <
sizeof(addr.sun_path));
88 std::strncpy(addr.sun_path, m_path.c_str(),
sizeof(addr.sun_path) - 1);
89 KJ_REQUIRE(connect(fd,
reinterpret_cast<sockaddr*
>(&addr),
sizeof(addr)) == 0);
105 explicit ClientSetup(
int fd)
106 : thread([this, fd] {
107 EventLoop loop(
"mptest-client", [](mp::LogMessage log) {
115 client = client_promise.get_future().get();
124 std::promise<std::unique_ptr<ProxyClient<messages::FooInterface>>> client_promise;
125 std::unique_ptr<ProxyClient<messages::FooInterface>> client;
138 explicit ListenSetup(std::optional<size_t> max_connections = std::nullopt)
139 : thread([this, max_connections] {
140 EventLoop loop(
"mptest-server", [](mp::LogMessage log) {
144 loop.testing_hook_disconnected = [&] {
145 Lock lock(counter_mutex);
146 ++disconnected_count;
147 counter_cv.notify_all();
149 loop.testing_hook_connected = [&] {
150 Lock lock(counter_mutex);
152 counter_cv.notify_all();
154 m_loop_ref.emplace(loop);
155 FooImplementation foo;
157 ready_promise.set_value();
161 ready_promise.get_future().get();
170 size_t ConnectedCount()
172 Lock lock(counter_mutex);
173 return connected_count;
176 size_t DisconnectedCount()
178 Lock lock(counter_mutex);
179 return disconnected_count;
182 void WaitForConnectedCount(
size_t expected_count)
184 Lock lock(counter_mutex);
185 const auto deadline = std::chrono::steady_clock::now() + FAILURE_TIMEOUT;
186 const bool matched = counter_cv.wait_until(lock.m_lock, deadline, [&]()
MP_REQUIRES(counter_mutex) {
187 return connected_count >= expected_count;
192 void WaitForDisconnectedCount(
size_t expected_count)
194 Lock lock(counter_mutex);
195 const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
196 const bool matched = counter_cv.wait_until(lock.m_lock, deadline, [&]()
MP_REQUIRES(counter_mutex) {
197 return disconnected_count >= expected_count;
202 UnixListener listener;
203 std::promise<void> ready_promise;
204 std::optional<EventLoopRef> m_loop_ref;
206 std::condition_variable counter_cv;
214KJ_TEST(
"ListenConnections accepts incoming connections")
217 KJ_EXPECT(server.ConnectedCount() == 0)
218 auto client = std::make_unique<ClientSetup>(server.listener.MakeConnectedSocket());
220 server.WaitForConnectedCount(1);
221 KJ_EXPECT(client->client->add(1, 2) == 3);
224KJ_TEST(
"ListenConnections enforces a local connection limit")
230 ListenSetup server(1);
232 KJ_EXPECT(server.ConnectedCount() == 0)
233 auto client1 = std::make_unique<ClientSetup>(server.listener.MakeConnectedSocket());
234 server.WaitForConnectedCount(1);
236 KJ_EXPECT(client1->client->add(1, 2) == 3);
238 auto client2 = std::make_unique<ClientSetup>(server.listener.MakeConnectedSocket());
242 (**server.m_loop_ref).sync([] {});
244 KJ_EXPECT(server.ConnectedCount() == 1);
245 KJ_EXPECT(server.DisconnectedCount() == 0);
247 server.WaitForDisconnectedCount(1);
248 server.WaitForConnectedCount(2);
250 KJ_EXPECT(client2->client->add(2, 3) == 5);
252 KJ_EXPECT(server.DisconnectedCount() == 1);
254 server.WaitForDisconnectedCount(2);
256 KJ_EXPECT(server.DisconnectedCount() == 2);
257 auto client3 = std::make_unique<ClientSetup>(server.listener.MakeConnectedSocket());
258 server.WaitForConnectedCount(3);
259 KJ_EXPECT(client3->client->add(3, 4) == 7);
262KJ_TEST(
"ListenConnections accepts multiple connections")
267 ListenSetup server(2);
269 KJ_EXPECT(server.ConnectedCount() == 0);
270 auto client1 = std::make_unique<ClientSetup>(server.listener.MakeConnectedSocket());
271 auto client2 = std::make_unique<ClientSetup>(server.listener.MakeConnectedSocket());
272 server.WaitForConnectedCount(2);
274 KJ_EXPECT(client1->client->add(1, 2) == 3);
275 KJ_EXPECT(client2->client->add(2, 3) == 5);
277 auto client3 = std::make_unique<ClientSetup>(server.listener.MakeConnectedSocket());
281 (**server.m_loop_ref).sync([] {});
283 KJ_EXPECT(server.ConnectedCount() == 2);
284 KJ_EXPECT(server.DisconnectedCount() == 0);
286 server.WaitForDisconnectedCount(1);
287 server.WaitForConnectedCount(3);
289 KJ_EXPECT(client3->client->add(3, 4) == 7);
KJ_TEST("Call FooInterface methods")
Functions to serialize / deserialize common bitcoin types.
void ListenConnections(EventLoop &loop, int fd, InitImpl &init, std::optional< size_t > max_connections=std::nullopt)
std::unique_ptr< ProxyClient< InitInterface > > ConnectStream(EventLoop &loop, int fd)
Log level
The severity level of this message.
std::string message
Message to be logged.
AnnotatedMixin< std::mutex > Mutex
Wrapped mutex: supports waiting but not recursive locking.