blob: 2fe7a27b1d427b488f3827af309dd34a475e24a5 [file]
// 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/stdcompat/optional.h>
#include <lib/sync/completion.h>
#include <fidl/test/coding/fuchsia/llcpp/fidl.h>
#include <zxtest/zxtest.h>
namespace {
using ::fidl_test_coding_fuchsia::Simple;
class Server : public fidl::WireServer<Simple> {
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(EchoRequestView request, EchoCompleter::Sync& completer) override {
ZX_PANIC("Never used");
}
void Close(CloseRequestView request, 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;
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;
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;
cpp17::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;
cpp17::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