blob: 3ee3354c1e8d7b0e4ec9697acee13b6513478f39 [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 <utility>
#include <fuchsia/modular/cpp/fidl.h>
#include <fuchsia/sys/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <lib/component/cpp/startup_context.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fsl/vmo/strings.h>
#include <src/lib/fxl/command_line.h>
#include <src/lib/fxl/logging.h>
#include <src/lib/fxl/macros.h>
#include <src/lib/fxl/strings/string_number_conversions.h>
#include <trace-provider/provider.h>
#include <trace/event.h>
#include <trace/observer.h>
#include "peridot/lib/fidl/single_service_app.h"
#include "peridot/lib/testing/component_main.h"
#include "peridot/lib/testing/session_shell_impl.h"
#include "peridot/public/lib/integration_testing/cpp/reporting.h"
#include "peridot/public/lib/integration_testing/cpp/testing.h"
#include "peridot/tests/benchmarks/story/tracing_waiter.h"
namespace {
const char kStoryNamePrefix[] = "story-";
const char kRootModuleName[] = "root";
class Settings {
public:
explicit Settings(const fxl::CommandLine& command_line) {
const auto story_count_str =
command_line.GetOptionValueWithDefault("story_count", "1");
if (!fxl::StringToNumberWithError(story_count_str, &story_count)) {
FXL_LOG(ERROR) << "Unrecognized value [--story_count=" << story_count_str
<< "]: Using 0.";
}
module_url = command_line.GetOptionValueWithDefault(
"module_url",
"fuchsia-pkg://fuchsia.com/modular_benchmark_story_module#meta/"
"modular_benchmark_story_module.cmx");
}
int story_count{0};
std::string module_url;
};
// A simple story watcher implementation that invokes a "continue" callback when
// it sees the watched story transition to the given state. Used to push the
// test sequence forward when the test story reaches the next state.
class StoryWatcherImpl : fuchsia::modular::StoryWatcher {
public:
StoryWatcherImpl() : binding_(this) {}
~StoryWatcherImpl() 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());
}
// Deregisters itself from the watched story.
void Reset() { binding_.Unbind(); }
// Sets the function where to continue when the story is observed to be done.
void Continue(fuchsia::modular::StoryState state, fit::function<void()> at) {
continue_state_ = state;
continue_ = std::move(at);
}
private:
// |fuchsia::modular::StoryWatcher|
void OnStateChange(fuchsia::modular::StoryState state) override {
if (state != continue_state_) {
return;
}
continue_();
}
// |fuchsia::modular::StoryWatcher|
void OnModuleAdded(fuchsia::modular::ModuleData module_data) override {}
// |fuchsia::modular::StoryWatcher|
void OnModuleFocused(std::vector<std::string> module_path) override {}
fidl::Binding<fuchsia::modular::StoryWatcher> binding_;
fuchsia::modular::StoryState continue_state_{
fuchsia::modular::StoryState::STOPPED};
fit::function<void()> continue_{[] {}};
FXL_DISALLOW_COPY_AND_ASSIGN(StoryWatcherImpl);
};
// A simple link watcher implementation that invokes a "continue" callback when
// it sees the watched link change.
class LinkWatcherImpl : fuchsia::modular::LinkWatcher {
public:
LinkWatcherImpl() : binding_(this) {}
~LinkWatcherImpl() override = default;
// Registers itself as a watcher on the given link. Only one story at a time
// can be watched.
void Watch(fuchsia::modular::Link* const link) {
link->WatchAll(binding_.NewBinding());
}
// Deregisters itself from the watched story.
void Reset() { binding_.Unbind(); }
// Sets the function where to continue when the story is observed to be done.
void Continue(fit::function<void(fidl::StringPtr)> at) {
continue_ = std::move(at);
}
private:
// |fuchsia::modular::LinkWatcher|
void Notify(fuchsia::mem::Buffer value) override {
std::string json;
FXL_CHECK(fsl::StringFromVmo(value, &json));
continue_(json);
}
fidl::Binding<fuchsia::modular::LinkWatcher> binding_;
fit::function<void(fidl::StringPtr)> continue_{[](fidl::StringPtr) {}};
FXL_DISALLOW_COPY_AND_ASSIGN(LinkWatcherImpl);
};
// Measures timing the machinery available to a session shell implementation.
// This is invoked as a session shell from basemgr and executes a predefined
// sequence of steps, rather than to expose a UI to be driven by user
// interaction, as a session shell normally would.
class TestApp : public modular::ViewApp {
public:
TestApp(component::StartupContext* const startup_context, Settings settings)
: ViewApp(startup_context), settings_(std::move(settings)) {
startup_context->ConnectToEnvironmentService(
session_shell_context_.NewRequest());
session_shell_context_->GetStoryProvider(story_provider_.NewRequest());
startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest());
startup_context->outgoing().AddPublicService(
session_shell_impl_.GetHandler());
tracing_waiter_.WaitForTracing([this] { Loop(); });
}
~TestApp() override = default;
// Called by AppDriver in ComponentMain(). NOTE(mesch): Even though it
// overrides SingleServiceApp::Terminate(), it is called directly on TestApp
// by AppDriver, so it must not be private.
void Terminate(fit::function<void()> done) override {
// The corresponding BEGIN() call is in Loop(), below.
TRACE_ASYNC_END("benchmark", "user/logout", 0);
done();
}
private:
void Loop() {
if (story_count_ < settings_.story_count) {
FXL_LOG(INFO) << "Loop at " << story_count_ << " of "
<< settings_.story_count;
async::PostTask(async_get_default_dispatcher(),
[this] { StoryCreate(); });
} else {
TRACE_ASYNC_BEGIN("benchmark", "user/logout", 0);
session_shell_context_->Logout();
}
}
void StoryCreate() {
FXL_LOG(INFO) << "StoryCreate()";
TRACE_ASYNC_BEGIN("benchmark", "story/create", 0);
std::string story_name =
std::string(kStoryNamePrefix) + std::to_string(story_count_);
puppet_master_->ControlStory(story_name, story_puppet_master_.NewRequest());
std::vector<fuchsia::modular::StoryCommand> commands;
fuchsia::modular::AddMod add_mod;
add_mod.mod_name_transitional = "root";
add_mod.intent.handler = settings_.module_url;
add_mod.intent.action = "action";
fuchsia::modular::StoryCommand command;
command.set_add_mod(std::move(add_mod));
commands.push_back(std::move(command));
story_puppet_master_->Enqueue(std::move(commands));
story_puppet_master_->Execute(
[this, story_name](fuchsia::modular::ExecuteResult result) {
TRACE_ASYNC_END("benchmark", "story/create", 0);
StoryInfo(story_name);
});
}
void StoryInfo(fidl::StringPtr story_id) {
FXL_LOG(INFO) << "StoryInfo()";
story_provider_->GetController(story_id, story_controller_.NewRequest());
TRACE_ASYNC_BEGIN("benchmark", "story/info", 0);
story_controller_->GetInfo([this](fuchsia::modular::StoryInfo story_info,
fuchsia::modular::StoryState state) {
TRACE_ASYNC_END("benchmark", "story/info", 0);
Link();
});
}
void Link() {
FXL_LOG(INFO) << "Link()";
fuchsia::modular::LinkPath link_path = fuchsia::modular::LinkPath();
std::vector<std::string> root_module_path;
root_module_path.push_back(kRootModuleName);
link_path.module_path = std::move(root_module_path);
link_path.link_name = nullptr;
story_controller_->GetLink(std::move(link_path), link_.NewRequest());
link_watcher_.Watch(link_.get());
link_watcher_.Continue([this](fidl::StringPtr json) {
FXL_LOG(INFO) << "Link() Watch: " << json;
if (json == "") {
return;
}
const int count = fxl::StringToNumber<int>(json.get());
// Corresponding TRACE_FLOW_BEGIN() is in the module.
TRACE_FLOW_END("benchmark", "link/trans", count);
if (count == 100) {
StoryStop();
}
});
StoryStart();
}
void StoryStart() {
FXL_LOG(INFO) << "StoryStart()";
TRACE_ASYNC_BEGIN("benchmark", "story/start", 0);
story_watcher_.Continue(fuchsia::modular::StoryState::RUNNING, [this] {
TRACE_ASYNC_END("benchmark", "story/start", 0);
});
story_watcher_.Watch(story_controller_.get());
story_controller_->RequestStart();
}
void StoryStop() {
FXL_LOG(INFO) << "StoryStop()";
TRACE_ASYNC_BEGIN("benchmark", "story/stop", 0);
story_controller_->Stop([this] {
TRACE_ASYNC_END("benchmark", "story/stop", 0);
MaybeRepeat();
});
}
void MaybeRepeat() {
FXL_LOG(INFO) << "MaybeRepeat()";
story_watcher_.Reset();
story_controller_.Unbind();
story_count_++;
Loop();
}
modular::TracingWaiter tracing_waiter_;
const Settings settings_;
int story_count_{};
StoryWatcherImpl story_watcher_;
LinkWatcherImpl link_watcher_;
modular::testing::SessionShellImpl session_shell_impl_;
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::LinkPtr link_;
FXL_DISALLOW_COPY_AND_ASSIGN(TestApp);
};
} // namespace
int main(int argc, const char** argv) {
auto command_line = fxl::CommandLineFromArgcArgv(argc, argv);
Settings settings(command_line);
modular::testing::ComponentMain<TestApp, Settings>(std::move(settings));
return 0;
}