blob: 78cc2b779388b511039aa6ec9e943871c76093a1 [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 <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/service/llcpp/service.h>
#include <lib/zx/channel.h>
#include <fs/pseudo_dir.h>
#include <fs/service.h>
#include <fs/synchronous_vfs.h>
#include <zxtest/zxtest.h>
#include "../generated/fidl_llcpp_test.test.h"
namespace {
using Echo = ::llcpp::fidl::service::test::Echo;
using EchoService = ::llcpp::fidl::service::test::EchoService;
class EchoCommon : public Echo::Interface {
public:
explicit EchoCommon(const char* prefix) : prefix_(prefix) {}
zx_status_t Connect(async_dispatcher_t* dispatcher, zx::channel request) {
return fidl::Bind(dispatcher, std::move(request), this);
}
void EchoString(fidl::StringView input, EchoStringCompleter::Sync completer) override {
std::string reply = prefix_ + ": " + std::string(input.data(), input.size());
completer.Reply(fidl::StringView(reply));
}
private:
std::string prefix_;
};
class MockEchoService {
public:
static constexpr const char* kName = EchoService::Name;
// Build a VFS that looks like:
//
// fuchsia.echo.EchoService/
// default/
// foo (Echo)
// bar (Echo)
// other/
// foo (Echo)
// bar (Echo)
//
explicit MockEchoService(async_dispatcher_t* dispatcher)
: dispatcher_(dispatcher), vfs_(dispatcher) {
auto default_member_foo = fbl::MakeRefCounted<fs::Service>([this](zx::channel request) {
return default_foo_.Connect(dispatcher_, std::move(request));
});
auto default_member_bar = fbl::MakeRefCounted<fs::Service>([this](zx::channel request) {
return default_bar_.Connect(dispatcher_, std::move(request));
});
auto default_instance = fbl::MakeRefCounted<fs::PseudoDir>();
default_instance->AddEntry("foo", std::move(default_member_foo));
default_instance->AddEntry("bar", std::move(default_member_bar));
auto other_member_foo = fbl::MakeRefCounted<fs::Service>([this](zx::channel request) {
return other_foo_.Connect(dispatcher_, std::move(request));
});
auto other_member_bar = fbl::MakeRefCounted<fs::Service>([this](zx::channel request) {
return other_bar_.Connect(dispatcher_, std::move(request));
});
auto other_instance = fbl::MakeRefCounted<fs::PseudoDir>();
other_instance->AddEntry("foo", std::move(other_member_foo));
other_instance->AddEntry("bar", std::move(other_member_bar));
auto service = fbl::MakeRefCounted<fs::PseudoDir>();
service->AddEntry("default", std::move(default_instance));
service->AddEntry("other", std::move(other_instance));
auto root_dir = fbl::MakeRefCounted<fs::PseudoDir>();
root_dir->AddEntry(kName, std::move(service));
zx::channel svc_remote;
ASSERT_OK(zx::channel::create(0, &svc_local_, &svc_remote));
vfs_.ServeDirectory(root_dir, std::move(svc_remote));
}
zx::unowned_channel svc() { return zx::unowned_channel(svc_local_); }
private:
async_dispatcher_t* dispatcher_;
fs::SynchronousVfs vfs_;
EchoCommon default_foo_{"default-foo"};
EchoCommon default_bar_{"default-bar"};
EchoCommon other_foo_{"other-foo"};
EchoCommon other_bar_{"other-bar"};
zx::channel svc_local_;
};
} // namespace
class ClientTest : public zxtest::Test {
protected:
ClientTest()
: loop_(&kAsyncLoopConfigNoAttachToCurrentThread),
echo_service_(loop_.dispatcher()),
svc_(echo_service_.svc()) {}
void SetUp() override { loop_.StartThread("client-test-loop"); }
void TearDown() override { loop_.Shutdown(); }
async::Loop loop_;
MockEchoService echo_service_;
zx::unowned_channel svc_;
};
TEST_F(ClientTest, ConnectsToDefault) {
fidl::result<EchoService::ServiceClient> open_result =
llcpp::sys::OpenServiceAt<EchoService>(std::move(svc_));
ASSERT_TRUE(open_result.is_ok());
EchoService::ServiceClient service = open_result.take_value();
// Connect to the member 'foo'.
fidl::result<fidl::ClientChannel<Echo>> connect_result = service.connect_foo();
ASSERT_TRUE(connect_result.is_ok());
Echo::SyncClient client = fidl::BindSyncClient(connect_result.take_value());
Echo::ResultOf::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(ClientTest, ConnectsToOther) {
fidl::result<EchoService::ServiceClient> open_result =
llcpp::sys::OpenServiceAt<EchoService>(std::move(svc_), "other");
ASSERT_TRUE(open_result.is_ok());
EchoService::ServiceClient service = open_result.take_value();
// Connect to the member 'bar'.
fidl::result<fidl::ClientChannel<Echo>> connect_result = service.connect_bar();
ASSERT_TRUE(connect_result.is_ok());
Echo::SyncClient client = fidl::BindSyncClient(connect_result.take_value());
Echo::ResultOf::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-bar: hello");
}
TEST_F(ClientTest, FilePathTooLong) {
std::string illegal_path;
illegal_path.assign(256, 'a');
// Use an instance name that is too long.
zx::unowned_channel svc_copy(*svc_);
fidl::result<EchoService::ServiceClient> open_result =
llcpp::sys::OpenServiceAt<EchoService>(std::move(svc_copy), illegal_path);
ASSERT_TRUE(open_result.is_error());
ASSERT_EQ(open_result.error(), ZX_ERR_INVALID_ARGS);
// Use a service name that is too long.
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
ASSERT_EQ(
llcpp::sys::OpenNamedServiceAt(std::move(svc_), illegal_path, "default", std::move(remote)),
ZX_ERR_INVALID_ARGS);
}