blob: 4e12d366112ece89a1a1b32ad66dde54bed8d0a7 [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 "lib/syslog/cpp/macros.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; });
}
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;
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();
}
};
class TestStoryWatcher : fuchsia::modular::StoryWatcher {
public:
TestStoryWatcher(fit::function<void(fuchsia::modular::StoryState)> on_state_change)
: binding_(this), on_state_change_(std::move(on_state_change)) {}
~TestStoryWatcher() override = default;
// Registers itself as a watcher on the given story. Only one story at a time
// can be watched.
void Watch(fuchsia::modular::StoryController* const story_controller) {
story_controller->Watch(binding_.NewBinding());
}
private:
// |fuchsia::modular::StoryWatcher|
void OnStateChange(fuchsia::modular::StoryState state) override { on_state_change_(state); }
void OnModuleAdded(fuchsia::modular::ModuleData /*module_data*/) override {}
void OnModuleFocused(std::vector<std::string> /*module_path*/) override {}
fidl::Binding<fuchsia::modular::StoryWatcher> binding_;
fit::function<void(fuchsia::modular::StoryState)> on_state_change_;
};
// Test that ModuleContext.RemoveSelfFromStory() on the only mod in a story 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) {
TestModule module1("module1");
modular_testing::TestHarnessBuilder builder;
builder.InterceptComponent(module1.BuildInterceptOptions());
StartSession(std::move(builder));
modular_testing::AddModToStory(test_harness(), "storyname", "modname1",
{.action = "action", .handler = module1.url()});
RunLoopUntil([&] { return module1.is_running(); });
// Instruct module1 to remove itself from the story. Expect to see that
// module1 is terminated.
module1.module_context()->RemoveSelfFromStory();
RunLoopUntil([&] { return !module1.is_running(); });
// Additionally, restarting the story should not result in module1 being
// restarted.
fuchsia::modular::StoryControllerPtr story_controller;
session_shell_->story_provider()->GetController("storyname", story_controller.NewRequest());
bool story_stopped = false;
bool story_restarted = false;
TestStoryWatcher story_watcher([&](fuchsia::modular::StoryState state) {
if (state == fuchsia::modular::StoryState::STOPPED) {
story_stopped = true;
} else if (state == fuchsia::modular::StoryState::RUNNING) {
story_restarted = true;
}
});
story_watcher.Watch(story_controller.get());
RestartStory("storyname");
RunLoopUntil([&] { return story_stopped && story_restarted; });
EXPECT_FALSE(module1.is_running());
}
// Test that when ModuleContext.RemoveSelfFromStory() is called on one of two
// modules in a story, it 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_2mods) {
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