blob: a78e4a8435327866859b68811cf462cdfed3930a [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/intl/cpp/fidl.h>
#include <fuchsia/modular/cpp/fidl.h>
#include <fuchsia/modular/testing/cpp/fidl.h>
#include <lib/modular/testing/cpp/fake_agent.h>
#include <lib/modular/testing/cpp/fake_component.h>
#include <lib/syslog/cpp/macros.h>
#include <src/modular/lib/modular_test_harness/cpp/test_harness_fixture.h>
#include "intl_property_provider_test_client.h"
namespace {
constexpr char kFakeRunnerUrl[] = "fuchsia-pkg://fuchsia.com/fake_runner#meta/fake_runner.cmx";
constexpr char kFakeRunnerServiceName[] = "fake.fuchsia.Service";
// A module that specifies kFakeRunnerUrl as the runner to be used to launch
// itself. The module doesn't have any functionality besides starting up and
// tearing down.
constexpr char kModuleWithFakeRunnerUrl[] =
"fuchsia-pkg://fuchsia.com/module_with_fake_runner#meta/module_with_fake_runner.cmx";
// A runner that is used to count the number of times it is launched, when starting
// multiple instances of the same module in different stories.
class FakeRunner : public modular_testing::FakeComponent, fuchsia::sys::Runner {
public:
FakeRunner() : FakeComponent({.url = kFakeRunnerUrl}) {}
int module_started_count() { return module_started_count_; }
protected:
void OnCreate(fuchsia::sys::StartupInfo startup_info) override {
runner_intercepted_count_++;
EXPECT_EQ(1, runner_intercepted_count_);
component_context()->outgoing()->AddPublicService<fuchsia::sys::Runner>(
[this](fidl::InterfaceRequest<fuchsia::sys::Runner> request) {
bindings_.AddBinding(this, std::move(request));
});
}
// |fuchsia::sys::Runner|
void StartComponent(
fuchsia::sys::Package package, fuchsia::sys::StartupInfo startup_info,
::fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller) override {
module_started_count_++;
}
fidl::BindingSet<fuchsia::sys::Runner> bindings_;
int runner_intercepted_count_ = 0;
int module_started_count_ = 0;
};
class StoriesShareSessionRunnersTest : public modular_testing::TestHarnessFixture {
protected:
void SetUp() override {
fake_agent_url_ = modular_testing::TestHarnessBuilder::GenerateFakeUrl(
"stories_share_session_runners_test_agent");
fake_agent_ = std::make_unique<modular_testing::FakeAgent>(modular_testing::FakeComponent::Args{
.url = fake_agent_url_,
.sandbox_services =
{
fuchsia::modular::ComponentContext::Name_,
fuchsia::intl::PropertyProvider::Name_,
kFakeRunnerServiceName,
},
});
fuchsia::modular::testing::TestHarnessSpec spec;
spec.mutable_sessionmgr_config()->set_session_agents({fake_agent_url_});
// Used by AgentsCanConnectToRunnerComponentServices test.
std::vector<fuchsia::modular::session::AgentServiceIndexEntry> agent_service_index;
fuchsia::modular::session::AgentServiceIndexEntry agent_service;
agent_service.set_service_name(kFakeRunnerServiceName);
agent_service.set_agent_url(kModuleWithFakeRunnerUrl);
agent_service_index.emplace_back(std::move(agent_service));
spec.mutable_sessionmgr_config()->set_agent_service_index(std::move(agent_service_index));
builder_ = std::make_unique<modular_testing::TestHarnessBuilder>(std::move(spec));
builder_->InterceptComponent(fake_agent_->BuildInterceptOptions());
builder_->InterceptComponent(
{.url = kFakeRunnerUrl,
.sandbox_services =
{
fuchsia::modular::ComponentContext::Name_,
fuchsia::intl::PropertyProvider::Name_,
},
.launch_handler =
[this](fuchsia::sys::StartupInfo startup_info,
fidl::InterfaceHandle<fuchsia::modular::testing::InterceptedComponent>
intercepted_component) mutable {
runners_requested_++;
// This test should ensure the fake_runner_ is requested only once, no matter how
// many stories or modules request it.
EXPECT_EQ(fake_runner_, nullptr); // don't ASSERT from void lambda
if (fake_runner_ != nullptr) {
// If unexpected second request, create a new runner so module construction will
// succeed in the new story, but fail the test based on |runners_requested_| > 1.
// (First, move the original runner so it isn't destructed on unique_ptr
// overwrite.)
saved_runner_ = std::move(fake_runner_);
}
fake_runner_ = std::make_unique<FakeRunner>();
fake_runner_->BuildInterceptOptions().launch_handler(
std::move(startup_info), std::move(intercepted_component));
}});
builder_->BuildAndRun(test_harness());
}
void AssertIntlPropertyProvider(const modular_testing::FakeComponent* fake_component) {
modular_tests::IntlPropertyProviderTestClient intl_client{fake_component};
ASSERT_EQ(ZX_OK, intl_client.Connect());
intl_client.LoadProfile();
RunLoopUntil([&] { return intl_client.HasProfile() || intl_client.HasError(); });
ASSERT_TRUE(intl_client.HasProfile());
fuchsia::intl::Profile* profile = intl_client.Profile();
ASSERT_TRUE(profile->has_locales());
ASSERT_TRUE(profile->has_calendars());
ASSERT_TRUE(profile->has_time_zones());
ASSERT_TRUE(profile->has_temperature_unit());
}
std::string fake_agent_url_;
std::unique_ptr<modular_testing::FakeAgent> fake_agent_;
std::unique_ptr<FakeRunner> fake_runner_;
std::unique_ptr<FakeRunner> saved_runner_;
int runners_requested_ = 0;
std::unique_ptr<modular_testing::TestHarnessBuilder> builder_;
};
// Tests that the same mod started in different stories will reuse the
// runner started by the first mod because stories share the same environment.
TEST_F(StoriesShareSessionRunnersTest, ModReusesRunner) {
auto first_intent = fuchsia::modular::Intent{.handler = kModuleWithFakeRunnerUrl,
.action = "com.google.fuchsia.module.runner"};
// Add a mod that will be launched via a fake runner
modular_testing::AddModToStory(test_harness(), "first_story", "mod_name",
std::move(first_intent));
RunLoopUntil([&] { return !!fake_runner_ && fake_runner_->module_started_count() > 0; });
EXPECT_EQ(1, runners_requested_);
EXPECT_EQ(1, fake_runner_->module_started_count());
// Add the same mod and check that the runner wasn't launched again
auto second_intent = fuchsia::modular::Intent{.handler = kModuleWithFakeRunnerUrl,
.action = "com.google.fuchsia.module.runner"};
modular_testing::AddModToStory(test_harness(), "second_story", "mod_name",
std::move(second_intent));
RunLoopUntil([&] { return fake_runner_->module_started_count() > 1 || runners_requested_ > 1; });
EXPECT_EQ(2, fake_runner_->module_started_count());
ASSERT_EQ(1, runners_requested_);
// Add the same mod and check that the runner wasn't launched again
auto third_intent = fuchsia::modular::Intent{.handler = kModuleWithFakeRunnerUrl,
.action = "com.google.fuchsia.module.runner"};
modular_testing::AddModToStory(test_harness(), "second_story", "mod_name_2_of_2",
std::move(third_intent));
RunLoopUntil([&] { return fake_runner_->module_started_count() > 2 || runners_requested_ > 1; });
EXPECT_EQ(3, fake_runner_->module_started_count());
ASSERT_EQ(1, runners_requested_);
// Add the same mod and check that the runner wasn't launched again
auto fourth_intent = fuchsia::modular::Intent{.handler = kModuleWithFakeRunnerUrl,
.action = "com.google.fuchsia.module.runner"};
modular_testing::AddModToStory(test_harness(), "third_story", "mod_name",
std::move(fourth_intent));
RunLoopUntil([&] { return fake_runner_->module_started_count() > 3 || runners_requested_ > 1; });
EXPECT_EQ(4, fake_runner_->module_started_count());
ASSERT_EQ(1, runners_requested_);
}
TEST_F(StoriesShareSessionRunnersTest, AgentsCanConnectToRunnerComponentServices) {
auto intent = fuchsia::modular::Intent{.handler = kModuleWithFakeRunnerUrl,
.action = "com.google.fuchsia.module.runner"};
// Add a mod that will be launched via a fake runner
modular_testing::AddModToStory(test_harness(), "story", "mod_name", std::move(intent));
RunLoopUntil([&] { return !!fake_runner_ && fake_runner_->module_started_count() > 0; });
EXPECT_EQ(1, fake_runner_->module_started_count());
EXPECT_EQ(1, runners_requested_);
// Create the fake Agent through which to request the service.
fuchsia::modular::ComponentContextPtr component_context;
fuchsia::modular::testing::ModularService modular_service;
modular_service.set_component_context(component_context.NewRequest());
test_harness()->ConnectToModularService(std::move(modular_service));
fuchsia::modular::AgentControllerPtr agent_controller;
fuchsia::sys::ServiceProviderPtr agent_services;
component_context->DeprecatedConnectToAgent(fake_agent_url_, agent_services.NewRequest(),
agent_controller.NewRequest());
RunLoopUntil([&] { return fake_agent_->is_running(); });
// Request the service to trigger and wait for the component to be started.
zx::channel channel0, channel1;
ASSERT_EQ(ZX_OK, zx::channel::create(0, &channel0, &channel1));
ASSERT_EQ(ZX_OK, fake_agent_->component_context()->svc()->Connect(kFakeRunnerServiceName,
std::move(channel0)));
RunLoopUntil([&] { return fake_runner_->module_started_count() > 1 || runners_requested_ > 1; });
EXPECT_EQ(2, fake_runner_->module_started_count());
// The Runner used to launch the Module should have been re-used to provide the service.
EXPECT_EQ(1, runners_requested_);
}
// Tests that a runner can still access the fuchsia::intl::PropertyProvider from its environment.
TEST_F(StoriesShareSessionRunnersTest, RunnerCanAccessIntlPropertyProvider) {
auto intent = fuchsia::modular::Intent{.handler = kModuleWithFakeRunnerUrl,
.action = "com.google.fuchsia.module.runner"};
// Add a mod that will be launched via a fake runner
modular_testing::AddModToStory(test_harness(), "story", "mod_name", std::move(intent));
RunLoopUntil([&] { return !!fake_runner_ && fake_runner_->module_started_count() > 0; });
AssertIntlPropertyProvider(fake_runner_.get());
}
// Tests that agents can get the intl::PropertyProvider exposed by Sessionmgr.
TEST_F(StoriesShareSessionRunnersTest, AgentGetsSessionmgrProvidedServices) {
fuchsia::modular::ComponentContextPtr component_context;
fuchsia::modular::testing::ModularService modular_service;
modular_service.set_component_context(component_context.NewRequest());
test_harness()->ConnectToModularService(std::move(modular_service));
fuchsia::modular::AgentControllerPtr agent_controller;
fuchsia::sys::ServiceProviderPtr agent_services;
component_context->DeprecatedConnectToAgent(fake_agent_url_, agent_services.NewRequest(),
agent_controller.NewRequest());
RunLoopUntil([&] { return fake_agent_->is_running(); });
AssertIntlPropertyProvider(fake_agent_.get());
}
} // namespace