blob: f09393119327770bcf97a889ce60a079c956a993 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl/llcpp/server.h>
#include <lib/sync/completion.h>
#include <fidl/test/coding/fuchsia/llcpp/fidl.h>
#include <zxtest/zxtest.h>
namespace {
using ::llcpp::fidl::test::coding::fuchsia::Simple;
class Server : public Simple::Interface {
public:
explicit Server(sync_completion_t* destroyed) : destroyed_(destroyed) {}
Server(Server&& other) = delete;
Server(const Server& other) = delete;
Server& operator=(Server&& other) = delete;
Server& operator=(const Server& other) = delete;
~Server() override { sync_completion_signal(destroyed_); }
void Echo(int32_t request, EchoCompleter::Sync& completer) override { ZX_PANIC("Never used"); }
void Close(CloseCompleter::Sync& completer) override { ZX_PANIC("Never used"); }
private:
sync_completion_t* destroyed_;
};
class BindServerOverloads : public zxtest::Test {
public:
BindServerOverloads()
: loop_(&kAsyncLoopConfigNoAttachToCurrentThread),
endpoints_(fidl::CreateEndpoints<Simple>()) {}
void SetUp() override {
ASSERT_OK(loop_.StartThread());
ASSERT_OK(endpoints_.status_value());
}
zx::status<fidl::Endpoints<Simple>>& endpoints() { return endpoints_; }
async_dispatcher_t* dispatcher() { return loop_.dispatcher(); }
async::Loop& loop() { return loop_; }
private:
async::Loop loop_;
zx::status<fidl::Endpoints<Simple>> endpoints_;
};
// Test that |BindServer| correctly destroys a server it uniquely owns.
TEST_F(BindServerOverloads, UniquePtrWithoutUnboundHook) {
sync_completion_t destroyed;
sync_completion_t unbound;
auto result = fidl::BindServer(dispatcher(), std::move(endpoints()->server),
std::make_unique<Server>(&destroyed));
// Trigger binding destruction before loop's destruction.
endpoints()->client.reset();
ASSERT_OK(sync_completion_wait(&destroyed, ZX_TIME_INFINITE));
}
// Test that |BindServer| correctly destroys a server it uniquely owns,
// and that the |on_unbound| hook is executed before server destruction.
TEST_F(BindServerOverloads, UniquePtrWithUnboundHook) {
sync_completion_t destroyed;
sync_completion_t unbound;
auto result = fidl::BindServer(
dispatcher(), std::move(endpoints()->server), std::make_unique<Server>(&destroyed),
[&unbound, &destroyed](Server*, fidl::UnbindInfo info, fidl::ServerEnd<Simple> server_end) {
// Server is held alive by the runtime until we leave this lambda.
ASSERT_EQ(ZX_ERR_TIMED_OUT, sync_completion_wait(&destroyed, ZX_TIME_INFINITE_PAST));
sync_completion_signal(&unbound);
});
// Trigger binding destruction before loop's destruction.
endpoints()->client.reset();
ASSERT_OK(sync_completion_wait(&unbound, ZX_TIME_INFINITE));
ASSERT_OK(sync_completion_wait(&destroyed, ZX_TIME_INFINITE));
}
// Test that |BindServer| correctly destroys a server it uniquely owns
// via a |shared_ptr|.
TEST_F(BindServerOverloads, SharedPtrWithoutUnboundHook) {
sync_completion_t destroyed;
sync_completion_t unbound;
auto result = fidl::BindServer(dispatcher(), std::move(endpoints()->server),
std::make_shared<Server>(&destroyed));
// Trigger binding destruction before loop's destruction.
endpoints()->client.reset();
ASSERT_OK(sync_completion_wait(&destroyed, ZX_TIME_INFINITE));
}
// Test that |BindServer| correctly destroys a server it uniquely owns
// via a |shared_ptr|, and that the |on_unbound| hook is executed before
// server destruction.
TEST_F(BindServerOverloads, SharedPtrWithUnboundHook) {
sync_completion_t destroyed;
sync_completion_t unbound;
auto result = fidl::BindServer(
dispatcher(), std::move(endpoints()->server), std::make_shared<Server>(&destroyed),
[&unbound, &destroyed](Server*, fidl::UnbindInfo info, fidl::ServerEnd<Simple> server_end) {
// Server is held alive by the runtime until we leave this lambda.
ASSERT_EQ(ZX_ERR_TIMED_OUT, sync_completion_wait(&destroyed, ZX_TIME_INFINITE_PAST));
sync_completion_signal(&unbound);
});
// Trigger binding destruction before loop's destruction.
endpoints()->client.reset();
ASSERT_OK(sync_completion_wait(&unbound, ZX_TIME_INFINITE));
ASSERT_OK(sync_completion_wait(&destroyed, ZX_TIME_INFINITE));
}
// Test that |BindServer| does not prematurely destroy a server managed by
// |shared_ptr| when there are still outstanding references.
TEST_F(BindServerOverloads, SharedPtrWithUnboundHookAndSharedOwnership) {
sync_completion_t destroyed;
sync_completion_t unbound;
auto shared_server = std::make_shared<Server>(&destroyed);
auto result = fidl::BindServer(
dispatcher(), std::move(endpoints()->server), shared_server,
[&unbound, &destroyed](Server*, fidl::UnbindInfo info, fidl::ServerEnd<Simple> server_end) {
// Server is held alive by the runtime until we leave this lambda.
ASSERT_EQ(ZX_ERR_TIMED_OUT, sync_completion_wait(&destroyed, ZX_TIME_INFINITE_PAST));
sync_completion_signal(&unbound);
});
// Trigger binding destruction before loop's destruction.
endpoints()->client.reset();
ASSERT_OK(sync_completion_wait(&unbound, ZX_TIME_INFINITE));
ASSERT_EQ(ZX_ERR_TIMED_OUT, sync_completion_wait(&destroyed, ZX_TIME_INFINITE_PAST));
shared_server.reset();
ASSERT_OK(sync_completion_wait(&destroyed, ZX_TIME_INFINITE));
}
// Test borrowing a server implementation with a raw pointer.
TEST_F(BindServerOverloads, RawPtrWithoutUnboundHook) {
sync_completion_t destroyed;
sync_completion_t unbound;
fit::optional<Server> server{&destroyed};
auto result = fidl::BindServer(dispatcher(), std::move(endpoints()->server), &server.value());
// Trigger binding destruction before loop's destruction.
endpoints()->client.reset();
ASSERT_EQ(ZX_ERR_TIMED_OUT, sync_completion_wait(&destroyed, ZX_TIME_INFINITE_PAST));
loop().Shutdown();
ASSERT_EQ(ZX_ERR_TIMED_OUT, sync_completion_wait(&destroyed, ZX_TIME_INFINITE_PAST));
// Now it's safe to destroy |server|.
server.reset();
ASSERT_OK(sync_completion_wait(&destroyed, ZX_TIME_INFINITE));
}
// Test borrowing a server implementation with a raw pointer, and supplying
// an |on_unbound| hook.
TEST_F(BindServerOverloads, RawPtrWithUnboundHook) {
sync_completion_t destroyed;
sync_completion_t unbound;
fit::optional<Server> server{&destroyed};
auto result = fidl::BindServer(
dispatcher(), std::move(endpoints()->server), &server.value(),
[&unbound, &destroyed](Server*, fidl::UnbindInfo info, fidl::ServerEnd<Simple> server_end) {
// Server is held alive by the local variable.
ASSERT_EQ(ZX_ERR_TIMED_OUT, sync_completion_wait(&destroyed, ZX_TIME_INFINITE_PAST));
sync_completion_signal(&unbound);
});
// Trigger binding destruction before loop's destruction.
endpoints()->client.reset();
ASSERT_OK(sync_completion_wait(&unbound, ZX_TIME_INFINITE));
ASSERT_EQ(ZX_ERR_TIMED_OUT, sync_completion_wait(&destroyed, ZX_TIME_INFINITE_PAST));
loop().Shutdown();
ASSERT_EQ(ZX_ERR_TIMED_OUT, sync_completion_wait(&destroyed, ZX_TIME_INFINITE_PAST));
// Now it's safe to destroy |server|.
server.reset();
ASSERT_OK(sync_completion_wait(&destroyed, ZX_TIME_INFINITE));
}
} // namespace