blob: da3a42319df91f67c6502b25c1b7597f8feb3916 [file] [log] [blame]
// Copyright 2019 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 <dirent.h>
#include <fcntl.h>
#include <fidl/fidl.service.test/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/directory.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/fit/defer.h>
#include <lib/service/llcpp/service.h>
#include <lib/sys/component/llcpp/handlers.h>
#include <lib/sys/component/llcpp/outgoing_directory.h>
#include <lib/zx/channel.h>
#include <iostream>
#include <fbl/unique_fd.h>
#include <zxtest/zxtest.h>
namespace {
using Echo = ::fidl_service_test::Echo;
using EchoService = ::fidl_service_test::EchoService;
class EchoCommon : public fidl::WireServer<Echo> {
public:
explicit EchoCommon(const char* prefix) : prefix_(prefix) {}
zx_status_t Connect(async_dispatcher_t* dispatcher, zx::channel request) {
return fidl::BindSingleInFlightOnly(dispatcher, std::move(request), this);
}
void EchoString(EchoStringRequestView request, EchoStringCompleter::Sync& completer) override {
std::string reply = prefix_ + ": " + std::string(request->value.data(), request->value.size());
completer.Reply(fidl::StringView::FromExternal(reply));
}
private:
std::string prefix_;
};
fbl::unique_fd OpenRootDir(const zx::channel& request) {
int root_fd = 0;
zx_status_t result = fdio_fd_create(fdio_service_clone(request.get()), &root_fd);
if (result != ZX_OK) {
return {};
}
return fbl::unique_fd(root_fd);
}
fbl::unique_fd OpenSvcDir(const zx::channel& request) {
fbl::unique_fd root_fd = OpenRootDir(request);
if (!root_fd.is_valid()) {
return {};
}
return fbl::unique_fd(openat(root_fd.get(), "svc", O_RDONLY));
}
fbl::unique_fd OpenAt(const fbl::unique_fd& dirfd, const char* path, int flags) {
return fbl::unique_fd(openat(dirfd.get(), path, flags));
}
} // namespace
class ServerTest : public zxtest::Test {
protected:
ServerTest()
: loop_(&kAsyncLoopConfigNoAttachToCurrentThread),
outgoing_(component::OutgoingDirectory::Create(loop_.dispatcher())) {}
component::ServiceHandler SetUpInstance(fidl::WireServer<Echo>* foo_impl,
fidl::WireServer<Echo>* bar_impl) {
component::ServiceHandler handler;
EchoService::Handler my_service(&handler);
auto add_foo_result =
my_service.add_foo([this, foo_impl](fidl::ServerEnd<Echo> request_channel) -> void {
fidl::BindServer(loop_.dispatcher(), std::move(request_channel), foo_impl);
});
ZX_ASSERT(add_foo_result.is_ok());
auto add_bar_result =
my_service.add_bar([this, bar_impl](fidl::ServerEnd<Echo> request_channel) -> void {
fidl::BindServer(loop_.dispatcher(), std::move(request_channel), bar_impl);
});
ZX_ASSERT(add_bar_result.is_ok());
return handler;
}
void SetUp() override {
loop_.StartThread("server-test-loop");
auto result = outgoing_.AddService<EchoService>(SetUpInstance(&default_foo_, &default_bar_));
ASSERT_OK(result.status_value());
result = outgoing_.AddService<EchoService>(SetUpInstance(&other_foo_, &other_bar_), "other");
ASSERT_OK(result.status_value());
zx::channel remote;
ASSERT_OK(zx::channel::create(0, &local_root_, &remote));
result = outgoing_.Serve(std::move(remote));
ASSERT_OK(result.status_value());
}
void TearDown() override { loop_.Shutdown(); }
EchoCommon default_foo_{"default-foo"};
EchoCommon default_bar_{"default-bar"};
EchoCommon other_foo_{"other-foo"};
EchoCommon other_bar_{"other-bar"};
async::Loop loop_;
zx::channel local_root_;
component::OutgoingDirectory outgoing_;
};
TEST_F(ServerTest, ConnectsToDefaultMember) {
// Open a copy of the local namespace (channel) as a file descriptor.
fbl::unique_fd svc_fd = OpenSvcDir(local_root_);
ASSERT_TRUE(svc_fd.is_valid());
// Extract the channel from `svc_fd`.
zx_handle_t svc_local;
ASSERT_OK(fdio_get_service_handle(svc_fd.release(), &svc_local));
// Connect to the `EchoService` at the 'default' instance.
zx::status<EchoService::ServiceClient> open_result =
service::OpenServiceAt<EchoService>(zx::unowned_channel(svc_local));
ASSERT_TRUE(open_result.is_ok());
EchoService::ServiceClient service = std::move(open_result.value());
// Connect to the member 'foo'.
zx::status<fidl::ClientEnd<Echo>> connect_result = service.connect_foo();
ASSERT_TRUE(connect_result.is_ok());
fidl::WireSyncClient<Echo> client = fidl::BindSyncClient(std::move(connect_result.value()));
fidl::WireResult<Echo::EchoString> echo_result = client->EchoString(fidl::StringView("hello"));
ASSERT_TRUE(echo_result.ok());
auto response = echo_result.Unwrap();
std::string result_string(response->response.data(), response->response.size());
ASSERT_EQ(result_string, "default-foo: hello");
}
TEST_F(ServerTest, ConnectsToOtherMember) {
// Open a copy of the local namespace (channel) as a file descriptor.
fbl::unique_fd svc_fd = OpenSvcDir(local_root_);
ASSERT_TRUE(svc_fd.is_valid());
// Extract the channel from `svc_fd`.
zx_handle_t svc_local;
ASSERT_OK(fdio_get_service_handle(svc_fd.release(), &svc_local));
// Connect to the `EchoService` at the 'default' instance.
zx::status<EchoService::ServiceClient> open_result =
service::OpenServiceAt<EchoService>(zx::unowned_channel(svc_local), "other");
ASSERT_TRUE(open_result.is_ok());
EchoService::ServiceClient service = std::move(open_result.value());
// Connect to the member 'foo'.
zx::status<fidl::ClientEnd<Echo>> connect_result = service.connect_foo();
ASSERT_TRUE(connect_result.is_ok());
fidl::WireSyncClient<Echo> client = fidl::BindSyncClient(std::move(connect_result.value()));
fidl::WireResult<Echo::EchoString> echo_result = client->EchoString(fidl::StringView("hello"));
ASSERT_TRUE(echo_result.ok());
auto response = echo_result.Unwrap();
std::string result_string(response->response.data(), response->response.size());
ASSERT_EQ(result_string, "other-foo: hello");
}
TEST_F(ServerTest, ListsMembers) {
// Open a copy of the local namespace (channel) as a file descriptor.
fbl::unique_fd svc_fd = OpenSvcDir(local_root_);
ASSERT_TRUE(svc_fd.is_valid());
// Open the 'default' instance of the test service.
fbl::unique_fd instance_fd = OpenAt(svc_fd, "fidl.service.test.EchoService/default", O_RDONLY);
ASSERT_TRUE(instance_fd.is_valid());
// fdopendir takes ownership of `instance_fd`.
DIR* dir = fdopendir(instance_fd.release());
ASSERT_NE(dir, nullptr);
auto defer_closedir = fit::defer([dir] { closedir(dir); });
dirent* entry = readdir(dir);
ASSERT_NE(entry, nullptr);
ASSERT_EQ(std::string(entry->d_name), ".");
entry = readdir(dir);
ASSERT_NE(entry, nullptr);
ASSERT_EQ(std::string(entry->d_name), "bar");
entry = readdir(dir);
ASSERT_NE(entry, nullptr);
ASSERT_EQ(std::string(entry->d_name), "foo");
ASSERT_EQ(readdir(dir), nullptr);
}