Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion example/example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <fstream>
#include <future>
#include <iostream>
#include <kj/async.h>
#include <kj/common.h>
#include <memory>
#include <mp/proxy.h>
Expand Down
32 changes: 26 additions & 6 deletions include/mp/proxy-io.h
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,28 @@ ProxyClientBase<Interface, Impl>::ProxyClientBase(typename Interface::Client cli
});
}
});
Sub::construct(*this);
// If construct() fails, run the cleanup functions before rethrowing,
// because ~ProxyClientBase will not run for an object whose constructor
// threw, and the connection would otherwise be leaked.
try {
Sub::construct(*this);
} catch (...) {
MP_LOG(*m_context.loop, Log::Debug) << "Cleaning up " << CxxTypeName(*this) << " " << this << " after construct() failure";
CleanupRun(m_context.cleanup_fns);
throw;
}

// If this client owns the connection, delete the connection on disconnect.
if (destroy_connection) {
m_context.loop->sync([&] {
EventLoop& loop = *m_context.loop;
Connection* connection = m_context.connection;
connection->onDisconnect([&loop, connection] {
MP_LOG(loop, Log::Warning) << "IPC client: unexpected network disconnect.";
delete connection;
});
});
}
}

template <typename Interface, typename Impl>
Expand Down Expand Up @@ -832,6 +853,10 @@ kj::Promise<T> ProxyServer<Thread>::post(Fn&& fn)
//! Given a stream, make a new ProxyClient object to send requests over it.
//! Also create a new Connection object embedded in the client that is freed
//! when the client is closed.
//!
//! If the init interface declares a construct() method, creating the client
//! calls it, so this function may block making an IPC call and may throw if
//! the call fails.
template <typename InitInterface>
std::unique_ptr<ProxyClient<InitInterface>> ConnectStream(EventLoop& loop, Stream stream)
{
Expand All @@ -840,11 +865,6 @@ std::unique_ptr<ProxyClient<InitInterface>> ConnectStream(EventLoop& loop, Strea
loop.sync([&] {
connection = std::make_unique<Connection>(loop, kj::mv(stream));
init_client = connection->m_rpc_system->bootstrap(ServerVatId().vat_id).castAs<InitInterface>();
Connection* connection_ptr = connection.get();
connection->onDisconnect([&loop, connection_ptr] {
MP_LOG(loop, Log::Warning) << "IPC client: unexpected network disconnect.";
delete connection_ptr;
});
});
return std::make_unique<ProxyClient<InitInterface>>(
kj::mv(init_client), connection.release(), /* destroy_connection= */ true);
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ if(BUILD_TESTING AND TARGET CapnProto::kj-test)
mp/test/foo.h
mp/test/listen_tests.cpp
mp/test/spawn_tests.cpp
mp/test/connect_tests.cpp
mp/test/test.cpp
)
include(${PROJECT_SOURCE_DIR}/cmake/TargetCapnpSources.cmake)
Expand Down
239 changes: 239 additions & 0 deletions test/mp/test/connect_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
// Copyright (c) The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "unixlistener.h"
#include <kj/async.h>
#include <kj/common.h>
#include <kj/debug.h>
#include <kj/memory.h>
#include <kj/test.h>
#include <mp/proxy.h>
#include <mp/proxy-io.h>
#include <mp/test/foo.capnp.h>
#include <mp/test/foo.capnp.proxy.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <chrono>
#include <condition_variable>
#include <cstring> // IWYU pragma: keep
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <thread>

namespace mp {
namespace test {
namespace {

constexpr auto FAILURE_TIMEOUT = std::chrono::seconds{30};

//! Default event loop log handler used by tests, throws so the calling code
//! can assert on errors.
void DefaultLogHandler(mp::LogMessage log)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Add test coverage for ConnectStream" (44d1914)

This seems to be slightly different than the DefaultLogHandler defined in listen_tests.cpp. Would be nice to define a shared on, maybe in a test.h file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice suggestion! I moved this logger to a common.h file instead at ae5c6bc (follow-up PR).

{
if (log.level == mp::Log::Raise)
throw std::runtime_error(log.message);
}

class TestSetup
{
public:
int client_fd;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Add test coverage for ConnectStream" (44d1914)

All these class members should have m_ prefixes so it is clear when they are accessed from methods that they are class members and not local variables. Lack of prefixes also makes code confusing below because there are two different variables called loop

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed this in 70467c5 (follow-up PR).

int server_fd;

mp::EventLoop* loop;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Add test coverage for ConnectStream" (44d1914)

Would probably drop mp:: prefix throughout this file since tests are in the mp namespace.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, addressed at 5790bec (follow-up PR).

std::optional<mp::EventLoopRef> loop_ref;
//! Thread variable should be after other struct members so the thread does
//! not start until the other members are initialized.
std::thread loop_thread;

TestSetup(mp::LogFn log_handler = DefaultLogHandler)
: TestSetup(
[](int fds[2]) {
KJ_REQUIRE(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != -1);
},
log_handler) {}

TestSetup(const std::function<void(int[2])>& init_sockets,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Add test coverage for ConnectStream" (44d1914)

Having the init_sockets callback and the client_fd and server_fd members seems unnecessarily complicated given that nothing else in the test setup class uses them. Would seem simpler to drop these

diff

--- a/test/mp/test/connect_tests.cpp
+++ b/test/mp/test/connect_tests.cpp
@@ -9,16 +9,15 @@
 #include <kj/test.h>
 #include <mp/proxy.h>
 #include <mp/proxy-io.h>
+#include <mp/util.h>
 #include <mp/test/foo.capnp.h>
 #include <mp/test/foo.capnp.proxy.h>
 #include <sys/socket.h>
-#include <sys/types.h>
 #include <unistd.h>
 
 #include <chrono>
 #include <condition_variable>
 #include <cstring> // IWYU pragma: keep
-#include <functional>
 #include <future>
 #include <memory>
 #include <mutex>
@@ -45,9 +44,6 @@ void DefaultLogHandler(mp::LogMessage log)
 class TestSetup
 {
 public:
-    int client_fd;
-    int server_fd;
-
     mp::EventLoop* loop;
     std::optional<mp::EventLoopRef> loop_ref;
     //! Thread variable should be after other struct members so the thread does
@@ -55,14 +51,6 @@ public:
     std::thread loop_thread;
 
     TestSetup(mp::LogFn log_handler = DefaultLogHandler)
-        : TestSetup(
-              [](int fds[2]) {
-                  KJ_REQUIRE(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != -1);
-              },
-              log_handler) {}
-
-    TestSetup(const std::function<void(int[2])>& init_sockets,
-              mp::LogFn log_handler = DefaultLogHandler)
     {
         std::promise<mp::EventLoop*> loop_promise;
         loop_thread = std::thread([&, log_handler] {
@@ -72,13 +60,6 @@ public:
         });
         loop = loop_promise.get_future().get();
         loop_ref.emplace(*loop);
-
-        // Initialize and store sockets
-        int fds[2] = {-1, -1};
-        init_sockets(fds);
-
-        client_fd = fds[0];
-        server_fd = fds[1];
     }
 
     ~TestSetup()
@@ -91,15 +72,16 @@ public:
 KJ_TEST("ConnectStream connects to a socket serving a valid init interface")
 {
     TestSetup setup;
+    auto [client_fd, server_fd] = SocketPair();
 
-    std::thread server_thread([&setup]() {
+    std::thread server_thread([&]() {
         mp::EventLoop server_loop("mptest-valid-server", DefaultLogHandler);
         std::unique_ptr<FooInit> init = std::make_unique<FooInit>();
-        ServeStream<messages::FooInit>(server_loop, MakeStream(server_loop, setup.server_fd), *init);
+        ServeStream<messages::FooInit>(server_loop, MakeStream(server_loop, server_fd), *init);
         server_loop.loop();
     });
 
-    auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));
+    auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, client_fd));
 
     init.reset();
     server_thread.join();
@@ -109,11 +91,12 @@ KJ_TEST("ConnectStream connects to a socket serving a valid init interface")
 KJ_TEST("ConnectStream throws when the socket is already disconnected")
 {
     TestSetup setup;
+    auto [client_fd, server_fd] = SocketPair();
 
-    close(setup.server_fd);
+    close(server_fd);
 
     try {
-        auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));
+        auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, client_fd));
 
         KJ_EXPECT(false);
     } catch (const std::runtime_error& e) {
@@ -126,12 +109,13 @@ KJ_TEST("ConnectStream throws when the socket is already disconnected")
 KJ_TEST("ConnectStream defers disconnect failure to the first IPC request for interfaces without construct()")
 {
     TestSetup setup;
+    auto [client_fd, server_fd] = SocketPair();
 
-    close(setup.server_fd);
+    close(server_fd);
 
     // Without a construct() method no IPC call is made during client
     // creation, so ConnectStream succeeds even though the peer is gone.
-    auto foo = ConnectStream<messages::FooInterface>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));
+    auto foo = ConnectStream<messages::FooInterface>(*setup.loop, MakeStream(*setup.loop, client_fd));
 
     try {
         foo->add(1, 2);
@@ -157,10 +141,11 @@ KJ_TEST("ConnectStream handles a disconnect when no client calls are made")
         }
         DefaultLogHandler(log);
     });
+    auto [client_fd, server_fd] = SocketPair();
 
-    close(setup.server_fd);
+    close(server_fd);
 
-    auto foo = ConnectStream<messages::FooInterface>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));
+    auto foo = ConnectStream<messages::FooInterface>(*setup.loop, MakeStream(*setup.loop, client_fd));
 
     // The disconnect handler registered by ProxyClientBase should run and
     // delete the connection even when no calls are ever made.
@@ -171,20 +156,21 @@ KJ_TEST("ConnectStream handles a disconnect when no client calls are made")
 KJ_TEST("ConnectStream throws when the socket disconnects after receiving data")
 {
     TestSetup setup;
+    auto [client_fd, server_fd] = SocketPair();
 
-    std::thread server_thread([&setup]() {
+    std::thread server_thread([&]() {
         char buf[128];
 
         ssize_t bytes_received =
-            recv(setup.server_fd, buf, sizeof(buf), 0);
+            recv(server_fd, buf, sizeof(buf), 0);
 
         if (bytes_received > 0) {
-            close(setup.server_fd);
+            close(server_fd);
         }
     });
 
     try {
-        auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));
+        auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, client_fd));
 
         if (server_thread.joinable()) server_thread.join();
         KJ_EXPECT(false);
@@ -199,16 +185,14 @@ KJ_TEST("ConnectStream throws when the socket disconnects after receiving data")
 KJ_TEST("ConnectStream throws when a connection accepted from a listener disconnects after receiving data")
 {
     UnixListener listener;
+    TestSetup setup;
+    int client_fd = listener.MakeConnectedSocket();
+    int server_fd = listener.release();
 
-    TestSetup setup([&listener](int fds[2]) {
-        fds[0] = listener.MakeConnectedSocket(); // client_fd
-        fds[1] = listener.release();             // server_fd
-    });
-
-    std::thread server_thread([&setup]() {
+    std::thread server_thread([&]() {
         char buf[128];
 
-        int connection_fd = accept(setup.server_fd, nullptr, nullptr);
+        int connection_fd = accept(server_fd, nullptr, nullptr);
 
         if (connection_fd >= 0) {
             ssize_t bytes_received =
@@ -218,11 +202,11 @@ KJ_TEST("ConnectStream throws when a connection accepted from a listener disconn
                 close(connection_fd);
             }
         }
-        close(setup.server_fd);
+        close(server_fd);
     });
 
     try {
-        auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));
+        auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, client_fd));
 
         if (server_thread.joinable()) server_thread.join();
         KJ_EXPECT(false);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you're right, having them was indeed unnecessary. Addressed this at 466afd0 (follow-up PR).

Thanks for the diff!

mp::LogFn log_handler = DefaultLogHandler)
{
std::promise<mp::EventLoop*> loop_promise;
loop_thread = std::thread([&, log_handler] {
mp::EventLoop loop("mptest-connect", log_handler);
loop_promise.set_value(&loop);
loop.loop();
});
loop = loop_promise.get_future().get();
loop_ref.emplace(*loop);

// Initialize and store sockets
int fds[2] = {-1, -1};
init_sockets(fds);

client_fd = fds[0];
server_fd = fds[1];
}

~TestSetup()
{
loop_ref.reset();
loop_thread.join();
}
};

KJ_TEST("ConnectStream connects to a socket serving a valid init interface")
{
TestSetup setup;

std::thread server_thread([&setup]() {
mp::EventLoop server_loop("mptest-valid-server", DefaultLogHandler);
std::unique_ptr<FooInit> init = std::make_unique<FooInit>();
ServeStream<messages::FooInit>(server_loop, MakeStream(server_loop, setup.server_fd), *init);
server_loop.loop();
});

auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Add test coverage for ConnectStream" (44d1914)

Might be good to note in a comment that FooInit capnproto interface has a construct method, so this is not just connecting to the IPC server, but also testing that the construct IPC request completes successfully.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, done at 76ab72f (follow-up PR).


init.reset();
server_thread.join();
KJ_EXPECT(true);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Add test coverage for ConnectStream" (44d1914)

Expecting true here seems unnecessary.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped at 8d30ea8 (follow-up PR).

}

KJ_TEST("ConnectStream throws when the socket is already disconnected")
{
TestSetup setup;

close(setup.server_fd);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Fix error handling when creating clients" (bb47369)

Would be good to use KJ_SYSCALL here and below to check close return value

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done at 2977072 (follow-up PR).


try {
auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));

KJ_EXPECT(false);
} catch (const std::runtime_error& e) {
std::string_view reason = e.what();

KJ_EXPECT(reason == "IPC client method call interrupted by disconnect.");
}
}

KJ_TEST("ConnectStream defers disconnect failure to the first IPC request for interfaces without construct()")
{
TestSetup setup;

close(setup.server_fd);

// Without a construct() method no IPC call is made during client
// creation, so ConnectStream succeeds even though the peer is gone.
auto foo = ConnectStream<messages::FooInterface>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));

try {
foo->add(1, 2);
KJ_EXPECT(false);
} catch (const std::runtime_error& e) {
std::string_view reason = e.what();

KJ_EXPECT(reason == "IPC client method called after disconnect.");
}
}

KJ_TEST("ConnectStream handles a disconnect when no client calls are made")
{
std::mutex mutex;
std::condition_variable cv;
bool warned = false;

TestSetup setup([&](mp::LogMessage log) {
if (log.level == mp::Log::Warning && log.message.find("unexpected network disconnect") != std::string::npos) {
const std::lock_guard<std::mutex> lock(mutex);
warned = true;
cv.notify_all();
}
DefaultLogHandler(log);
});

close(setup.server_fd);

auto foo = ConnectStream<messages::FooInterface>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));

// The disconnect handler registered by ProxyClientBase should run and
// delete the connection even when no calls are ever made.
std::unique_lock<std::mutex> lock(mutex);
KJ_EXPECT(cv.wait_for(lock, FAILURE_TIMEOUT, [&] { return warned; }));
Comment thread
xyzconstant marked this conversation as resolved.
}

KJ_TEST("ConnectStream throws when the socket disconnects after receiving data")
{
TestSetup setup;

std::thread server_thread([&setup]() {
char buf[128];

ssize_t bytes_received =
recv(setup.server_fd, buf, sizeof(buf), 0);

if (bytes_received > 0) {
close(setup.server_fd);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Add test coverage for ConnectStream" (44d1914)

I don't understand the reason for only closing the descriptor if bytes or received (and leaking otherwise)? Would make more sense to close it unconditionally. Same applies to test below. If there is a reason for this conditional it would be good to explain in a comment

@xyzconstant xyzconstant Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. Honestly, when I wrote this I thought recv() might somewhat "read 0 bytes", but I see now that I had it wrong. There's no reason to have this conditional, so dropped it at 2977072 (follow-up PR).

}
});

try {
auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));

if (server_thread.joinable()) server_thread.join();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Add test coverage for ConnectStream" (44d1914)

I don't think it makes sense to call joinable here and to repeat this same line. It would make more sense to simply call server_thread.join() unconditionally at the end of this function after the try/catch.

Same comment also applies to test below

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch. Addressed at 24f17bd (follow-up PR).

KJ_EXPECT(false);
} catch (const std::runtime_error& e) {
if (server_thread.joinable()) server_thread.join();

std::string_view reason = e.what();
KJ_EXPECT(reason == "IPC client method call interrupted by disconnect.");
}
}

KJ_TEST("ConnectStream throws when a connection accepted from a listener disconnects after receiving data")
{
UnixListener listener;

TestSetup setup([&listener](int fds[2]) {
fds[0] = listener.MakeConnectedSocket(); // client_fd
fds[1] = listener.release(); // server_fd
});

std::thread server_thread([&setup]() {
char buf[128];

int connection_fd = accept(setup.server_fd, nullptr, nullptr);

if (connection_fd >= 0) {
ssize_t bytes_received =
recv(connection_fd, buf, sizeof(buf), 0);

if (bytes_received > 0) {
close(connection_fd);
}
}
close(setup.server_fd);
});

try {
auto init = ConnectStream<messages::FooInit>(*setup.loop, MakeStream(*setup.loop, setup.client_fd));

if (server_thread.joinable()) server_thread.join();
KJ_EXPECT(false);
} catch (const std::runtime_error& e) {
if (server_thread.joinable()) server_thread.join();

std::string_view reason = e.what();
KJ_EXPECT(reason == "IPC client method call interrupted by disconnect.");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Fix error handling when creating clients" (bb47369)

It would be helpful if commit message noted the reason existing tests in this commit are changing, that because the onDisconnect call now happens later, client code might detect disconnects happening during IPC calls instead of before them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, couldn't reword the commit in time. Thanks for the feedback, though.

}
}

} // namespace
} // namespace test
} // namespace mp
4 changes: 4 additions & 0 deletions test/mp/test/foo.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ interface FooInterface $Proxy.wrap("mp::test::FooImplementation") {
passDataPointers @22 (arg :List(Data)) -> (result :List(Data));
}

interface FooInit $Proxy.wrap("mp::test::FooInit") {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit "Add test coverage for ConnectStream" (31c1ac2)

Am curious why this new FooInit interface is needed and existing Foo interface isn't used.

It also seems like a potentially complicating factor that could make the tests harder to debug & understand for this to have a construct method. I wonder if it could be dropped or at least the Thread map parameters could be dropped since it doesn't look like anything in these tests requires threadmaps

EDIT: Oh, I see in next commit it looks like there are new tests that rely on the construct call failing. I think it would be to only use the FooInit type for the tests which actually need the construct method, and use FooInterface for other tests. Also would be good to drop ThreadMap parameters as I believe they should not be needed.

@xyzconstant xyzconstant Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice suggestion!

I addressed it in 44d1914 by removing the ThreadMap parameters.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the FooInit/FooInterface split, the tests already follow this. Only the 4 tests that require the construct() call use FooInit.

Also, I've replaced the initThreadMap call with a simple add(1, 2) in the "ConnectStream defers disconnect failure to the first IPC request for interfaces without construct()" test case.

construct @0 () -> ();
}

interface FooCallback $Proxy.wrap("mp::test::FooCallback") {
destroy @0 (context :Proxy.Context) -> ();
call @1 (context :Proxy.Context, arg :Int32) -> (result :Int32);
Expand Down
4 changes: 4 additions & 0 deletions test/mp/test/foo.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ class ExtendedCallback : public FooCallback
virtual int callExtended(int arg) = 0;
};

class FooInit
{
};

class FooImplementation
{
public:
Expand Down
Loading
Loading