| // 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 |