blob: cbeca89a8291cb17af3d84c947f57fcf41f15950 [file] [log] [blame]
// Copyright 2017 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 <fuchsia/ui/viewsv1token/cpp/fidl.h>
#include <lib/component/cpp/connect.h>
#include <lib/component/cpp/startup_context.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fxl/logging.h>
#include <lib/fxl/macros.h>
#include "peridot/lib/rapidjson/rapidjson.h"
#include "peridot/lib/testing/component_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/story_update/defs.h"
using modular::testing::TestPoint;
namespace {
const char kStoryName[] = "story";
// Tests how modules are updated in a story.
class TestApp : public modular::testing::ComponentBase<void> {
public:
TestApp(component::StartupContext* const startup_context)
: ComponentBase(startup_context) {
TestInit(__FILE__);
puppet_master_ =
startup_context
->ConnectToEnvironmentService<fuchsia::modular::PuppetMaster>();
session_shell_context_ = startup_context->ConnectToEnvironmentService<
fuchsia::modular::SessionShellContext>();
session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
CreateStory();
}
~TestApp() override = default;
private:
TestPoint story_create_{"Story Create"};
void CreateStory() {
fidl::VectorPtr<fuchsia::modular::StoryCommand> commands;
fuchsia::modular::AddMod add_mod;
add_mod.mod_name.push_back("root");
add_mod.intent.action = kCommonNullAction;
add_mod.intent.handler = kCommonNullModule;
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 root_running_{"Root Module RUNNING"};
void StartStory() {
story_provider_->GetController(kStoryName, story_controller_.NewRequest());
fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> story_view;
story_controller_->Start(story_view.NewRequest());
fidl::VectorPtr<fidl::StringPtr> module_path;
module_path.push_back("root");
story_controller_->GetModuleController(std::move(module_path),
module0_controller_.NewRequest());
module0_controller_.events().OnStateChange =
[this](fuchsia::modular::ModuleState module_state) {
if (module_state == fuchsia::modular::ModuleState::RUNNING) {
root_running_.Pass();
PipelinedAddGetStop();
}
};
}
TestPoint module1_stopped_{"Module1 STOPPED"};
TestPoint module1_gone_{"Module1 gone"};
void PipelinedAddGetStop() {
// Tests two invariants:
//
// 1. Pipelined fuchsia::modular::AddModule(), GetModuleController(),
// fuchsia::modular::ModuleController.Stop()
// transitions to the module state STOPPED.
//
// 2. After fuchsia::modular::ModuleController.Stop() completes (as observed
// by reaching teh
// STOPPED state), GetActiveModules() shows the module as not running.
// (This cannot be pipelined because the requests are on different
// existing connections.)
//
// TODO(mesch): The API as it is defined now does not allow to guarantee to
// observe a transition through the STARTING and RUNNING states. The
// implementation also makes no guarantees in the first place to await the
// module reaching RUNNING before it gets stopped, irrespective of
// observability of the state transitions.
//
// The observability of the STOPPED state, however, is guaranteed.
fuchsia::modular::Intent intent;
intent.handler = kCommonNullModule;
intent.action = kCommonNullAction;
story_controller_->AddModule(nullptr /* parent_module_path */, "module1",
std::move(intent),
nullptr /* surface_relation */);
fidl::VectorPtr<fidl::StringPtr> module_path;
module_path.push_back("module1");
module1_controller_.events().OnStateChange =
[this](fuchsia::modular::ModuleState new_state) {
if (new_state == fuchsia::modular::ModuleState::STOPPED) {
module1_stopped_.Pass();
}
};
story_controller_->GetModuleController(std::move(module_path),
module1_controller_.NewRequest());
module1_controller_->Stop([this] { GetActiveModules1(); });
}
void GetActiveModules1() {
story_controller_->GetActiveModules(
nullptr, [this](fidl::VectorPtr<fuchsia::modular::ModuleData> modules) {
if (modules->size() == 1) {
module1_gone_.Pass();
}
SequentialAddGetStop();
});
}
TestPoint module2_running_{"Module2 RUNNING"};
TestPoint module2_stopped_{"Module2 STOPPED"};
TestPoint module2_gone_{"Module2 gone"};
void SequentialAddGetStop() {
// Tests these invariants:
//
// 1. Pipelined fuchsia::modular::AddModule(), GetModuleController()
// transitions to the
// module state RUNNING.
//
// 2. Sequential (sequenced after RUNNING state is reached)
// fuchsia::modular::ModuleController.Stop() transitions to the module
// state STOPPED.
//
// 3. Sequential GetActiveModules() (sequenced after STOPPED state is
// reached) shows the module as not running.
//
// TODO(mesch): Like above, the API does not make guarantees to be able to
// observe the STARTING state. It only guarantees to observe the RUNNING
// state, and only if the module doesn't call Done() in its own.
//
// TODO(mesch): If the module calls Done() on its context (as
// common_done_module, for example, would), it is stopped by the story
// runner because it's a top level module. If this happens at the same time
// as this call, the callback may never invoked because it's preempted by
// the story runner handling the Done() request from the module. Instead,
// the controller connection is just closed, and flow of control would need
// to resume from the connection error handler of the module controller.
fuchsia::modular::Intent intent;
intent.handler = kCommonNullModule;
intent.action = kCommonNullAction;
story_controller_->AddModule(nullptr /* parent_module_path */, "module2",
std::move(intent),
nullptr /* surface_relation */);
fidl::VectorPtr<fidl::StringPtr> module_path;
module_path.push_back("module2");
module2_controller_.events().OnStateChange =
[this](fuchsia::modular::ModuleState module_state) {
if (module_state == fuchsia::modular::ModuleState::RUNNING) {
module2_running_.Pass();
module2_controller_->Stop([this] { GetActiveModules2(); });
} else if (module_state == fuchsia::modular::ModuleState::STOPPED) {
module2_stopped_.Pass();
}
};
story_controller_->GetModuleController(std::move(module_path),
module2_controller_.NewRequest());
}
void GetActiveModules2() {
story_controller_->GetActiveModules(
nullptr, [this](fidl::VectorPtr<fuchsia::modular::ModuleData> modules) {
if (modules->size() == 1) {
module2_gone_.Pass();
}
Logout();
});
}
void Logout() { session_shell_context_->Logout(); }
fuchsia::modular::PuppetMasterPtr puppet_master_;
fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
fuchsia::modular::SessionShellContextPtr session_shell_context_;
fuchsia::modular::StoryProviderPtr story_provider_;
fuchsia::modular::StoryControllerPtr story_controller_;
fuchsia::modular::StoryInfoPtr story_info_;
fuchsia::modular::ModuleControllerPtr module0_controller_;
fuchsia::modular::ModuleControllerPtr module1_controller_;
fuchsia::modular::ModuleControllerPtr module2_controller_;
FXL_DISALLOW_COPY_AND_ASSIGN(TestApp);
};
} // namespace
int main(int /*argc*/, const char** /*argv*/) {
modular::testing::ComponentMain<TestApp>();
return 0;
}