blob: 8a238d3f5fcc0febdaccc1031c3dccbc31348c56 [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 <fuchsia/component/cpp/fidl.h>
#include <fuchsia/component/decl/cpp/fidl.h>
#include <fuchsia/component/runner/cpp/fidl.h>
#include <fuchsia/component/test/cpp/fidl.h>
#include <fuchsia/data/cpp/fidl.h>
#include <fuchsia/io/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/loop.h>
#include <lib/async/dispatcher.h>
#include <lib/fdio/namespace.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/fidl/cpp/comparison.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/fidl/cpp/string.h>
#include <lib/fit/function.h>
#include <lib/sys/component/cpp/testing/internal/convert.h>
#include <lib/sys/component/cpp/testing/internal/local_component_runner.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 <lib/syslog/cpp/macros.h>
#include <lib/zx/channel.h>
#include <zircon/status.h>
#include <cstddef>
#include <utility>
#include <gtest/gtest.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 {
// NOLINTNEXTLINE
using namespace component_testing;
// NOLINTNEXTLINE
using namespace component_testing::internal;
namespace fctest = fuchsia::component::test;
namespace fcdecl = fuchsia::component::decl;
namespace fio = fuchsia::io;
/*
* Tests for |component_testing::internal::LocalComponentRunner| and
* |component_testing::internal::LocalComponentRunner::Builder|.
*/
class TestComponent : public LocalComponentImpl {
public:
explicit TestComponent(fit::closure quit_loop) : quit_loop_(std::move(quit_loop)) {}
void OnStart() override {
called_ = true;
quit_loop_();
}
bool WasCalled() const { return called_; }
private:
fit::closure quit_loop_;
bool called_ = false;
};
class LocalComponentRunnerTest : public gtest::RealLoopFixture {
public:
void SetUp() override {
auto builder = LocalComponentRunner::Builder();
builder.Register(kTestComponentName, [this]() {
auto test_component = std::make_unique<TestComponent>(QuitLoopClosure());
FX_CHECK(test_component_ == nullptr);
test_component_ = test_component.get();
return test_component;
});
local_component_runner_ = builder.Build(dispatcher());
runner_proxy_.Bind(local_component_runner_->NewBinding());
}
void TearDown() override {
local_component_runner_.reset();
default_component_controller_.Unbind();
runner_proxy_.Unbind();
}
protected:
static constexpr char kTestComponentName[] = "test_component";
void CallStart(
fuchsia::component::runner::ComponentStartInfo start_info,
fidl::InterfaceRequest<fuchsia::component::runner::ComponentController> controller) {
runner_proxy_->Start(std::move(start_info), std::move(controller));
}
void CallStart(fuchsia::component::runner::ComponentStartInfo start_info) {
CallStart(std::move(start_info), default_component_controller_.NewRequest(dispatcher()));
}
TestComponent* GetTestComponent() { return test_component_; }
static fuchsia::component::runner::ComponentStartInfo CreateValidStartInfo() {
fuchsia::component::runner::ComponentStartInfo start_info;
start_info.set_program(CreateValidProgram());
return start_info;
}
static fuchsia::data::Dictionary CreateValidProgram() {
fuchsia::data::Dictionary program;
fuchsia::data::DictionaryEntry entry;
entry.key = fctest::LOCAL_COMPONENT_NAME_KEY;
auto value = fuchsia::data::DictionaryValue::New();
value->set_str(kTestComponentName);
entry.value = std::move(value);
program.mutable_entries()->push_back(std::move(entry));
return program;
}
private:
std::unique_ptr<LocalComponentRunner> local_component_runner_ = nullptr;
TestComponent* test_component_ = nullptr;
fuchsia::component::runner::ComponentControllerPtr default_component_controller_;
fuchsia::component::runner::ComponentRunnerPtr runner_proxy_;
};
TEST_F(LocalComponentRunnerTest, RunnerStartsOnStartRequest) {
CallStart(CreateValidStartInfo());
RunLoop();
EXPECT_TRUE(GetTestComponent()->WasCalled());
}
class EchoImpl : public test::placeholders::Echo {
public:
void EchoString(::fidl::StringPtr value, EchoStringCallback callback) override {
callback(std::move(value));
}
};
TEST_F(LocalComponentRunnerTest, RunnerGivesComponentItsOutgoingDir) {
auto start_info = CreateValidStartInfo();
fuchsia::io::DirectoryPtr outgoing_directory_proxy;
start_info.set_outgoing_dir(outgoing_directory_proxy.NewRequest(dispatcher()));
EchoImpl echo_impl;
test::placeholders::EchoPtr echo_proxy;
fidl::Binding<test::placeholders::Echo> echo_binding(&echo_impl);
CallStart(std::move(start_info));
RunLoop();
ASSERT_EQ(GetTestComponent()->outgoing()->AddPublicService<test::placeholders::Echo>(
[&](fidl::InterfaceRequest<test::placeholders::Echo> request) {
echo_binding.Bind(std::move(request));
}),
ZX_OK);
echo_proxy.Bind(echo_binding.NewBinding(dispatcher()));
bool echoed = false;
echo_proxy->EchoString("hello", [&](fidl::StringPtr _) { echoed = true; });
RunLoopUntil([&]() { return echoed; });
EXPECT_TRUE(echoed);
}
TEST_F(LocalComponentRunnerTest, RunnerGivesComponentItsNamespace) {
static constexpr char kNamespacePath[] = "/test/path";
auto start_info = CreateValidStartInfo();
fuchsia::component::runner::ComponentNamespaceEntry ns_entry;
ns_entry.set_path(kNamespacePath);
zx::channel e1, e2;
ASSERT_EQ(zx::channel::create(0, &e1, &e2), ZX_OK);
// We'll provide a valid channel to and verify that it's present down below.
ns_entry.set_directory(fidl::InterfaceHandle<fuchsia::io::Directory>(std::move(e2)));
start_info.mutable_ns()->push_back(std::move(ns_entry));
CallStart(std::move(start_info));
RunLoop();
EXPECT_TRUE(fdio_ns_is_bound(GetTestComponent()->ns(), kNamespacePath));
}
/*
* Tests for |component_testing::internal::Convert*| functions.
*/
template <typename InputType, typename OutputType>
class ConvertParameterizedFixture
: public testing::TestWithParam<std::pair<InputType, std::shared_ptr<OutputType>>> {
protected:
InputType GetInput(const InputType& input) { return input; }
};
#define ZX_COMPONENT_TYPED_TEST_P(test_suite_name, method_name) \
TEST_P(test_suite_name, method_name) { \
auto param = GetParam(); \
auto actual = ConvertToFidl(GetInput(param.first)); \
auto expected = std::move(*param.second); \
EXPECT_TRUE(fidl::Equals(actual, expected)); \
}
// The parameter type must be a copyable type per
// http://google.github.io/googletest/advanced.html#how-to-write-value-parameterized-tests.
// Since at least one member of `ChildOptions` is not copyable, we need to work around this.
// A `std::shared_ptr` is copyable but must be dereferenced to access the value.
// `GetInput()` abstracts the dereferencing when necessary.
class ConvertChildOptionsParameterizedFixture
: public ConvertParameterizedFixture<std::shared_ptr<ChildOptions>, fctest::ChildOptions> {
protected:
const ChildOptions& GetInput(std::shared_ptr<ChildOptions> input) { return *input; }
};
std::vector<fcdecl::ConfigOverride> GetTestConfigOverrides() {
std::vector<fuchsia::component::decl::ConfigOverride> result;
result.emplace_back();
result.back().set_key("baz");
result.back().set_value(
fcdecl::ConfigValue::WithSingle(::fcdecl::ConfigSingleValue::WithUint64(1234u)));
result.emplace_back();
result.back().set_key("bat");
result.back().set_value(
fcdecl::ConfigValue::WithSingle(::fcdecl::ConfigSingleValue::WithInt8(-100)));
// Add the same key with a different value. There are no checks for duplicates.
result.emplace_back();
result.back().set_key(result[0].key());
result.back().set_value(
fcdecl::ConfigValue::WithSingle(::fcdecl::ConfigSingleValue::WithString("hello")));
return result;
}
std::vector<std::pair<std::string, fcdecl::ConfigValue>> GetExpectedConfigOverrides() {
std::vector<std::pair<std::string, fcdecl::ConfigValue>> result;
result.emplace_back(std::string("baz"), fcdecl::ConfigValue::WithSingle(
::fcdecl::ConfigSingleValue::WithUint64(1234u)));
result.emplace_back(std::string("bat"),
fcdecl::ConfigValue::WithSingle(::fcdecl::ConfigSingleValue::WithInt8(-100)));
result.emplace_back(std::string("baz"), fcdecl::ConfigValue::WithSingle(
::fcdecl::ConfigSingleValue::WithString("hello")));
return result;
}
ZX_COMPONENT_TYPED_TEST_P(ConvertChildOptionsParameterizedFixture, ConvertsAllFields)
INSTANTIATE_TEST_SUITE_P(
ConvertTest, ConvertChildOptionsParameterizedFixture,
testing::Values(
std::make_pair(std::shared_ptr<ChildOptions>(new ChildOptions{
.startup_mode = StartupMode::EAGER,
.environment = "foo",
.config_overrides = GetTestConfigOverrides()}),
component::tests::CreateFidlChildOptions(StartupMode::EAGER, "foo",
GetExpectedConfigOverrides())),
std::make_pair(std::shared_ptr<ChildOptions>(new ChildOptions{
.startup_mode = StartupMode::LAZY, .environment = "bar"}),
component::tests::CreateFidlChildOptions(StartupMode::LAZY, "bar", {}))));
class ConvertRefParameterizedFixture : public ConvertParameterizedFixture<Ref, fcdecl::Ref> {};
ZX_COMPONENT_TYPED_TEST_P(ConvertRefParameterizedFixture, ConvertsAllFields)
INSTANTIATE_TEST_SUITE_P(
ConvertTest, ConvertRefParameterizedFixture,
testing::Values(
std::make_pair(Ref{ParentRef{}},
std::make_shared<fcdecl::Ref>(fcdecl::Ref::WithParent(fcdecl::ParentRef{}))),
std::make_pair(ChildRef{.name = "foo"}, component::tests::CreateFidlChildRef("foo"))));
class ConvertCapabilityParameterizedFixture
: public ConvertParameterizedFixture<Capability, fctest::Capability> {};
ZX_COMPONENT_TYPED_TEST_P(ConvertCapabilityParameterizedFixture, ConvertsAllField)
INSTANTIATE_TEST_SUITE_P(
ConvertTest, ConvertCapabilityParameterizedFixture,
testing::Values(std::make_pair(Protocol{"foo"},
component::tests::CreateFidlProtocolCapability(/*name=*/"foo")),
std::make_pair(Protocol{.name = "foo",
.as = "bar",
.type = fcdecl::DependencyType::WEAK,
.path = "/foo/bar/baz",
.from_dictionary = "source/dict"},
component::tests::CreateFidlProtocolCapability(
/*name=*/"foo", /*as=*/"bar",
/*type=*/fcdecl::DependencyType::WEAK,
/*path=*/"/foo/bar/baz",
/*from_dictionary=*/"source/dict")),
std::make_pair(Service{"foo"},
component::tests::CreateFidlServiceCapability(/*name=*/"foo")),
std::make_pair(Service{.name = "foo",
.as = "bar",
.path = "/foo/bar/baz",
.from_dictionary = "source/dict"},
component::tests::CreateFidlServiceCapability(
/*name=*/"foo", /*as=*/"bar",
/*path=*/"/foo/bar/baz",
/*from_dictionary=*/"source/dict")),
std::make_pair(Directory{"foo"},
component::tests::CreateFidlDirectoryCapability(/*name=*/"foo")),
std::make_pair(Directory{.name = "foo",
.as = "bar",
.type = fcdecl::DependencyType::WEAK,
.subdir = "baz",
.rights = fio::Operations::EXECUTE,
.path = "/foo/bar/baz",
.from_dictionary = "source/dict"},
component::tests::CreateFidlDirectoryCapability(
/*name=*/"foo", /*as=*/"bar",
/*type=*/fcdecl::DependencyType::WEAK,
/*subdir=*/"baz",
/*rights=*/fio::Operations::EXECUTE,
/*path=*/"/foo/bar/baz",
/*from_dictionary=*/"source/dict"))));
class ConvertTest : public testing::Test {};
TEST_F(ConvertTest, ToFidlVec) {
std::vector<fcdecl::Ref> actual_values =
ConvertToFidlVec<Ref, fcdecl::Ref>({ChildRef{"foo"}, ChildRef{"bar"}});
std::vector<std::shared_ptr<fcdecl::Ref>> expected_values = {
component::tests::CreateFidlChildRef("foo"), component::tests::CreateFidlChildRef("bar")};
ZX_ASSERT(actual_values.size() == expected_values.size());
for (size_t i = 0; i < expected_values.size(); ++i) {
auto& actual = actual_values[i];
auto& expected = (*expected_values[i]);
EXPECT_TRUE(fidl::Equals(actual, expected));
}
}
} // namespace