blob: 665104c5af864bd26c6f532d68398aa471c23e4c [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 <lib/fsl/vmo/strings.h>
#include <lib/modular_test_harness/cpp/test_harness_fixture.h>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
namespace modular {
namespace testing {
namespace {
const char kTestHarnessUrl[] =
"fuchsia-pkg://fuchsia.com/modular_test_harness#meta/"
"modular_test_harness.cmx";
std::string BuildExtraCmx(TestHarnessBuilder::InterceptOptions options) {
if (options.sandbox_services.empty())
return "";
rapidjson::Document cmx;
cmx.SetObject();
rapidjson::Value sandbox;
sandbox.SetObject();
rapidjson::Value services;
services.SetArray();
for (const auto& service : options.sandbox_services) {
rapidjson::Value v(service, cmx.GetAllocator());
services.PushBack(v.Move(), cmx.GetAllocator());
}
sandbox.AddMember("services", services.Move(), cmx.GetAllocator());
cmx.AddMember("sandbox", sandbox.Move(), cmx.GetAllocator());
rapidjson::StringBuffer buf;
rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
cmx.Accept(writer);
return buf.GetString();
}
bool BufferFromString(std::string str, fuchsia::mem::Buffer* buffer) {
ZX_ASSERT(buffer != nullptr);
uint64_t num_bytes = str.size();
zx::vmo vmo;
zx_status_t status = zx::vmo::create(num_bytes, 0u, &vmo);
if (status < 0) {
return false;
}
if (num_bytes > 0) {
status = vmo.write(str.data(), 0, num_bytes);
if (status < 0) {
return false;
}
}
buffer->vmo = std::move(vmo);
buffer->size = num_bytes;
return true;
}
} // namespace
fuchsia::modular::testing::TestHarnessSpec TestHarnessBuilder::BuildSpec() {
return std::move(spec_);
}
TestHarnessBuilder::OnNewComponentHandler
TestHarnessBuilder::BuildOnNewComponentHandler() {
return
[handlers = std::move(handlers_)](
fuchsia::sys::StartupInfo startup_info,
fidl::InterfaceHandle<fuchsia::modular::testing::InterceptedComponent>
component) {
auto it = handlers.find(startup_info.launch_info.url);
if (it == handlers.end()) {
ZX_ASSERT_MSG(false, "Unexpected component URL: %s",
startup_info.launch_info.url.c_str());
}
it->second(std::move(startup_info), component.Bind());
};
}
TestHarnessBuilder& TestHarnessBuilder::InterceptComponent(
OnNewComponentHandler on_new_component, InterceptOptions options) {
ZX_ASSERT(on_new_component);
if (options.url.empty()) {
options.url = GenerateFakeUrl();
}
fuchsia::modular::testing::InterceptSpec intercept_spec;
intercept_spec.set_component_url(options.url);
auto extra_cmx_contents = BuildExtraCmx(options);
if (!extra_cmx_contents.empty()) {
ZX_ASSERT(BufferFromString(extra_cmx_contents,
intercept_spec.mutable_extra_cmx_contents()));
}
spec_.mutable_components_to_intercept()->push_back(std::move(intercept_spec));
handlers_.insert(std::make_pair(options.url, std::move(on_new_component)));
return *this;
}
TestHarnessBuilder& TestHarnessBuilder::InterceptBaseShell(
OnNewComponentHandler on_new_component, InterceptOptions options) {
if (options.url.empty()) {
options.url = GenerateFakeUrl();
}
auto url = options.url;
InterceptComponent(std::move(on_new_component), std::move(options));
spec_.mutable_basemgr_config()
->mutable_base_shell()
->mutable_app_config()
->set_url(url);
return *this;
}
TestHarnessBuilder& TestHarnessBuilder::InterceptSessionShell(
OnNewComponentHandler on_new_component, InterceptOptions options) {
if (options.url.empty()) {
options.url = GenerateFakeUrl();
}
auto url = options.url;
InterceptComponent(std::move(on_new_component), std::move(options));
fuchsia::modular::session::SessionShellMapEntry entry;
entry.mutable_config()->mutable_app_config()->set_url(url);
spec_.mutable_basemgr_config()->mutable_session_shell_map()->push_back(
std::move(entry));
return *this;
}
TestHarnessBuilder& TestHarnessBuilder::InterceptStoryShell(
OnNewComponentHandler on_new_component, InterceptOptions options) {
if (options.url.empty()) {
options.url = GenerateFakeUrl();
}
auto url = options.url;
InterceptComponent(std::move(on_new_component), std::move(options));
spec_.mutable_basemgr_config()
->mutable_story_shell()
->mutable_app_config()
->set_url(url);
return *this;
}
std::string TestHarnessBuilder::GenerateFakeUrl() const {
uint32_t random_number = 0;
zx_cprng_draw(&random_number, sizeof random_number);
std::string rand_str = std::to_string(random_number);
// Since we cannot depend on utlitites outside of the stdlib and SDK, here
// is a quick way to format a string safely.
std::string url;
url = "fuchsia-pkg://example.com/GENERATED_URL_";
url += rand_str;
url += "#meta/GENERATED_URL_";
url += rand_str;
url += ".cmx";
return url;
}
TestHarnessFixture::~TestHarnessFixture() = default;
std::string TestHarnessFixture::InterceptBaseShell(
fuchsia::modular::testing::TestHarnessSpec* spec) const {
auto url = TestHarnessBuilder().GenerateFakeUrl();
spec->mutable_basemgr_config()
->mutable_base_shell()
->mutable_app_config()
->set_url(url);
fuchsia::modular::testing::InterceptSpec intercept_spec;
intercept_spec.set_component_url(url);
spec->mutable_components_to_intercept()->push_back(std::move(intercept_spec));
return url;
}
std::string TestHarnessFixture::InterceptSessionShell(
fuchsia::modular::testing::TestHarnessSpec* spec,
std::string extra_cmx_contents) const {
auto url = TestHarnessBuilder().GenerateFakeUrl();
// 1. Add session shell to modular config.
{
fuchsia::modular::session::SessionShellMapEntry entry;
entry.mutable_config()->mutable_app_config()->set_url(url);
spec->mutable_basemgr_config()->mutable_session_shell_map()->push_back(
std::move(entry));
}
// 2. Set up interception for session shell.
fuchsia::modular::testing::InterceptSpec shell_intercept_spec;
if (!extra_cmx_contents.empty()) {
ZX_ASSERT(BufferFromString(
extra_cmx_contents, shell_intercept_spec.mutable_extra_cmx_contents()));
}
shell_intercept_spec.set_component_url(url);
spec->mutable_components_to_intercept()->push_back(
std::move(shell_intercept_spec));
return url;
}
std::string TestHarnessFixture::InterceptStoryShell(
fuchsia::modular::testing::TestHarnessSpec* spec) const {
auto url = TestHarnessBuilder().GenerateFakeUrl();
spec->mutable_basemgr_config()
->mutable_story_shell()
->mutable_app_config()
->set_url(url);
fuchsia::modular::testing::InterceptSpec intercept_spec;
intercept_spec.set_component_url(url);
spec->mutable_components_to_intercept()->push_back(std::move(intercept_spec));
return url;
}
TestHarnessFixture::TestHarnessFixture() {
fuchsia::sys::LaunchInfo launch_info;
launch_info.url = kTestHarnessUrl;
svc_ =
sys::ServiceDirectory::CreateWithRequest(&launch_info.directory_request);
launcher_ptr()->CreateComponent(std::move(launch_info),
test_harness_ctrl_.NewRequest());
test_harness_ = svc_->Connect<fuchsia::modular::testing::TestHarness>();
}
} // namespace testing
} // namespace modular