blob: b75a92d010cced65ded559acb2c18a00aa84fb1d [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 <fuchsia/modular/testing/cpp/fidl.h>
#include <lib/fsl/vmo/strings.h>
#include <lib/modular_test_harness/cpp/fake_component.h>
#include <lib/modular_test_harness/cpp/test_harness_fixture.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/sys/cpp/testing/test_with_environment.h>
class TestHarnessFixtureTest : public modular::testing::TestHarnessFixture {};
// Test that InterceptBaseShell() generates a base shell URL and sets it up for
// interception.
TEST_F(TestHarnessFixtureTest, InterceptBaseShell) {
fuchsia::modular::testing::TestHarnessSpec spec;
auto url = InterceptBaseShell(&spec);
EXPECT_FALSE(url.empty());
EXPECT_EQ(url, spec.basemgr_config().base_shell().app_config().url());
}
// Test that InterceptSessionShell() generates a new session shell URL and sets
// it up for interception.
TEST_F(TestHarnessFixtureTest, InterceptSessionShell) {
fuchsia::modular::testing::TestHarnessSpec spec;
auto url = InterceptSessionShell(&spec);
EXPECT_FALSE(url.empty());
EXPECT_EQ(url, spec.basemgr_config()
.session_shell_map()
.at(0)
.config()
.app_config()
.url());
EXPECT_EQ(url, spec.components_to_intercept().at(0).component_url());
}
// Test that InterceptStoryShell() generates a story shell URL and sets it up
// for interception.
TEST_F(TestHarnessFixtureTest, InterceptStoryShell) {
fuchsia::modular::testing::TestHarnessSpec spec;
auto url = InterceptStoryShell(&spec);
EXPECT_FALSE(url.empty());
EXPECT_EQ(url, spec.basemgr_config().story_shell().app_config().url());
EXPECT_EQ(url, spec.components_to_intercept().at(0).component_url());
}
// Test that the TestHarnessBuilder builds a sane TestHarnessSpec and
// OnNewComponent router function.
TEST_F(TestHarnessFixtureTest, TestHarnessBuilderTest) {
modular::testing::TestHarnessBuilder builder;
std::string called;
builder.InterceptComponent(
[&](auto launch_info, auto handle) { called = "generic"; },
{.url = "generic", .sandbox_services = {"library.Protocol"}});
builder.InterceptBaseShell(
[&](auto launch_info, auto handle) { called = "base_shell"; },
{.url = "base_shell"});
builder.InterceptSessionShell(
[&](auto launch_info, auto handle) { called = "session_shell"; },
{.url = "session_shell"});
builder.InterceptStoryShell(
[&](auto launch_info, auto handle) { called = "story_shell"; },
{.url = "story_shell"});
auto spec = builder.BuildSpec();
EXPECT_EQ("generic", spec.components_to_intercept().at(0).component_url());
ASSERT_TRUE(spec.components_to_intercept().at(0).has_extra_cmx_contents());
std::string cmx_str;
ASSERT_TRUE(fsl::StringFromVmo(
spec.components_to_intercept().at(0).extra_cmx_contents(), &cmx_str));
EXPECT_EQ(R"({"sandbox":{"services":["library.Protocol"]}})", cmx_str);
EXPECT_EQ("base_shell", spec.components_to_intercept().at(1).component_url());
EXPECT_EQ("session_shell",
spec.components_to_intercept().at(2).component_url());
EXPECT_EQ("story_shell",
spec.components_to_intercept().at(3).component_url());
EXPECT_EQ("base_shell",
spec.basemgr_config().base_shell().app_config().url());
EXPECT_EQ("session_shell", spec.basemgr_config()
.session_shell_map()
.at(0)
.config()
.app_config()
.url());
EXPECT_EQ("story_shell",
spec.basemgr_config().story_shell().app_config().url());
auto handler = builder.BuildOnNewComponentHandler();
{
fuchsia::sys::StartupInfo startup_info;
startup_info.launch_info.url = "generic";
handler(std::move(startup_info), nullptr);
EXPECT_EQ("generic", called);
}
{
fuchsia::sys::StartupInfo startup_info;
startup_info.launch_info.url = "base_shell";
handler(std::move(startup_info), nullptr);
EXPECT_EQ("base_shell", called);
}
{
fuchsia::sys::StartupInfo startup_info;
startup_info.launch_info.url = "session_shell";
handler(std::move(startup_info), nullptr);
EXPECT_EQ("session_shell", called);
}
{
fuchsia::sys::StartupInfo startup_info;
startup_info.launch_info.url = "story_shell";
handler(std::move(startup_info), nullptr);
EXPECT_EQ("story_shell", called);
}
}
// Test that GenerateFakeUrl() returns new urls each time.
TEST_F(TestHarnessFixtureTest, GenerateFakeUrl) {
modular::testing::TestHarnessBuilder builder;
EXPECT_NE(builder.GenerateFakeUrl(), builder.GenerateFakeUrl());
}
// Test that the TestHarnessFixture is able to launch the modular runtime by
// asserting that we can intercept a base shell.
TEST_F(TestHarnessFixtureTest, SimpleSuccess) {
constexpr char kFakeBaseShellUrl[] =
"fuchsia-pkg://example.com/FAKE_BASE_SHELL_PKG/fake_base_shell.cmx";
// Setup base shell interception.
modular::testing::TestHarnessBuilder builder;
bool intercepted = false;
builder.InterceptBaseShell(
[&](fuchsia::sys::StartupInfo startup_info,
fidl::InterfaceHandle<fuchsia::modular::testing::InterceptedComponent>
component) {
ASSERT_EQ(kFakeBaseShellUrl, startup_info.launch_info.url);
intercepted = true;
},
{.url = kFakeBaseShellUrl});
test_harness().events().OnNewComponent = builder.BuildOnNewComponentHandler();
test_harness()->Run(builder.BuildSpec());
RunLoopUntil([&] { return intercepted; });
}
class TestComponent : public modular::testing::FakeComponent {
public:
TestComponent(fit::function<void()> on_created,
fit::function<void()> on_destroyed)
: on_created_(std::move(on_created)),
on_destroyed_(std::move(on_destroyed)) {}
protected:
void OnCreate(fuchsia::sys::StartupInfo startup_info) override {
on_created_();
}
void OnDestroy() override { on_destroyed_(); }
fit::function<void()> on_created_;
fit::function<void()> on_destroyed_;
};
// Tests that FakeComponent receives lifecycle events when it is killed
// by its parent.
TEST_F(TestHarnessFixtureTest, FakeComponentLifecycle_KilledByParent) {
modular::testing::TestHarnessBuilder builder;
bool running = false;
TestComponent session_shell([&] { running = true; },
[&] { running = false; });
builder.InterceptSessionShell(
session_shell.GetOnCreateHandler(),
{.url = builder.GenerateFakeUrl(),
.sandbox_services = {"fuchsia.modular.SessionShellContext"}});
test_harness().events().OnNewComponent = builder.BuildOnNewComponentHandler();
test_harness()->Run(builder.BuildSpec());
RunLoopUntil([&] { return session_shell.is_running(); });
EXPECT_TRUE(running);
fuchsia::modular::SessionShellContextPtr session_shell_context;
session_shell.component_context()->svc()->Connect(
session_shell_context.NewRequest());
session_shell_context->Logout();
RunLoopUntil([&] { return !session_shell.is_running(); });
EXPECT_FALSE(running);
}
// Tests that FakeComponent receives lifecycle events when it kills
// itself.
TEST_F(TestHarnessFixtureTest, FakeComponentLifecycle_KilledBySelf) {
modular::testing::TestHarnessBuilder builder;
bool running = false;
TestComponent base_shell([&] { running = true; }, [&] { running = false; });
builder.InterceptBaseShell(base_shell.GetOnCreateHandler(),
{.url = builder.GenerateFakeUrl()});
test_harness().events().OnNewComponent = builder.BuildOnNewComponentHandler();
test_harness()->Run(builder.BuildSpec());
RunLoopUntil([&] { return base_shell.is_running(); });
EXPECT_TRUE(running);
base_shell.Exit(0);
RunLoopUntil([&] { return !base_shell.is_running(); });
EXPECT_FALSE(running);
}