blob: 3d475aa6e259ebc9b57070f1e8280cd3454689b7 [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 <gmock/gmock.h>
#include "src/lib/fsl/vmo/strings.h"
#include "src/modular/lib/modular_test_harness/cpp/fake_module.h"
#include "src/modular/lib/modular_test_harness/cpp/fake_session_shell.h"
#include "src/modular/lib/modular_test_harness/cpp/test_harness_fixture.h"
using testing::ElementsAre;
namespace {
class ModuleContextTest : public modular_testing::TestHarnessFixture {
protected:
ModuleContextTest()
: session_shell_(modular_testing::FakeSessionShell::CreateWithDefaultOptions()) {}
void StartSession(modular_testing::TestHarnessBuilder builder) {
builder.InterceptSessionShell(session_shell_->BuildInterceptOptions());
builder.BuildAndRun(test_harness());
// Wait for our session shell to start.
RunLoopUntil([this] { return session_shell_->is_running(); });
}
void RestartStory(std::string story_name) {
fuchsia::modular::StoryControllerPtr story_controller;
session_shell_->story_provider()->GetController(story_name, story_controller.NewRequest());
bool restarted = false;
story_controller->Stop([&] {
story_controller->RequestStart();
restarted = true;
});
RunLoopUntil([&] { return restarted; });
}
private:
std::unique_ptr<modular_testing::FakeSessionShell> session_shell_;
};
// A version of FakeModule which captures handled intents in a std::vector<>
// and exposes callbacks triggered on certain lifecycle events.
class TestModule : public modular_testing::FakeModule {
public:
explicit TestModule(std::string module_name = "")
: modular_testing::FakeModule(
{.url = modular_testing::TestHarnessBuilder::GenerateFakeUrl(module_name),
.sandbox_services = modular_testing::FakeModule::GetDefaultSandboxServices()}) {}
fit::function<void()> on_destroy;
fit::function<void()> on_create;
fuchsia::modular::ModuleControllerPtr controller;
private:
// |modular_testing::FakeModule|
void OnCreate(fuchsia::sys::StartupInfo startup_info) override {
modular_testing::FakeModule::OnCreate(std::move(startup_info));
if (on_create)
on_create();
}
// |modular_testing::FakeModule|
void OnDestroy() override {
if (on_destroy)
on_destroy();
}
};
// Test that ModuleContext.AddModuleToStory() starts child modules and that
// calling it multiple times for the same child has different behavior if the
// Intent specifies the same handler, versus if it specifies a different
// handler.
TEST_F(ModuleContextTest, AddModuleToStory) {
modular_testing::TestHarnessBuilder builder;
TestModule parent_module("parent_module");
TestModule child_module1("child_module1");
TestModule child_module2("child_module2");
builder.InterceptComponent(parent_module.BuildInterceptOptions());
builder.InterceptComponent(child_module1.BuildInterceptOptions());
builder.InterceptComponent(child_module2.BuildInterceptOptions());
StartSession(std::move(builder));
modular_testing::AddModToStory(test_harness(), "storyname", "modname",
{.action = "action", .handler = parent_module.url()});
RunLoopUntil([&] { return parent_module.is_running(); });
// Add a single child module.
parent_module.module_context()->AddModuleToStory(
"childmodname", {.action = "action", .handler = child_module1.url()},
child_module1.controller.NewRequest(),
/*surface_relation=*/nullptr, [&](fuchsia::modular::StartModuleStatus status) {
ASSERT_EQ(status, fuchsia::modular::StartModuleStatus::SUCCESS);
});
RunLoopUntil([&] { return child_module1.is_running(); });
// Add the same module again but with a different Intent action.
bool child_module1_destroyed{false};
child_module1.on_destroy = [&] { child_module1_destroyed = true; };
parent_module.module_context()->AddModuleToStory(
"childmodname", {.action = "action2", .handler = child_module1.url()},
child_module1.controller.NewRequest(),
/*surface_relation=*/nullptr, [&](fuchsia::modular::StartModuleStatus status) {
ASSERT_EQ(status, fuchsia::modular::StartModuleStatus::SUCCESS);
});
RunLoopUntil([&] { return child_module1.is_running(); });
// At no time should the child module have been destroyed.
EXPECT_EQ(child_module1_destroyed, false);
// This time change the handler. Expect the first module to be shut down,
// and the second to run in its place.
parent_module.module_context()->AddModuleToStory(
"childmodname", {.action = "action", .handler = child_module2.url()},
child_module2.controller.NewRequest(),
/*surface_relation=*/nullptr, [&](fuchsia::modular::StartModuleStatus status) {
ASSERT_EQ(status, fuchsia::modular::StartModuleStatus::SUCCESS);
});
RunLoopUntil([&] { return child_module2.is_running(); });
EXPECT_FALSE(child_module1.is_running());
}
// Test that ModuleContext.RemoveSelfFromStory() has the affect of shutting
// down the module and removing it permanently from the story (if the story is
// restarted, it is not relaunched).
TEST_F(ModuleContextTest, RemoveSelfFromStory) {
modular_testing::TestHarnessBuilder builder;
TestModule module1("module1");
TestModule module2("module2");
builder.InterceptComponent(module1.BuildInterceptOptions());
builder.InterceptComponent(module2.BuildInterceptOptions());
StartSession(std::move(builder));
modular_testing::AddModToStory(test_harness(), "storyname", "modname1",
{.action = "action", .handler = module1.url()});
modular_testing::AddModToStory(test_harness(), "storyname", "modname2",
{.action = "action", .handler = module2.url()});
RunLoopUntil([&] { return module1.is_running() && module2.is_running(); });
// Instruct module1 to remove itself from the story. Expect to see that
// module1 is terminated and module2 is not.
module1.module_context()->RemoveSelfFromStory();
RunLoopUntil([&] { return !module1.is_running(); });
ASSERT_TRUE(module2.is_running());
// Additionally, restarting the story should not result in module1 being
// restarted whereas it should for module2.
bool module2_destroyed = false;
bool module2_restarted = false;
module2.on_destroy = [&] { module2_destroyed = true; };
module2.on_create = [&] { module2_restarted = true; };
RestartStory("storyname");
RunLoopUntil([&] { return module2_restarted; });
EXPECT_FALSE(module1.is_running());
EXPECT_TRUE(module2_destroyed);
}
} // namespace