blob: 220287ff3ee0ff73f8adc2d9230a9b6d2828843b [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 <errno.h>
#include <fcntl.h>
#include <fuchsia/component/cpp/fidl.h>
#include <fuchsia/component/decl/cpp/fidl.h>
#include <fuchsia/data/cpp/fidl.h>
#include <fuchsia/examples/cpp/fidl.h>
#include <fuchsia/io/cpp/fidl.h>
#include <fuchsia/logger/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/dispatcher.h>
#include <lib/fdio/namespace.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/fidl/cpp/comparison.h>
#include <lib/fidl/cpp/string.h>
#include <lib/fit/function.h>
#include <lib/stdcompat/optional.h>
#include <lib/sys/component/cpp/testing/realm_builder.h>
#include <lib/sys/component/cpp/testing/realm_builder_types.h>
#include <lib/sys/component/cpp/tests/utils.h>
#include <lib/sys/cpp/component_context.h>
#include <string.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <iostream>
#include <memory>
#include <optional>
#include <sstream>
#include <gtest/gtest.h>
#include <src/lib/fostr/fidl/fuchsia/component/decl/formatting.h>
#include <src/lib/testing/loop_fixture/real_loop_fixture.h>
#include <test/placeholders/cpp/fidl.h>
#include "src/lib/testing/loop_fixture/real_loop_fixture.h"
namespace {
using namespace component_testing;
namespace fdecl = fuchsia::component::decl;
constexpr char kEchoServerUrl[] =
"fuchsia-pkg://fuchsia.com/component_cpp_testing_realm_builder_tests#meta/echo_server.cm";
constexpr char kEchoServerScUrl[] = "#meta/echo_server_sc.cm";
constexpr char kEchoServerLegacyUrl[] =
"fuchsia-pkg://fuchsia.com/component_cpp_testing_realm_builder_tests#meta/echo_server.cmx";
constexpr char kEchoServerRelativeUrl[] = "#meta/echo_server.cm";
constexpr char kEchoServiceServerUrl[] = "#meta/echo_service_server.cm";
class RealmBuilderTest : public gtest::RealLoopFixture {};
TEST_F(RealmBuilderTest, RoutesProtocolFromChild) {
static constexpr char kEchoServer[] = "echo_server";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServer, kEchoServerUrl);
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServer},
.targets = {ParentRef()}});
auto realm = realm_builder.Build(dispatcher());
auto echo = realm.ConnectSync<test::placeholders::Echo>();
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString("hello", &response), ZX_OK);
EXPECT_EQ(response, fidl::StringPtr("hello"));
}
TEST_F(RealmBuilderTest, StructuredConfig) {
static constexpr char kEchoServerSc[] = "echo_server_sc";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
realm_builder.AddRoute(Route{.capabilities = {Protocol{fuchsia::logger::LogSink::Name_}},
.source = ParentRef(),
.targets = {ChildRef{kEchoServerSc}}});
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServerSc},
.targets = {ParentRef()}});
auto realm = realm_builder.Build(dispatcher());
auto echo = realm.ConnectSync<test::placeholders::Echo>();
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString("hello", &response), ZX_OK);
EXPECT_EQ(response,
fidl::StringPtr(
"hello "
"[1][255][65535][4000000000][8000000000][-127][-32766][-2000000000][-4000000000]["
"hello][1,0,][1,2,][2,3,][3,4,][4,5,][-1,-2,][-2,-3,][-3,-4,][-4,-5,][foo,bar,]"));
}
TEST_F(RealmBuilderTest, SetConfigValue) {
static constexpr char kEchoServerSc[] = "echo_server_sc";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
realm_builder.SetConfigValue(kEchoServerSc, "my_flag", ConfigValue::Bool(true));
realm_builder.SetConfigValue(kEchoServerSc, "my_uint8", ConfigValue::Uint8(1));
realm_builder.SetConfigValue(kEchoServerSc, "my_uint16", ConfigValue::Uint16(1));
realm_builder.SetConfigValue(kEchoServerSc, "my_uint32", ConfigValue::Uint32(1));
realm_builder.SetConfigValue(kEchoServerSc, "my_uint64", ConfigValue::Uint64(1));
realm_builder.SetConfigValue(kEchoServerSc, "my_int8", ConfigValue::Int8(-1));
realm_builder.SetConfigValue(kEchoServerSc, "my_int16", ConfigValue::Int16(-1));
realm_builder.SetConfigValue(kEchoServerSc, "my_int32", ConfigValue::Int32(-1));
realm_builder.SetConfigValue(kEchoServerSc, "my_int64", ConfigValue::Int64(-1));
realm_builder.SetConfigValue(kEchoServerSc, "my_string", "foo");
realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_flag", std::vector<bool>{false, true});
realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_uint8", std::vector<uint8_t>{1, 1});
realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_uint16", std::vector<uint16_t>{1, 1});
realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_uint32", std::vector<uint32_t>{1, 1});
realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_uint64", std::vector<uint64_t>{1, 1});
realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_int8", std::vector<int8_t>{-1, 1});
realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_int16", std::vector<int16_t>{-1, 1});
realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_int32", std::vector<int32_t>{-1, 1});
realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_int64", std::vector<int64_t>{-1, 1});
realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_string",
std::vector<std::string>{"bar", "foo"});
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServerSc},
.targets = {ParentRef()}});
realm_builder.AddRoute(Route{.capabilities = {Protocol{fuchsia::logger::LogSink::Name_}},
.source = ParentRef(),
.targets = {ChildRef{kEchoServerSc}}});
auto realm = realm_builder.Build(dispatcher());
auto echo = realm.ConnectSync<test::placeholders::Echo>();
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString("hello", &response), ZX_OK);
EXPECT_EQ(response, fidl::StringPtr("hello "
"[1][1][1][1][1][-1][-1][-1][-1][foo][0,1,][1,1,][1,1,][1,1,]"
"[1,1,][-1,1,][-1,1,][-1,1,][-1,1,][bar,foo,]"));
}
TEST_F(RealmBuilderTest, SetConfigValueFails) {
ASSERT_DEATH(
{
static constexpr char kEchoServer[] = "echo_server";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServer, kEchoServerRelativeUrl);
realm_builder.SetConfigValue(kEchoServer, "my_flag", ConfigValue::Bool(true));
},
"");
ASSERT_DEATH(
{
static constexpr char kEchoServerSc[] = "echo_server_sc";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
realm_builder.SetConfigValue(kEchoServerSc, "doesnt_exist", ConfigValue::Bool(true));
},
"");
ASSERT_DEATH(
{
static constexpr char kEchoServerSc[] = "echo_server_sc";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
realm_builder.SetConfigValue(kEchoServerSc, "my_string", ConfigValue::Bool(true));
},
"");
ASSERT_DEATH(
{
static constexpr char kEchoServerSc[] = "echo_server_sc";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
realm_builder.SetConfigValue(kEchoServerSc, "my_string", "abccdefghijklmnop");
},
"");
ASSERT_DEATH(
{
static constexpr char kEchoServerSc[] = "echo_server_sc";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
realm_builder.SetConfigValue(kEchoServerSc, "my_string",
std::vector<std::string>{"abcdefghijklmnopqrstuvwxyz", "abc"});
},
"");
}
TEST_F(RealmBuilderTest, RoutesProtocolFromLegacyChild) {
static constexpr char kEchoServer[] = "echo_server";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddLegacyChild(kEchoServer, kEchoServerLegacyUrl);
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServer},
.targets = {ParentRef()}});
auto realm = realm_builder.Build(dispatcher());
auto echo = realm.ConnectSync<test::placeholders::Echo>();
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString("hello", &response), ZX_OK);
EXPECT_EQ(response, fidl::StringPtr("hello"));
}
TEST_F(RealmBuilderTest, RoutesProtocolFromRelativeChild) {
static constexpr char kEchoServer[] = "echo_server";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServer, kEchoServerRelativeUrl);
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServer},
.targets = {ParentRef()}});
auto realm = realm_builder.Build(dispatcher());
auto echo = realm.ConnectSync<test::placeholders::Echo>();
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString("hello", &response), ZX_OK);
EXPECT_EQ(response, fidl::StringPtr("hello"));
}
class LocalEchoServer : public test::placeholders::Echo, public LocalComponent {
public:
explicit LocalEchoServer(fit::closure quit_loop, async_dispatcher_t* dispatcher)
: quit_loop_(std::move(quit_loop)), dispatcher_(dispatcher), called_(false) {}
void EchoString(::fidl::StringPtr value, EchoStringCallback callback) override {
callback(std::move(value));
called_ = true;
quit_loop_();
}
void Start(std::unique_ptr<LocalComponentHandles> handles) override {
handles_ = std::move(handles);
ASSERT_EQ(handles_->outgoing()->AddPublicService(bindings_.GetHandler(this, dispatcher_)),
ZX_OK);
}
bool WasCalled() const { return called_; }
private:
fit::closure quit_loop_;
async_dispatcher_t* dispatcher_;
fidl::BindingSet<test::placeholders::Echo> bindings_;
bool called_;
std::unique_ptr<LocalComponentHandles> handles_;
};
TEST_F(RealmBuilderTest, RoutesProtocolFromLocalComponent) {
static constexpr char kEchoServer[] = "echo_server";
LocalEchoServer local_echo_server(QuitLoopClosure(), dispatcher());
auto realm_builder = RealmBuilder::Create();
realm_builder.AddLocalChild(kEchoServer, &local_echo_server);
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServer},
.targets = {ParentRef()}});
auto realm = realm_builder.Build(dispatcher());
test::placeholders::EchoPtr echo;
ASSERT_EQ(realm.Connect(echo.NewRequest()), ZX_OK);
echo->EchoString("hello", [](fidl::StringPtr _) {});
RunLoop();
EXPECT_TRUE(local_echo_server.WasCalled());
}
class LocalEchoClient : public LocalComponent {
public:
explicit LocalEchoClient(fit::closure quit_loop)
: quit_loop_(std::move(quit_loop)), called_(false) {}
void Start(std::unique_ptr<LocalComponentHandles> handles) override {
handles_ = std::move(handles);
test::placeholders::EchoSyncPtr echo;
ASSERT_EQ(handles_->svc().Connect<test::placeholders::Echo>(echo.NewRequest()), ZX_OK);
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString("milk", &response), ZX_OK);
ASSERT_EQ(response, fidl::StringPtr("milk"));
called_ = true;
quit_loop_();
}
bool WasCalled() const { return called_; }
private:
fit::closure quit_loop_;
bool called_;
std::unique_ptr<LocalComponentHandles> handles_;
};
TEST_F(RealmBuilderTest, RoutesProtocolToLocalComponent) {
static constexpr char kEchoClient[] = "echo_client";
static constexpr char kEchoServer[] = "echo_server";
LocalEchoClient local_echo_client(QuitLoopClosure());
auto realm_builder = RealmBuilder::Create();
realm_builder.AddLocalChild(kEchoClient, &local_echo_client,
ChildOptions{.startup_mode = StartupMode::EAGER});
realm_builder.AddChild(kEchoServer, kEchoServerUrl);
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServer},
.targets = {ChildRef{kEchoClient}}});
auto realm = realm_builder.Build(dispatcher());
RunLoop();
EXPECT_TRUE(local_echo_client.WasCalled());
}
TEST_F(RealmBuilderTest, RoutesServiceFromChild) {
static constexpr char kEchoServiceServer[] = "echo_service_server";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServiceServer, kEchoServiceServerUrl);
realm_builder.AddRoute(Route{.capabilities = {Service{fuchsia::examples::EchoService::Name}},
.source = ChildRef{kEchoServiceServer},
.targets = {ParentRef()}});
realm_builder.AddRoute(Route{.capabilities = {Protocol{fuchsia::logger::LogSink::Name_}},
.source = ParentRef(),
.targets = {ChildRef{kEchoServiceServer}}});
auto realm = realm_builder.Build(dispatcher());
auto default_service = sys::OpenServiceAt<fuchsia::examples::EchoService>(realm.CloneRoot());
auto regular = default_service.regular_echo().Connect().Bind();
constexpr char kMessage[] = "Ping!";
bool message_replied = false;
regular->EchoString(kMessage, [expected_reply = kMessage, &message_replied,
quit_loop = QuitLoopClosure()](fidl::StringPtr value) {
EXPECT_EQ(value, expected_reply);
message_replied = true;
quit_loop();
});
RunLoop();
EXPECT_TRUE(message_replied);
}
TEST_F(RealmBuilderTest, ConnectsToChannelDirectly) {
static constexpr char kEchoServer[] = "echo_server";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServer, kEchoServerUrl);
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServer},
.targets = {ParentRef()}});
auto realm = realm_builder.Build(dispatcher());
zx::channel controller, request;
ASSERT_EQ(zx::channel::create(0, &controller, &request), ZX_OK);
fidl::SynchronousInterfacePtr<test::placeholders::Echo> echo;
echo.Bind(std::move(controller));
ASSERT_EQ(realm.Connect(test::placeholders::Echo::Name_, std::move(request)), ZX_OK);
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString("hello", &response), ZX_OK);
EXPECT_EQ(response, fidl::StringPtr("hello"));
}
TEST_F(RealmBuilderTest, RoutesProtocolFromLocalComponentInSubRealm) {
static constexpr char kEchoServer[] = "echo_server";
static constexpr char kSubRealm[] = "sub_realm";
LocalEchoServer local_echo_server(QuitLoopClosure(), dispatcher());
auto realm_builder = RealmBuilder::Create();
auto sub_realm = realm_builder.AddChildRealm(kSubRealm);
// Route test.placeholders.Echo from local Echo server impl to parent.
sub_realm.AddLocalChild(kEchoServer, &local_echo_server);
sub_realm.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServer},
.targets = {ParentRef()}});
// Route test.placeholders.Echo from sub_realm child to parent.
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kSubRealm},
.targets = {ParentRef()}});
auto realm = realm_builder.Build(dispatcher());
test::placeholders::EchoPtr echo;
ASSERT_EQ(realm.Connect(echo.NewRequest()), ZX_OK);
echo->EchoString("hello", [](fidl::StringPtr _) {});
RunLoop();
EXPECT_TRUE(local_echo_server.WasCalled());
}
class FileReader : public LocalComponent {
public:
explicit FileReader(fit::closure quit_loop) : quit_loop_(std::move(quit_loop)) {}
void Start(std::unique_ptr<LocalComponentHandles> handles) override {
handles_ = std::move(handles);
started_ = true;
quit_loop_();
}
std::string GetContentsAt(std::string_view dirpath, std::string_view filepath) {
ZX_ASSERT_MSG(handles_ != nullptr,
"FileReader/GetContentsAt called before FileReader was started.");
constexpr static size_t kMaxBufferSize = 1024;
static char kReadBuffer[kMaxBufferSize];
int dirfd = fdio_ns_opendir(handles_->ns());
ZX_ASSERT_MSG(dirfd > 0, "Failed to open root ns as a file descriptor: %s", strerror(errno));
std::stringstream path_builder;
path_builder << dirpath << '/' << filepath;
auto path = path_builder.str();
int filefd = openat(dirfd, path.c_str(), O_RDONLY);
ZX_ASSERT_MSG(filefd > 0, "Failed to open path \"%s\": %s", path.c_str(), strerror(errno));
size_t bytes_read = read(filefd, reinterpret_cast<void*>(kReadBuffer), kMaxBufferSize);
ZX_ASSERT_MSG(bytes_read > 0, "Read 0 bytes from file at \"%s\": %s", path.c_str(),
strerror(errno));
return std::string(kReadBuffer, bytes_read);
}
bool HasStarted() const { return started_; }
private:
fit::closure quit_loop_;
bool started_ = false;
std::unique_ptr<LocalComponentHandles> handles_;
};
TEST_F(RealmBuilderTest, RoutesReadOnlyDirectory) {
static constexpr char kDirectoryName[] = "config";
static constexpr char kFilename[] = "environment";
static constexpr char kContent[] = "DEV";
FileReader file_reader(QuitLoopClosure());
auto realm_builder = RealmBuilder::Create();
realm_builder.AddLocalChild("file_reader", &file_reader,
ChildOptions{.startup_mode = StartupMode::EAGER});
realm_builder.RouteReadOnlyDirectory(kDirectoryName, {ChildRef{"file_reader"}},
std::move(DirectoryContents().AddFile(kFilename, kContent)));
auto realm = realm_builder.Build(dispatcher());
RunLoop();
ASSERT_TRUE(file_reader.HasStarted());
EXPECT_EQ(file_reader.GetContentsAt(kDirectoryName, kFilename), kContent);
}
// This test is similar to RealmBuilderTest.RoutesProtocolFromChild except
// that its setup is done by mutating the realm's root's decl. This is to
// assert that invoking |ReplaceRealmDecl| works as expected.
TEST_F(RealmBuilderTest, RealmDeclCanBeReplaced) {
static constexpr char kEchoServer[] = "echo_server";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild(kEchoServer, kEchoServerUrl);
auto decl = realm_builder.GetRealmDecl();
fdecl::ExposeProtocol expose_protocol;
expose_protocol.set_source(fdecl::Ref::WithChild(fdecl::ChildRef{.name = "echo_server"}));
expose_protocol.set_target(fdecl::Ref::WithParent(fdecl::ParentRef{}));
expose_protocol.set_source_name("test.placeholders.Echo");
expose_protocol.set_target_name("test.placeholders.Echo");
decl.mutable_exposes()->emplace_back(fdecl::Expose::WithProtocol(std::move(expose_protocol)));
realm_builder.ReplaceRealmDecl(std::move(decl));
auto realm = realm_builder.Build(dispatcher());
auto echo = realm.ConnectSync<test::placeholders::Echo>();
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString("hello", &response), ZX_OK);
EXPECT_EQ(response, fidl::StringPtr("hello"));
}
TEST_F(RealmBuilderTest, ForwardsArgsToLegacyComponent) {
static constexpr char kEchoServer[] = "echo_server";
static constexpr char kExpectedReply[] = "test";
auto realm_builder = RealmBuilder::Create();
realm_builder.AddLegacyChild(kEchoServer, kEchoServerLegacyUrl);
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServer},
.targets = {ParentRef()}});
auto decl = realm_builder.GetComponentDecl(kEchoServer);
fuchsia::data::DictionaryEntry args;
auto value = std::make_unique<fuchsia::data::DictionaryValue>(
fuchsia::data::DictionaryValue::WithStrVec({kExpectedReply}));
args.key = "args", args.value = std::move(value);
decl.mutable_program()->mutable_info()->mutable_entries()->emplace_back(std::move(args));
realm_builder.ReplaceComponentDecl(kEchoServer, std::move(decl));
auto realm = realm_builder.Build(dispatcher());
auto echo = realm.ConnectSync<test::placeholders::Echo>();
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString(cpp17::nullopt, &response), ZX_OK);
EXPECT_EQ(response, fidl::StringPtr(kExpectedReply));
}
// This test is similar to RealmBuilderTest.RoutesProtocolFromChild except
// that its setup is done statically via a manifest. This is to assert that
// invoking |CreateFromRelativeUrl| works as expected.
TEST_F(RealmBuilderTest, BuildsRealmFromRelativeUrl) {
static constexpr char kPrePopulatedRealmUrl[] = "#meta/pre_populated_realm.cm";
auto realm_builder = RealmBuilder::CreateFromRelativeUrl(kPrePopulatedRealmUrl);
auto realm = realm_builder.Build(dispatcher());
auto echo = realm.ConnectSync<test::placeholders::Echo>();
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString("hello", &response), ZX_OK);
EXPECT_EQ(response, fidl::StringPtr("hello"));
}
// This test is nearly identicaly to the RealmBuilderTest.RoutesProtocolFromChild
// test case above. The only difference is that it provides a svc directory
// from the sys::Context singleton object to the Realm::Builder::Create method.
// If the test passes, it must follow that Realm::Builder supplied a Context
// object internally, otherwise the test component wouldn't be able to connect
// to fuchsia.component.Realm protocol.
TEST_F(RealmBuilderTest, UsesProvidedSvcDirectory) {
auto context = sys::ComponentContext::Create();
static constexpr char kEchoServer[] = "echo_server";
auto realm_builder = RealmBuilder::Create(context->svc());
realm_builder.AddChild(kEchoServer, kEchoServerUrl);
realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
.source = ChildRef{kEchoServer},
.targets = {ParentRef()}});
auto realm = realm_builder.Build(dispatcher());
auto echo = realm.ConnectSync<test::placeholders::Echo>();
fidl::StringPtr response;
ASSERT_EQ(echo->EchoString("hello", &response), ZX_OK);
EXPECT_EQ(response, fidl::StringPtr("hello"));
}
TEST_F(RealmBuilderTest, UsesRandomChildName) {
std::string child_name_1;
{
auto realm_builder = RealmBuilder::Create();
auto realm = realm_builder.Build(dispatcher());
child_name_1 = realm.GetChildName();
}
std::string child_name_2;
{
auto realm_builder = RealmBuilder::Create();
auto realm = realm_builder.Build(dispatcher());
child_name_2 = realm.GetChildName();
}
EXPECT_NE(child_name_1, child_name_2);
}
TEST_F(RealmBuilderTest, CanCreateLongChildName) {
std::string child_name_1;
{
auto realm_builder = RealmBuilder::Create();
{
const std::string long_child_name(fuchsia::component::MAX_NAME_LENGTH + 1, 'a');
// AddChild should not panic.
realm_builder.AddChild(std::move(long_child_name), kEchoServerUrl);
}
{
const std::string long_child_name(fuchsia::component::MAX_CHILD_NAME_LENGTH, 'a');
// AddChild should not panic.
realm_builder.AddChild(std::move(long_child_name), kEchoServerUrl);
}
{
ASSERT_DEATH(
{
const std::string too_long_child_name(fuchsia::component::MAX_CHILD_NAME_LENGTH + 1,
'a');
realm_builder.AddChild(std::move(too_long_child_name), kEchoServerUrl);
},
"");
}
}
}
TEST_F(RealmBuilderTest, PanicsWhenBuildCalledMultipleTimes) {
ASSERT_DEATH(
{
auto realm_builder = RealmBuilder::Create();
realm_builder.Build(dispatcher());
realm_builder.Build(dispatcher());
},
"");
}
TEST(RealmBuilderUnittest, PanicsIfChildNameIsEmpty) {
ASSERT_DEATH(
{
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild("", kEchoServerUrl);
},
"");
ASSERT_DEATH(
{
auto realm_builder = RealmBuilder::Create();
realm_builder.AddLegacyChild("", kEchoServerUrl);
},
"");
class BasicLocalImpl : public LocalComponent {
void Start(std::unique_ptr<LocalComponentHandles> /* mock_handles */) {}
};
ASSERT_DEATH(
{
auto realm_builder = RealmBuilder::Create();
BasicLocalImpl local_impl;
realm_builder.AddLocalChild("", &local_impl);
},
"");
}
TEST(RealmBuilderUnittest, PanicsIfUrlIsEmpty) {
ASSERT_DEATH(
{
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild("some_valid_name", "");
},
"");
ASSERT_DEATH(
{
auto realm_builder = RealmBuilder::Create();
realm_builder.AddLegacyChild("some_valid_name", "");
},
"");
}
TEST(RealmBuilderUnittest, PanicsWhenArgsAreNullptr) {
ASSERT_DEATH(
{
auto realm_builder = RealmBuilder::Create();
// Should panic because |async_get_default_dispatcher| was not configured
// to return nullptr.
realm_builder.Build(nullptr);
},
"");
ASSERT_DEATH(
{
auto realm_builder = RealmBuilder::Create();
realm_builder.AddLocalChild("some_valid_name", nullptr);
},
"");
}
TEST(DirectoryContentsUnittest, PanicWhenGivenInvalidPath) {
ASSERT_DEATH(
{
auto directory_contents = DirectoryContents();
directory_contents.AddFile("/foo/bar.txt", "Hello World!");
},
"");
ASSERT_DEATH(
{
auto directory_contents = DirectoryContents();
directory_contents.AddFile("foo/bar/", "Hello World!");
},
"");
ASSERT_DEATH(
{
auto directory_contents = DirectoryContents();
directory_contents.AddFile("", "Hello World!");
},
"");
}
class PlaceholderComponent : public LocalComponent {
public:
PlaceholderComponent() = default;
void Start(std::unique_ptr<LocalComponentHandles> handles) override {}
};
constexpr char kRoutingTestChildName[] = "foobar";
class RealmBuilderRoutingParameterizedFixture
: public testing::TestWithParam<std::pair<Capability, std::shared_ptr<fdecl::Offer>>> {};
TEST_P(RealmBuilderRoutingParameterizedFixture, RoutedCapabilitiesYieldExpectedOfferClauses) {
auto realm_builder = RealmBuilder::Create();
PlaceholderComponent placeholder;
realm_builder.AddLocalChild(kRoutingTestChildName, &placeholder);
auto param = GetParam();
auto capability = param.first;
realm_builder.AddRoute(Route{.capabilities = {capability},
.source = ParentRef{},
.targets = {ChildRef{kRoutingTestChildName}}});
auto root_decl = realm_builder.GetRealmDecl();
ASSERT_EQ(root_decl.offers().size(), 1ul);
const fdecl::Offer& actual = root_decl.offers().at(0);
const fdecl::Offer& expected = *param.second;
EXPECT_TRUE(fidl::Equals(actual, expected)) << "Actual: " << actual << std::endl
<< "Expected: " << expected << std::endl;
}
INSTANTIATE_TEST_SUITE_P(
RealmBuilderRoutingTest, RealmBuilderRoutingParameterizedFixture,
testing::Values(
std::make_pair(Protocol{.name = "foo", .as = "bar"},
component::tests::CreateFidlProtocolOfferDecl(
/*source_name=*/"foo",
/*source=*/component::tests::CreateFidlParentRef(),
/*target_name=*/"bar",
/*target=*/component::tests::CreateFidlChildRef(kRoutingTestChildName))),
std::make_pair(Service{.name = "foo", .as = "bar"},
component::tests::CreateFidlServiceOfferDecl(
/*source_name=*/"foo",
/*source=*/component::tests::CreateFidlParentRef(),
/*target_name=*/"bar",
/*target=*/component::tests::CreateFidlChildRef(kRoutingTestChildName))),
std::make_pair(Directory{.name = "foo",
.as = "bar",
.subdir = "sub",
.rights = fuchsia::io::RW_STAR_DIR,
.path = "/foo"},
component::tests::CreateFidlDirectoryOfferDecl(
/*source_name=*/"foo",
/*source=*/component::tests::CreateFidlParentRef(),
/*target_name=*/"bar",
/*target=*/component::tests::CreateFidlChildRef(kRoutingTestChildName),
/*subdir=*/"sub",
/*rights=*/fuchsia::io::RW_STAR_DIR)),
std::make_pair(
Storage{.name = "foo", .as = "bar", .path = "/foo"},
component::tests::CreateFidlStorageOfferDecl(
/*source_name=*/"foo", /*source=*/component::tests::CreateFidlParentRef(),
/*target_name=*/"bar",
/*target=*/component::tests::CreateFidlChildRef(kRoutingTestChildName)))));
} // namespace