blob: 8fae39df49e9802461b34844fcc3907f7c7f34fe [file] [log] [blame]
// Copyright 2022 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 <errno.h>
#include <fcntl.h>
#include <fidl/fuchsia.examples/cpp/fidl.h>
#include <fidl/fuchsia.io/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/namespace.h>
#include <lib/fidl/llcpp/client.h>
#include <lib/service/llcpp/service.h>
#include <lib/sys/component/llcpp/constants.h>
#include <lib/sys/component/llcpp/outgoing_directory.h>
#include <lib/zx/channel.h>
#include <lib/zx/handle.h>
#include <unistd.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <algorithm>
#include <array>
#include <memory>
#include <thread>
#include <utility>
#include <fbl/unique_fd.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <src/lib/storage/vfs/cpp/managed_vfs.h>
#include <src/lib/storage/vfs/cpp/pseudo_dir.h>
#include <src/lib/storage/vfs/cpp/pseudo_file.h>
#include <src/lib/testing/loop_fixture/real_loop_fixture.h>
namespace {
// Expected path of directory hosting FIDL Services & Protocols.
constexpr char kSvcDirectoryPath[] = "svc";
constexpr char kTestString[] = "FizzBuzz";
constexpr char kTestStringReversed[] = "zzuBzziF";
class EchoImpl final : public fidl::WireServer<fuchsia_examples::Echo> {
public:
explicit EchoImpl(bool reversed) : reversed_(reversed) {}
void SendString(SendStringRequestView request, SendStringCompleter::Sync& completer) override {}
void EchoString(EchoStringRequestView request, EchoStringCompleter::Sync& completer) override {
std::string value(request->value.get());
if (reversed_) {
std::reverse(value.begin(), value.end());
}
auto reply = fidl::StringView::FromExternal(value);
completer.Reply(reply);
}
private:
bool reversed_ = false;
};
class NaturalEchoImpl final : public fidl::Server<fuchsia_examples::Echo> {
public:
explicit NaturalEchoImpl(bool reversed) : reversed_(reversed) {}
void SendString(SendStringRequest& request, SendStringCompleter::Sync& completer) override {}
void EchoString(EchoStringRequest& request, EchoStringCompleter::Sync& completer) override {
std::string value(request.value());
if (reversed_) {
std::reverse(value.begin(), value.end());
}
completer.Reply(value);
}
private:
bool reversed_ = false;
};
class OutgoingDirectoryTest : public gtest::RealLoopFixture {
public:
void SetUp() override {
outgoing_directory_ = std::make_unique<component::OutgoingDirectory>(
component::OutgoingDirectory::Create(dispatcher()));
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
ZX_ASSERT(outgoing_directory_->Serve(std::move(endpoints->server)).is_ok());
client_end_ = std::move(endpoints->client);
}
component::OutgoingDirectory* GetOutgoingDirectory() { return outgoing_directory_.get(); }
fidl::ClientEnd<fuchsia_io::Directory> TakeSvcClientEnd(
fidl::ClientEnd<fuchsia_io::Directory> root, fidl::StringView path = kSvcDirectoryPath) {
zx::channel server_end, client_end;
ZX_ASSERT(ZX_OK == zx::channel::create(0, &server_end, &client_end));
// Check if this has already be initialized.
if (!svc_client_.is_valid()) {
svc_client_ = fidl::WireClient<fuchsia_io::Directory>(std::move(root), dispatcher());
}
auto status = svc_client_->Open(
fuchsia_io::wire::OpenFlags::kRightWritable | fuchsia_io::wire::OpenFlags::kRightReadable,
fuchsia_io::wire::kModeTypeDirectory, path,
fidl::ServerEnd<fuchsia_io::Node>(std::move(server_end)));
ZX_ASSERT_MSG(status.ok(), "Failed to open /%s client: %s", path.data(),
status.status_string());
return fidl::ClientEnd<fuchsia_io::Directory>(std::move(client_end));
}
fidl::ClientEnd<fuchsia_io::Directory> TakeRootClientEnd() { return std::move(client_end_); }
protected:
void InstallServiceHandler(fuchsia_examples::EchoService::Handler& service_handler,
EchoImpl* impl, bool reversed) {
auto handler = [dispatcher = dispatcher(),
impl = impl](fidl::ServerEnd<fuchsia_examples::Echo> request) -> void {
// This is invoked during handler unbound. We're not testing for that
// here so we just provide a no-op callback.
auto _on_unbound = [](EchoImpl* impl, fidl::UnbindInfo info,
fidl::ServerEnd<fuchsia_examples::Echo> server_end) {};
fidl::BindServer(dispatcher, std::move(request), impl, std::move(_on_unbound));
};
auto result = reversed ? service_handler.add_reversed_echo(std::move(handler))
: service_handler.add_regular_echo(std::move(handler));
ZX_ASSERT(result.is_ok());
}
fidl::WireClient<fuchsia_examples::Echo> ConnectToServiceMember(
fuchsia_examples::EchoService::ServiceClient& service, bool reversed) {
auto connect_result =
reversed ? service.connect_reversed_echo() : service.connect_regular_echo();
ZX_ASSERT(connect_result.is_ok());
return fidl::WireClient<fuchsia_examples::Echo>(std::move(connect_result.value()),
dispatcher());
}
// Service handler that is pre-populated. This is only used for tests that
// want to test failure paths.
component::ServiceHandler CreateNonEmptyServiceHandler() {
// Setup service handler.
component::ServiceHandler service_handler;
fuchsia_examples::EchoService::Handler echo_service_handler(&service_handler);
// First, install the regular Echo server in this service handler.
EchoImpl regular_impl(/*reversed=*/false);
InstallServiceHandler(echo_service_handler, &regular_impl, /*reversed=*/false);
return service_handler;
}
private:
std::unique_ptr<component::OutgoingDirectory> outgoing_directory_ = nullptr;
fidl::ClientEnd<fuchsia_io::Directory> client_end_;
fidl::WireClient<fuchsia_io::Directory> svc_client_;
};
TEST_F(OutgoingDirectoryTest, AddProtocolWireServer) {
// Setup fuchsia.examples.Echo server.
EchoImpl impl(/*reversed=*/false);
ASSERT_EQ(GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&impl).status_value(),
ZX_OK);
// Setup fuchsia.examples.Echo client.
auto client_end =
service::ConnectAt<fuchsia_examples::Echo>(TakeSvcClientEnd(TakeRootClientEnd()));
ASSERT_EQ(client_end.status_value(), ZX_OK);
fidl::WireClient<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher());
std::string reply_received;
client->EchoString(kTestString)
.ThenExactlyOnce([&reply_received, quit_loop = QuitLoopClosure()](
fidl::WireUnownedResult<fuchsia_examples::Echo::EchoString>& result) {
ASSERT_TRUE(result.ok()) << "EchoString failed: " << result.error();
auto* response = result.Unwrap();
reply_received = std::string(response->response.data(), response->response.size());
quit_loop();
});
RunLoop();
EXPECT_EQ(reply_received, kTestString);
}
TEST_F(OutgoingDirectoryTest, AddProtocolNaturalServer) {
// Setup fuchsia.examples.Echo server.
NaturalEchoImpl impl(/*reversed=*/false);
ASSERT_EQ(GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&impl).status_value(),
ZX_OK);
// Setup fuchsia.examples.Echo client.
auto client_end =
service::ConnectAt<fuchsia_examples::Echo>(TakeSvcClientEnd(TakeRootClientEnd()));
ASSERT_EQ(client_end.status_value(), ZX_OK);
fidl::Client<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher());
std::string reply_received;
client->EchoString({kTestString})
.ThenExactlyOnce([&reply_received, quit_loop = QuitLoopClosure()](
fidl::Result<fuchsia_examples::Echo::EchoString>& result) {
ASSERT_TRUE(result.is_ok()) << "EchoString failed: " << result.error_value();
reply_received = result->response();
quit_loop();
});
RunLoop();
EXPECT_EQ(reply_received, kTestString);
}
// Test that outgoing directory is able to serve multiple service members. In
// this case, the directory will host the `fuchsia.examples.EchoService` which
// contains two `fuchsia.examples.Echo` member. One regular, and one reversed.
TEST_F(OutgoingDirectoryTest, AddServiceServesAllMembers) {
// Setup service handler.
component::ServiceHandler service_handler;
fuchsia_examples::EchoService::Handler echo_service_handler(&service_handler);
// First, install the regular Echo server in this service handler.
EchoImpl regular_impl(/*reversed=*/false);
InstallServiceHandler(echo_service_handler, &regular_impl, /*reversed=*/false);
// Then, install the reverse Echo server. This instance will reverse the string
// received in calls to EchoString.
EchoImpl reversed_impl(/*reversed=*/true);
InstallServiceHandler(echo_service_handler, &reversed_impl, /*reversed=*/true);
ZX_ASSERT(GetOutgoingDirectory()
->AddService<fuchsia_examples::EchoService>(std::move(service_handler))
.is_ok());
// Setup test client.
auto open_result =
service::OpenServiceAt<fuchsia_examples::EchoService>(TakeSvcClientEnd(TakeRootClientEnd()));
ZX_ASSERT(open_result.is_ok());
fuchsia_examples::EchoService::ServiceClient service = std::move(open_result.value());
// Assert that service is connected and that proper impl returns expected reply.
for (bool reversed : {true, false}) {
bool message_echoed = false;
auto client = ConnectToServiceMember(service, reversed);
auto expected_reply = reversed ? kTestStringReversed : kTestString;
client->EchoString(kTestString)
.ThenExactlyOnce(
[quit_loop = QuitLoopClosure(), &message_echoed, expected_reply = expected_reply](
fidl::WireUnownedResult<fuchsia_examples::Echo::EchoString>& reply) {
EXPECT_TRUE(reply.ok()) << "Reply failed with: " << reply.error().status_string();
EXPECT_EQ(reply.value().response.get(), cpp17::string_view(expected_reply));
message_echoed = true;
quit_loop();
});
RunLoop();
EXPECT_TRUE(message_echoed);
}
// Next, assert that after removing the service, the client end yields ZX_ERR_PEER_CLOSED.
ZX_ASSERT(GetOutgoingDirectory()->RemoveService<fuchsia_examples::EchoService>().is_ok());
for (bool reversed : {true, false}) {
auto connect_result =
reversed ? service.connect_reversed_echo() : service.connect_regular_echo();
ZX_ASSERT(connect_result.is_error());
EXPECT_EQ(connect_result.status_value(), ZX_ERR_PEER_CLOSED);
}
}
// Test that serving a FIDL Protocol works as expected.
TEST_F(OutgoingDirectoryTest, AddProtocolCanServeMultipleProtocols) {
constexpr static std::array<std::pair<bool, const char*>, 2> kIsReversedAndPaths = {
{{false, "fuchsia.examples.Echo"}, {true, "fuchsia.examples.Ohce"}}};
// Setup fuchsia.examples.Echo servers
EchoImpl regular_impl(/*reversed=*/false);
EchoImpl reversed_impl(/*reversed=*/true);
for (auto [reversed, path] : kIsReversedAndPaths) {
auto* impl = reversed ? &reversed_impl : &regular_impl;
ASSERT_EQ(
GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(impl, path).status_value(),
ZX_OK);
}
// Setup fuchsia.examples.Echo client
for (auto [reversed, path] : kIsReversedAndPaths) {
auto client_end =
service::ConnectAt<fuchsia_examples::Echo>(TakeSvcClientEnd(TakeRootClientEnd()), path);
ASSERT_EQ(client_end.status_value(), ZX_OK);
fidl::WireClient<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher());
std::string reply_received;
client->EchoString(kTestString)
.ThenExactlyOnce([&reply_received, quit_loop = QuitLoopClosure()](
fidl::WireUnownedResult<fuchsia_examples::Echo::EchoString>& result) {
ASSERT_TRUE(result.ok()) << "EchoString failed: " << result.error();
auto* response = result.Unwrap();
reply_received = std::string(response->response.data(), response->response.size());
quit_loop();
});
RunLoop();
auto expected_reply = reversed ? kTestStringReversed : kTestString;
EXPECT_EQ(reply_received, expected_reply);
}
}
// Test that after removing protocol, all clients are unable to make a call on
// the channel.
TEST_F(OutgoingDirectoryTest, RemoveProtocolClosesAllConnections) {
// For this test case, 3 clients will connect to one Echo protocol.
static constexpr size_t kNumClients = 3;
class EventHandler : public fidl::AsyncEventHandler<fuchsia_examples::Echo> {
public:
EventHandler() = default;
void on_fidl_error(fidl::UnbindInfo error) override { errors_.emplace_back(error); }
std::vector<fidl::UnbindInfo> GetErrors() { return errors_; }
private:
std::vector<fidl::UnbindInfo> errors_;
};
EventHandler event_handler;
EchoImpl regular_impl(/*reversed=*/false);
ASSERT_EQ(
GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&regular_impl).status_value(),
ZX_OK);
fidl::ClientEnd<fuchsia_io::Directory> svc_directory = TakeSvcClientEnd(TakeRootClientEnd());
std::vector<fidl::Client<fuchsia_examples::Echo>> clients = {};
for (size_t i = 0; i < kNumClients; ++i) {
auto client_end = service::ConnectAt<fuchsia_examples::Echo>(svc_directory.borrow());
ASSERT_EQ(client_end.status_value(), ZX_OK);
fidl::Client<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher(),
&event_handler);
client->EchoString(std::string(kTestString))
.ThenExactlyOnce([quit_loop = QuitLoopClosure()](
fidl::Result<fuchsia_examples::Echo::EchoString>& result) {
ASSERT_TRUE(result.is_ok());
ASSERT_EQ(result->response(), kTestString);
quit_loop();
});
RunLoop();
clients.emplace_back(std::move(client));
}
ASSERT_EQ(GetOutgoingDirectory()->RemoveProtocol<fuchsia_examples::Echo>().status_value(), ZX_OK);
RunLoopUntilIdle();
ASSERT_EQ(event_handler.GetErrors().size(), kNumClients);
for (auto& error : event_handler.GetErrors()) {
EXPECT_TRUE(error.is_peer_closed())
<< "Expected peer_closed. Got : " << error.FormatDescription();
}
}
// Test that serving a FIDL Protocol from a non-svc directory works as expected.
TEST_F(OutgoingDirectoryTest, AddProtocolAtServesProtocol) {
constexpr static char kDirectory[] = "test";
// Setup fuchsia.examples.Echo servers
EchoImpl regular_impl(/*reversed=*/false);
ASSERT_EQ(GetOutgoingDirectory()
->AddProtocolAt<fuchsia_examples::Echo>(kDirectory, &regular_impl)
.status_value(),
ZX_OK);
auto client_end = service::ConnectAt<fuchsia_examples::Echo>(
TakeSvcClientEnd(TakeRootClientEnd(), /*path=*/kDirectory));
ASSERT_EQ(client_end.status_value(), ZX_OK);
fidl::WireClient<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher());
std::string reply_received;
client->EchoString(kTestString)
.ThenExactlyOnce([&reply_received, quit_loop = QuitLoopClosure()](
fidl::WireUnownedResult<fuchsia_examples::Echo::EchoString>& result) {
ASSERT_TRUE(result.ok()) << "EchoString failed: " << result.error().FormatDescription();
auto* response = result.Unwrap();
reply_received = std::string(response->response.data(), response->response.size());
quit_loop();
});
RunLoop();
EXPECT_EQ(reply_received, kTestString);
}
TEST_F(OutgoingDirectoryTest, AddDirectoryCanServeADirectory) {
static constexpr char kTestDirectory[] = "diagnostics";
static constexpr char kTestFile[] = "sample.txt";
static constexpr char kTestContent[] = "Hello World!";
fs::ManagedVfs vfs(dispatcher());
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
auto text_file = fbl::MakeRefCounted<fs::BufferedPseudoFile>(
/*read_handler=*/[](fbl::String* output) -> zx_status_t {
*output = kTestContent;
return ZX_OK;
});
auto diagnostics = fbl::MakeRefCounted<fs::PseudoDir>();
diagnostics->AddEntry(kTestFile, text_file);
vfs.ServeDirectory(diagnostics, std::move(endpoints->server));
ASSERT_EQ(GetOutgoingDirectory()
->AddDirectory(std::move(endpoints->client), kTestDirectory)
.status_value(),
ZX_OK);
std::thread([client_end = TakeRootClientEnd().TakeChannel().release(),
quit_loop = QuitLoopClosure()]() {
fbl::unique_fd root_fd;
ASSERT_EQ(fdio_fd_create(client_end, root_fd.reset_and_get_address()), ZX_OK);
ZX_ASSERT_MSG(root_fd.is_valid(), "Failed to open root ns as a file descriptor: %s",
strerror(errno));
fbl::unique_fd dir_fd(openat(root_fd.get(), kTestDirectory, O_DIRECTORY));
ZX_ASSERT_MSG(dir_fd.is_valid(), "Failed to open directory \"%s\": %s", kTestDirectory,
strerror(errno));
fbl::unique_fd filefd(openat(dir_fd.get(), kTestFile, O_RDONLY));
ZX_ASSERT_MSG(filefd.is_valid(), "Failed to open file \"%s\": %s", kTestFile, strerror(errno));
static constexpr size_t kMaxBufferSize = 1024;
static char kReadBuffer[kMaxBufferSize];
size_t bytes_read = read(filefd.get(), reinterpret_cast<void*>(kReadBuffer), kMaxBufferSize);
ZX_ASSERT_MSG(bytes_read > 0, "Read 0 bytes from file at \"%s\": %s", kTestFile,
strerror(errno));
std::string actual_content(kReadBuffer, bytes_read);
EXPECT_EQ(actual_content, kTestContent);
quit_loop();
}).detach();
RunLoop();
vfs.Shutdown([quit_loop = QuitLoopClosure()](zx_status_t status) {
ASSERT_EQ(status, ZX_OK);
quit_loop();
});
RunLoop();
EXPECT_EQ(GetOutgoingDirectory()->RemoveDirectory(kTestDirectory).status_value(), ZX_OK);
}
// Test that we can connect to the outgoing directory via multiple connections.
TEST_F(OutgoingDirectoryTest, ServeCanYieldMultipleConnections) {
// Setup fuchsia.examples.Echo server
EchoImpl regular_impl(/*reversed=*/false);
ASSERT_EQ(
GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&regular_impl).status_value(),
ZX_OK);
// Setup fuchsia.examples.Echo client
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
// First |Serve| is invoked as part of test setup, so we'll assert that a
// subsequent invocation is allowed.
ASSERT_EQ(GetOutgoingDirectory()->Serve(std::move(endpoints->server)).status_value(), ZX_OK);
std::vector<fidl::ClientEnd<fuchsia_io::Directory>> root_client_ends;
// Take client end for channel used during invocation of |Serve| during setup.
root_client_ends.emplace_back(TakeRootClientEnd());
// Take client end for channel used during invocation of |Serve| in this function.
root_client_ends.emplace_back(std::move(endpoints->client));
while (!root_client_ends.empty()) {
auto root = std::move(root_client_ends.back());
root_client_ends.pop_back();
auto client_end =
service::ConnectAt<fuchsia_examples::Echo>(TakeSvcClientEnd(/*root=*/std::move(root)));
ASSERT_EQ(client_end.status_value(), ZX_OK);
fidl::WireClient<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher());
std::string reply_received;
client->EchoString(kTestString)
.ThenExactlyOnce([&reply_received, quit_loop = QuitLoopClosure()](
fidl::WireUnownedResult<fuchsia_examples::Echo::EchoString>& result) {
ASSERT_TRUE(result.ok()) << "EchoString failed: " << result.error();
auto* response = result.Unwrap();
reply_received = std::string(response->response.data(), response->response.size());
quit_loop();
});
RunLoop();
EXPECT_EQ(reply_received, kTestString);
}
}
TEST_F(OutgoingDirectoryTest, CreateFailsIfDispatcherIsNullptr) {
ASSERT_DEATH(
{ auto outgoing_directory = component::OutgoingDirectory::Create(/*dispatcher=*/nullptr); },
"");
}
TEST_F(OutgoingDirectoryTest, ServeFailsIfHandleInvalid) {
auto outgoing_directory = component::OutgoingDirectory::Create(dispatcher());
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
// Close server end in order to invalidate channel.
endpoints->server.reset();
EXPECT_EQ(outgoing_directory.Serve(std::move(endpoints->server)).status_value(),
ZX_ERR_BAD_HANDLE);
}
TEST_F(OutgoingDirectoryTest, AddServiceFailsIfInstanceNameIsEmpty) {
EXPECT_EQ(GetOutgoingDirectory()
->AddService<fuchsia_examples::EchoService>(CreateNonEmptyServiceHandler(),
/*instance=*/"")
.status_value(),
ZX_ERR_INVALID_ARGS);
}
TEST_F(OutgoingDirectoryTest, AddServiceFailsIfEntryExists) {
ASSERT_EQ(GetOutgoingDirectory()
->AddService<fuchsia_examples::EchoService>(CreateNonEmptyServiceHandler())
.status_value(),
ZX_OK);
EXPECT_EQ(GetOutgoingDirectory()
->AddService<fuchsia_examples::EchoService>(CreateNonEmptyServiceHandler())
.status_value(),
ZX_ERR_ALREADY_EXISTS);
}
TEST_F(OutgoingDirectoryTest, AddServiceFailsIfServiceHandlerEmpty) {
EXPECT_EQ(GetOutgoingDirectory()
->AddService<fuchsia_examples::EchoService>(component::ServiceHandler())
.status_value(),
ZX_ERR_INVALID_ARGS);
}
TEST_F(OutgoingDirectoryTest, AddNamedServiceFailsIfServiceNameIsEmpty) {
EXPECT_EQ(GetOutgoingDirectory()
->AddNamedService(CreateNonEmptyServiceHandler(), /*service=*/"",
/*instance=*/component::kDefaultInstance)
.status_value(),
ZX_ERR_INVALID_ARGS);
}
TEST_F(OutgoingDirectoryTest, AddProtocolFailsIfImplIsNullptr) {
EXPECT_EQ(GetOutgoingDirectory()
->AddProtocol<fuchsia_examples::Echo>(
/*impl*/ static_cast<fidl::WireServer<fuchsia_examples::Echo>*>(nullptr))
.status_value(),
ZX_ERR_INVALID_ARGS);
EXPECT_EQ(GetOutgoingDirectory()
->AddProtocol<fuchsia_examples::Echo>(
/*impl*/ static_cast<fidl::Server<fuchsia_examples::Echo>*>(nullptr))
.status_value(),
ZX_ERR_INVALID_ARGS);
}
TEST_F(OutgoingDirectoryTest, AddProtocolFailsIfNameIsEmpty) {
EchoImpl regular_impl(/*reversed=*/false);
EXPECT_EQ(GetOutgoingDirectory()
->AddProtocol<fuchsia_examples::Echo>(&regular_impl, /*name=*/"")
.status_value(),
ZX_ERR_INVALID_ARGS);
}
TEST_F(OutgoingDirectoryTest, AddProtocolFailsIfEntryExists) {
EchoImpl regular_impl(/*reversed=*/false);
ASSERT_EQ(
GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&regular_impl).status_value(),
ZX_OK);
EXPECT_EQ(
GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&regular_impl).status_value(),
ZX_ERR_ALREADY_EXISTS);
}
TEST_F(OutgoingDirectoryTest, AddProtocolAtFailsIfDirectoryIsEmpty) {
EchoImpl regular_impl(/*reversed=*/false);
EXPECT_EQ(GetOutgoingDirectory()
->AddProtocolAt<fuchsia_examples::Echo>(/*directory=*/"", &regular_impl)
.status_value(),
ZX_ERR_INVALID_ARGS);
}
TEST_F(OutgoingDirectoryTest, AddDirectoryFailsIfRemoteDirInvalid) {
fidl::ClientEnd<fuchsia_io::Directory> dangling_client_end;
ASSERT_FALSE(dangling_client_end.is_valid());
EXPECT_EQ(GetOutgoingDirectory()
->AddDirectory(std::move(dangling_client_end), "AValidName")
.status_value(),
ZX_ERR_BAD_HANDLE);
}
TEST_F(OutgoingDirectoryTest, AddDirectoryFailsIfDirectoryNameIsEmpty) {
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
EXPECT_EQ(GetOutgoingDirectory()
->AddDirectory(std::move(endpoints->client), /*directory_name=*/"")
.status_value(),
ZX_ERR_INVALID_ARGS);
}
TEST_F(OutgoingDirectoryTest, AddDirectoryFailsIfEntryExists) {
constexpr char kDirectoryName[] = "test";
for (auto expected_status : {ZX_OK, ZX_ERR_ALREADY_EXISTS}) {
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
EXPECT_EQ(GetOutgoingDirectory()
->AddDirectory(std::move(endpoints->client), kDirectoryName)
.status_value(),
expected_status);
}
}
TEST_F(OutgoingDirectoryTest, AddDirectoryFailsIfNameUsedForAddProtocolAt) {
constexpr char kDirectoryName[] = "diagnostics";
EchoImpl regular_impl(/*reversed=*/false);
ASSERT_EQ(GetOutgoingDirectory()
->AddProtocolAt<fuchsia_examples::Echo>(kDirectoryName, &regular_impl)
.status_value(),
ZX_OK);
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
EXPECT_EQ(GetOutgoingDirectory()
->AddDirectory(std::move(endpoints->client), kDirectoryName)
.status_value(),
ZX_ERR_ALREADY_EXISTS);
}
TEST_F(OutgoingDirectoryTest, RemoveServiceFailsIfEntryDoesNotExist) {
EXPECT_EQ(GetOutgoingDirectory()->RemoveService<fuchsia_examples::EchoService>().status_value(),
ZX_ERR_NOT_FOUND);
}
TEST_F(OutgoingDirectoryTest, RemoveProtocolFailsIfEntryDoesNotExist) {
EXPECT_EQ(GetOutgoingDirectory()->RemoveProtocol<fuchsia_examples::Echo>().status_value(),
ZX_ERR_NOT_FOUND);
}
TEST_F(OutgoingDirectoryTest, RemoveDirectoryFailsIfEntryDoesNotExist) {
EXPECT_EQ(GetOutgoingDirectory()->RemoveDirectory(/*directory_name=*/"test").status_value(),
ZX_ERR_NOT_FOUND);
}
class OutgoingDirectoryPathParameterizedFixture
: public testing::TestWithParam<std::pair<std::string, std::string>> {};
TEST_P(OutgoingDirectoryPathParameterizedFixture, BadServicePaths) {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto outgoing_directory = component::OutgoingDirectory::Create(loop.dispatcher());
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
ZX_ASSERT(outgoing_directory.Serve(std::move(endpoints->server)).is_ok());
component::ServiceHandler service_handler;
fuchsia_examples::EchoService::Handler echo_service_handler(&service_handler);
EchoImpl regular_impl(/*reversed=*/false);
auto noop_handler = [](fidl::ServerEnd<fuchsia_examples::Echo> _request) -> void {};
ZX_ASSERT(echo_service_handler.add_regular_echo(std::move(noop_handler)).is_ok());
auto service_and_instance_names = GetParam();
EXPECT_EQ(outgoing_directory
.AddNamedService(component::ServiceHandler(), service_and_instance_names.first,
service_and_instance_names.second)
.status_value(),
ZX_ERR_INVALID_ARGS);
}
INSTANTIATE_TEST_SUITE_P(OutgoingDirectoryTestPathTest, OutgoingDirectoryPathParameterizedFixture,
testing::Values(std::make_pair("", component::kDefaultInstance),
std::make_pair(".", component::kDefaultInstance),
std::make_pair("fuchsia.examples.EchoService", ""),
std::make_pair("fuchsia.examples.EchoService", "")));
} // namespace