blob: 66d9fb7e7ca506a1dd3c0e3dfddb1393563b899a [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/zx/channel.h>
#include <zircon/status.h>
#include <cstddef>
#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 LocalComponent {
public:
explicit TestComponent(fit::closure quit_loop) : quit_loop_(std::move(quit_loop)) {}
void Start(std::unique_ptr<LocalComponentHandles> mock_handles) override {
mock_handles_ = std::move(mock_handles);
called_ = true;
quit_loop_();
}
LocalComponentHandles* GetMockHandles() { return mock_handles_.get(); }
bool WasCalled() const { return called_; }
private:
fit::closure quit_loop_;
std::unique_ptr<LocalComponentHandles> mock_handles_ = nullptr;
bool called_ = false;
};
class LocalComponentRunnerTest : public gtest::RealLoopFixture {
public:
void SetUp() override {
auto builder = LocalComponentRunner::Builder();
test_component_ = std::make_unique<TestComponent>(QuitLoopClosure());
builder.Register(kTestComponentName, test_component_.get());
local_component_runner_ = builder.Build(dispatcher());
runner_proxy_.Bind(local_component_runner_->NewBinding());
}
void TearDown() override {
local_component_runner_.reset();
test_component_.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_.get(); }
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;
std::unique_ptr<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();
auto mock_handles = GetTestComponent()->GetMockHandles();
ASSERT_TRUE(mock_handles != nullptr);
ASSERT_EQ(mock_handles->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();
auto mock_handles = GetTestComponent()->GetMockHandles();
ASSERT_TRUE(mock_handles != nullptr);
EXPECT_TRUE(fdio_ns_is_bound(mock_handles->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>>> {};
#define ZX_COMPONENT_TYPED_TEST_P(test_suite_name, method_name) \
TEST_P(test_suite_name, method_name) { \
auto param = GetParam(); \
auto actual = ConvertToFidl(param.first); \
auto expected = std::move(*param.second); \
EXPECT_TRUE(fidl::Equals(actual, expected)); \
}
class ConvertChildOptionsParameterizedFixture
: public ConvertParameterizedFixture<ChildOptions, fctest::ChildOptions> {};
ZX_COMPONENT_TYPED_TEST_P(ConvertChildOptionsParameterizedFixture, ConvertsAllFields)
INSTANTIATE_TEST_SUITE_P(
ConvertTest, ConvertChildOptionsParameterizedFixture,
testing::Values(
std::make_pair(ChildOptions{.startup_mode = StartupMode::EAGER, .environment = "foo"},
component::tests::CreateFidlChildOptions(StartupMode::EAGER, "foo")),
std::make_pair(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"},
component::tests::CreateFidlProtocolCapability(/*name=*/"foo", /*as=*/"bar",
/*type=*/fcdecl::DependencyType::WEAK,
/*path=*/"/foo/bar/baz")),
std::make_pair(Service{"foo"},
component::tests::CreateFidlServiceCapability(/*name=*/"foo")),
std::make_pair(Service{.name = "foo", .as = "bar", .path = "/foo/bar/baz"},
component::tests::CreateFidlServiceCapability(/*name=*/"foo", /*as=*/"bar",
/*path=*/"/foo/bar/baz")),
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"},
component::tests::CreateFidlDirectoryCapability(/*name=*/"foo", /*as=*/"bar",
/*type=*/fcdecl::DependencyType::WEAK,
/*subdir=*/"baz",
/*rights=*/fio::Operations::EXECUTE,
/*path=*/"/foo/bar/baz"))));
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