blob: 770a9d0a3654de10b8ef54154c9fe5b1e25c3cb4 [file] [log] [blame]
// Copyright 2018 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 <memory>
#include <fuchsia/modular/cpp/fidl.h>
#include <lib/callback/scoped_callback.h>
#include <lib/component/cpp/connect.h>
#include <lib/component/cpp/startup_context.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fsl/vmo/strings.h>
#include <src/lib/fxl/logging.h>
#include <src/lib/fxl/macros.h>
#include "peridot/lib/rapidjson/rapidjson.h"
#include "peridot/lib/testing/component_main.h"
#include "peridot/lib/testing/session_shell_base.h"
#include "peridot/public/lib/integration_testing/cpp/reporting.h"
#include "peridot/public/lib/integration_testing/cpp/testing.h"
#include "peridot/tests/common/defs.h"
#include "peridot/tests/module_context/defs.h"
using ::modular::testing::Await;
using ::modular::testing::Signal;
using ::modular::testing::TestPoint;
namespace {
const char kStoryName[] = "story";
// A simple story activity watcher implementation.
class StoryActivityWatcherImpl : fuchsia::modular::StoryActivityWatcher {
public:
StoryActivityWatcherImpl()
: binding_(this),
on_notify_([](std::string,
std::vector<fuchsia::modular::OngoingActivityType>) {}) {}
~StoryActivityWatcherImpl() override = default;
void Watch(fuchsia::modular::StoryProvider* const story_provider) {
story_provider->WatchActivity(binding_.NewBinding());
}
void OnNotify(
fit::function<void(std::string,
std::vector<fuchsia::modular::OngoingActivityType>)>
on_notify) {
on_notify_ = std::move(on_notify);
}
private:
// |fuchsia::modular::StoryActivityWatcher|
void OnStoryActivityChange(
std::string story_id,
std::vector<fuchsia::modular::OngoingActivityType> activities) override {
on_notify_(std::move(story_id), std::move(activities));
}
fidl::Binding<fuchsia::modular::StoryActivityWatcher> binding_;
fit::function<void(std::string,
std::vector<fuchsia::modular::OngoingActivityType>)>
on_notify_;
FXL_DISALLOW_COPY_AND_ASSIGN(StoryActivityWatcherImpl);
};
class TestApp : public modular::testing::SessionShellBase {
public:
TestApp(component::StartupContext* const startup_context)
: SessionShellBase(startup_context), weak_ptr_factory_(this) {
TestInit(__FILE__);
startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
CreateStory();
}
~TestApp() override = default;
private:
TestPoint story_create_{"Created story."};
void CreateStory() {
std::vector<fuchsia::modular::StoryCommand> commands;
{
fuchsia::modular::AddMod add_mod;
add_mod.mod_name_transitional = kFirstModuleName;
add_mod.intent = IntentWithParameterString(kFirstModuleName);
fuchsia::modular::StoryCommand command;
command.set_add_mod(std::move(add_mod));
commands.push_back(std::move(command));
}
{
fuchsia::modular::AddMod add_mod;
add_mod.mod_name_transitional = kSecondModuleName;
add_mod.intent = IntentWithParameterString(kSecondModuleName);
fuchsia::modular::StoryCommand command;
command.set_add_mod(std::move(add_mod));
commands.push_back(std::move(command));
}
{
fuchsia::modular::Intent intent;
intent.handler = kEntityModuleUrl;
intent.action = kEntityIntentAction;
fuchsia::modular::AddMod add_mod;
add_mod.mod_name_transitional = "entity_module";
add_mod.intent = std::move(intent);
add_mod.surface_parent_mod_name.resize(0);
fuchsia::modular::StoryCommand command;
command.set_add_mod(std::move(add_mod));
commands.push_back(std::move(command));
}
puppet_master_->ControlStory(kStoryName, story_puppet_master_.NewRequest());
story_puppet_master_->Enqueue(std::move(commands));
story_puppet_master_->Execute(
[this](fuchsia::modular::ExecuteResult result) {
story_create_.Pass();
StartStory();
});
}
TestPoint story_get_controller_{"Story GetController()"};
// Starts the story and adds two modules to it.
void StartStory() {
story_provider()->GetController(kStoryName, story_controller_.NewRequest());
story_controller_.set_error_handler([](zx_status_t status) {
FXL_LOG(ERROR) << "Story controller for story " << kStoryName
<< " died. Does this story exist?";
});
story_controller_->RequestStart();
story_controller_->GetInfo(
[this](fuchsia::modular::StoryInfo, fuchsia::modular::StoryState) {
story_get_controller_.Pass();
PerformWatchActivity();
});
}
TestPoint on_watch_ongoing_activities_dispatched{
"When a watcher is registered, ongoing activities should be dispatched."};
void PerformWatchActivity() {
story_activity_watcher_.Watch(story_provider());
story_activity_watcher_.OnNotify(
[this](std::string story_id,
std::vector<fuchsia::modular::OngoingActivityType> activities) {
if (story_id == kStoryName && activities.empty()) {
on_watch_ongoing_activities_dispatched.Pass();
}
PerformFirstModuleStartActivity();
});
}
TestPoint on_start_ongoing_activity_dispatched{
"When there is a new ongoing activity, the ongoing activity should be "
"dispatched."};
// Signals the first module to call ModuleContext.StartOngoingActivity().
void PerformFirstModuleStartActivity() {
Signal(kFirstModuleCallStartActivity);
story_activity_watcher_.OnNotify(
[this](std::string story_id,
std::vector<fuchsia::modular::OngoingActivityType> activities) {
if (story_id == kStoryName && activities.size() == 1 &&
activities[0] == fuchsia::modular::OngoingActivityType::VIDEO) {
on_start_ongoing_activity_dispatched.Pass();
}
PerformSecondModuleStartActivity();
});
}
TestPoint on_start_all_ongoing_activities_dispatched{
"When there is a new ongoing activity, all ongoing activities should be "
"dispatched."};
// Signals the second module to call ModuleContext.StartOngoingActivity().
void PerformSecondModuleStartActivity() {
Signal(kSecondModuleCallStartActivity);
story_activity_watcher_.OnNotify(
[this](std::string story_id,
std::vector<fuchsia::modular::OngoingActivityType> activities) {
if (story_id == kStoryName && activities.size() == 2 &&
activities[0] == fuchsia::modular::OngoingActivityType::VIDEO &&
activities[1] == fuchsia::modular::OngoingActivityType::VIDEO) {
on_start_all_ongoing_activities_dispatched.Pass();
}
PerformSecondModuleStopActivity();
});
}
TestPoint on_stop_remaining_ongoing_activities_dispatched{
"When an ongoing activity is stopped, all remaining ongoing activities "
"should be dispatched."};
// Signals the second module to stop ongoing activity.
void PerformSecondModuleStopActivity() {
Signal(kSecondModuleCallStopActivity);
story_activity_watcher_.OnNotify(
[this](std::string story_id,
std::vector<fuchsia::modular::OngoingActivityType> activities) {
if (story_id == kStoryName && activities.size() == 1 &&
activities[0] == fuchsia::modular::OngoingActivityType::VIDEO) {
on_stop_remaining_ongoing_activities_dispatched.Pass();
}
TestModuleCreatingEntity();
});
}
void TestModuleCreatingEntity() {
Await(kEntityModuleDoneFirstTask, [this] {
Await(kEntityModuleDoneSecondTask, [this] {
fuchsia::modular::Intent intent;
intent.handler = kEntityModuleUrl;
intent.action = kEntityIntentAction;
fuchsia::modular::RemoveMod remove_mod;
remove_mod.mod_name_transitional = "entity_module";
std::vector<fuchsia::modular::StoryCommand> commands;
fuchsia::modular::StoryCommand command;
command.set_remove_mod(std::move(remove_mod));
commands.push_back(std::move(command));
puppet_master_->ControlStory(kStoryName,
story_puppet_master_.NewRequest());
story_puppet_master_->Enqueue(std::move(commands));
story_puppet_master_->Execute(
[this](fuchsia::modular::ExecuteResult result) {
PerformFirstModuleDone();
});
});
});
}
TestPoint on_done_ongoing_activities_stopped{
"When a module is teared down, the ongoing activity should also be "
"stopped"};
TestPoint second_module_active_{
"Only second module is still active after first calls "
"RemoveSelfFromStory()"};
// Signals the first module to call ModuleContext.RemoveSelfFromStory().
void PerformFirstModuleDone() {
Signal(kFirstModuleCallDone);
Await(kFirstModuleTerminated, [this] {
// Verify that the second module is still active, but the first one is
// not.
story_controller_->GetActiveModules(
[this](std::vector<fuchsia::modular::ModuleData> module_data) {
if (module_data.size() == 1) {
second_module_active_.Pass();
}
VerifyStoryStillRunning();
});
});
story_activity_watcher_.OnNotify(
[this](std::string story_id,
std::vector<fuchsia::modular::OngoingActivityType> activities) {
if (story_id == kStoryName && activities.empty()) {
on_done_ongoing_activities_stopped.Pass();
}
});
}
TestPoint story_still_active_{
"The story is still active after first module calls "
"RemoveSelfFromStory()"};
// Verifies that the story is still running after the first module has called
// done and been stopped.
void VerifyStoryStillRunning() {
IsStoryRunning([this](bool is_running) {
if (is_running) {
story_still_active_.Pass();
}
PerformSecondModuleDone();
});
}
TestPoint no_module_active_{
"No modules are active after second mod calls RemoveSelfFromStory()"};
TestPoint story_stopped_{"The story was stopped."};
// Signals the second module to call ModuleContext.Done.
void PerformSecondModuleDone() {
Signal(kSecondModuleCallDone);
Await(kSecondModuleTerminated, [this] {
// Verify that the second module is still active.
story_controller_->GetActiveModules(
[this](std::vector<fuchsia::modular::ModuleData> module_data) {
if (module_data.empty()) {
no_module_active_.Pass();
}
IsStoryRunning([this](bool is_running) {
if (!is_running) {
story_stopped_.Pass();
}
Signal(modular::testing::kTestShutdown);
});
});
});
}
// Verifies that the story is stopped when the last module that is part of the
// story calls ModuleContext.Done and is stopped.
void IsStoryRunning(fit::function<void(bool)> callback) {
story_controller_->GetInfo(
[callback = std::move(callback)](fuchsia::modular::StoryInfo story_info,
fuchsia::modular::StoryState state) {
callback(state == fuchsia::modular::StoryState::RUNNING);
});
}
// Creates an intent with one parameter, kLinkName, with the following
// contents: { |kLinkKey| : |parameter_string| }.
fuchsia::modular::Intent IntentWithParameterString(
std::string parameter_string) {
fuchsia::modular::Intent intent;
intent.handler = kModuleUrl;
intent.action = kIntentAction;
fuchsia::modular::IntentParameter parameter;
parameter.name = kLinkName;
rapidjson::Document document;
document.SetObject();
document.AddMember(kLinkKey, parameter_string, document.GetAllocator());
fuchsia::modular::IntentParameterData parameter_data;
fsl::SizedVmo vmo;
FXL_CHECK(fsl::VmoFromString(modular::JsonValueToString(document), &vmo));
parameter_data.set_json(std::move(vmo).ToTransport());
parameter.data = std::move(parameter_data);
intent.parameters.push_back(std::move(parameter));
return intent;
}
fuchsia::modular::PuppetMasterPtr puppet_master_;
fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
fuchsia::modular::StoryControllerPtr story_controller_;
StoryActivityWatcherImpl story_activity_watcher_;
fxl::WeakPtrFactory<TestApp> weak_ptr_factory_;
FXL_DISALLOW_COPY_AND_ASSIGN(TestApp);
};
} // namespace
int main(int /*argc*/, const char** /*argv*/) {
modular::testing::ComponentMain<TestApp>();
return 0;
}