| // 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/modular/test/harness/cpp/fidl.h> |
| #include <fuchsia/modular/testing/cpp/fidl.h> |
| #include <lib/modular/testing/cpp/fake_component.h> |
| #include <lib/modular/testing/cpp/test_harness_builder.h> |
| #include <lib/sys/cpp/service_directory.h> |
| #include <lib/sys/cpp/testing/test_with_environment_fixture.h> |
| |
| #include <gmock/gmock.h> |
| #include <rapidjson/document.h> |
| |
| #include "src/lib/fsl/vmo/strings.h" |
| #include "src/modular/lib/modular_test_harness/cpp/fake_module.h" |
| #include "src/modular/lib/modular_test_harness/cpp/test_harness_fixture.h" |
| |
| using testing::HasSubstr; |
| using testing::Not; |
| |
| class FakeTestHarness : public fuchsia::modular::testing::TestHarness { |
| public: |
| std::optional<fuchsia::modular::testing::TestHarnessSpec>& spec() { return spec_; } |
| |
| private: |
| // |fuchsia::modular::testing::TestHarness| |
| void Run(fuchsia::modular::testing::TestHarnessSpec spec) override { spec_ = std::move(spec); } |
| |
| // |fuchsia::modular::testing::TestHarness| |
| void ConnectToModularService(fuchsia::modular::testing::ModularService service) override {} |
| |
| // |fuchsia::modular::testing::TestHarness| |
| void ConnectToEnvironmentService(std::string service_name, zx::channel request) override {} |
| |
| // |fuchsia::modular::testing::TestHarness| |
| void ParseConfig(std::string config, ParseConfigCallback callback) override {} |
| |
| std::optional<fuchsia::modular::testing::TestHarnessSpec> spec_; |
| }; |
| |
| // TestHarnessBuilder. Provides a service directory, typically used for |
| // testing environment service building. |
| class TestHarnessBuilderTest : public modular_testing::TestHarnessFixture { |
| public: |
| TestHarnessBuilderTest() { |
| zx::channel dir_req; |
| env_service_dir_ = sys::ServiceDirectory::CreateWithRequest(&dir_req); |
| env_pseudo_dir_.Serve( |
| fuchsia::io::OpenFlags::RIGHT_READABLE | fuchsia::io::OpenFlags::RIGHT_WRITABLE, |
| std::move(dir_req)); |
| } |
| |
| template <typename Interface> |
| void AddEnvService(fidl::InterfaceRequestHandler<Interface> req_handler) { |
| env_pseudo_dir_.AddEntry( |
| Interface::Name_, std::make_unique<vfs::Service>( |
| [req_handler = std::move(req_handler)]( |
| zx::channel request, async_dispatcher_t* dispatcher) mutable { |
| req_handler(fidl::InterfaceRequest<Interface>(std::move(request))); |
| })); |
| } |
| |
| std::shared_ptr<sys::ServiceDirectory> env_service_dir() { return env_service_dir_; } |
| |
| std::tuple<fuchsia::modular::testing::TestHarnessSpec, |
| fuchsia::modular::testing::TestHarness::OnNewComponentCallback> |
| TakeSpecAndHandler(modular_testing::TestHarnessBuilder* builder) { |
| FakeTestHarness fake_test_harness; |
| fidl::Binding<fuchsia::modular::testing::TestHarness> fake_test_harness_binding( |
| &fake_test_harness); |
| fuchsia::modular::testing::TestHarnessPtr fake_test_harness_ptr; |
| fake_test_harness_ptr.Bind(fake_test_harness_binding.NewBinding()); |
| builder->BuildAndRun(fake_test_harness_ptr); |
| |
| RunLoopUntil([&] { return fake_test_harness.spec().has_value(); }); |
| |
| return {std::move(fake_test_harness.spec()).value(), |
| std::move(fake_test_harness_ptr.events().OnNewComponent)}; |
| } |
| |
| private: |
| vfs::PseudoDir env_pseudo_dir_; |
| std::shared_ptr<sys::ServiceDirectory> env_service_dir_; |
| }; |
| |
| // A Pinger implementation used for testing environment services. |
| class PingerImpl : public fuchsia::modular::test::harness::Pinger { |
| public: |
| bool pinged() { return pinged_; } |
| |
| private: |
| // |fuchsia::modular::test::harness::Pinger| |
| void Ping() override { pinged_ = true; } |
| |
| bool pinged_ = false; |
| }; |
| |
| // Test that modular_testing::TestHarnessBuilder::GenerateFakeUrl() returns new urls each time. |
| TEST_F(TestHarnessBuilderTest, GenerateFakeUrl) { |
| modular_testing::TestHarnessBuilder builder; |
| EXPECT_NE(modular_testing::TestHarnessBuilder::GenerateFakeUrl(), |
| modular_testing::TestHarnessBuilder::GenerateFakeUrl()); |
| |
| EXPECT_THAT(modular_testing::TestHarnessBuilder::GenerateFakeUrl("foobar"), HasSubstr("foobar")); |
| EXPECT_THAT(modular_testing::TestHarnessBuilder::GenerateFakeUrl("foo!_bar"), |
| HasSubstr("foobar")); |
| EXPECT_THAT(modular_testing::TestHarnessBuilder::GenerateFakeUrl("foo!_bar"), |
| Not(HasSubstr("foo!_bar"))); |
| } |
| |
| bool JsonEq(std::string a, std::string b) { |
| rapidjson::Document doc_a; |
| doc_a.Parse(a); |
| |
| rapidjson::Document doc_b; |
| doc_b.Parse(b); |
| |
| return doc_a == doc_b; |
| } |
| |
| // Test that the TestHarnessBuilder builds a sane TestHarnessSpec and |
| // OnNewComponent router function. |
| TEST_F(TestHarnessBuilderTest, InterceptSpecTest) { |
| modular_testing::TestHarnessBuilder builder; |
| |
| std::string called; |
| builder.InterceptComponent( |
| {.url = "generic", |
| .sandbox_services = {"library.Protocol"}, |
| .launch_handler = [&](auto launch_info, auto handle) { called = "generic"; }}); |
| builder.InterceptSessionShell( |
| {.url = "session_shell", |
| .launch_handler = [&](auto launch_info, auto handle) { called = "session_shell"; }}); |
| builder.InterceptStoryShell( |
| {.url = "story_shell", |
| .launch_handler = [&](auto launch_info, auto handle) { called = "story_shell"; }}); |
| builder.InterceptSessionLauncherComponent( |
| {.url = "session_launcher_component", .launch_handler = [&](auto launch_info, auto handle) { |
| called = "session_launcher_component"; |
| }}); |
| |
| auto [spec, new_component_handler] = TakeSpecAndHandler(&builder); |
| |
| EXPECT_EQ("generic", spec.components_to_intercept().at(0).component_url()); |
| ASSERT_TRUE(spec.components_to_intercept().at(0).has_extra_cmx_contents()); |
| std::string cmx_str; |
| ASSERT_TRUE( |
| fsl::StringFromVmo(spec.components_to_intercept().at(0).extra_cmx_contents(), &cmx_str)); |
| EXPECT_TRUE(JsonEq(R"({"sandbox":{"services":["library.Protocol"]}})", cmx_str)); |
| EXPECT_EQ("session_shell", spec.components_to_intercept().at(1).component_url()); |
| EXPECT_EQ("story_shell", spec.components_to_intercept().at(2).component_url()); |
| EXPECT_EQ("session_launcher_component", spec.components_to_intercept().at(3).component_url()); |
| |
| EXPECT_EQ("session_shell", |
| spec.basemgr_config().session_shell_map().at(0).config().app_config().url()); |
| EXPECT_EQ("story_shell", spec.basemgr_config().story_shell().app_config().url()); |
| EXPECT_EQ("session_launcher_component", spec.basemgr_config().session_launcher().url()); |
| |
| { |
| fuchsia::sys::StartupInfo startup_info; |
| startup_info.launch_info.url = "generic"; |
| new_component_handler(std::move(startup_info), nullptr); |
| EXPECT_EQ("generic", called); |
| } |
| { |
| fuchsia::sys::StartupInfo startup_info; |
| startup_info.launch_info.url = "session_shell"; |
| new_component_handler(std::move(startup_info), nullptr); |
| EXPECT_EQ("session_shell", called); |
| } |
| { |
| fuchsia::sys::StartupInfo startup_info; |
| startup_info.launch_info.url = "story_shell"; |
| new_component_handler(std::move(startup_info), nullptr); |
| EXPECT_EQ("story_shell", called); |
| } |
| { |
| fuchsia::sys::StartupInfo startup_info; |
| startup_info.launch_info.url = "session_launcher_component"; |
| new_component_handler(std::move(startup_info), nullptr); |
| EXPECT_EQ("session_launcher_component", called); |
| } |
| } |
| |
| // Inject the 'Pinger' service into the env. Test that we can connect to Pinger |
| // and use it successfully. |
| TEST_F(TestHarnessBuilderTest, AddService) { |
| PingerImpl pinger_impl; |
| fidl::BindingSet<fuchsia::modular::test::harness::Pinger> pinger_bindings; |
| |
| modular_testing::TestHarnessBuilder builder; |
| builder.AddService<fuchsia::modular::test::harness::Pinger>( |
| pinger_bindings.GetHandler(&pinger_impl)); |
| builder.BuildAndRun(test_harness()); |
| |
| fuchsia::modular::test::harness::PingerPtr pinger; |
| test_harness()->ConnectToEnvironmentService(fuchsia::modular::test::harness::Pinger::Name_, |
| pinger.NewRequest().TakeChannel()); |
| |
| pinger->Ping(); |
| RunLoopUntil([&] { return pinger_impl.pinged(); }); |
| } |
| |
| // Test that TestHarnessBuilder::BuildSpec() populates the |
| // env_services.services_from_components correctly. |
| TEST_F(TestHarnessBuilderTest, AddServiceFromComponent) { |
| modular_testing::TestHarnessBuilder builder; |
| auto fake_url = modular_testing::TestHarnessBuilder::GenerateFakeUrl(); |
| builder.AddServiceFromComponent<fuchsia::modular::test::harness::Pinger>(fake_url); |
| |
| auto [spec, new_component_handler] = TakeSpecAndHandler(&builder); |
| |
| auto& services_from_components = spec.env_services().services_from_components(); |
| ASSERT_EQ(1u, services_from_components.size()); |
| EXPECT_EQ(fuchsia::modular::test::harness::Pinger::Name_, services_from_components[0].name); |
| EXPECT_EQ(fake_url, services_from_components[0].url); |
| } |
| |
| // Test that InheritService() borrows services from the given |service_dir|. |
| // This is tested by trying to inherit and use the Pinger service. |
| TEST_F(TestHarnessBuilderTest, AddServiceFromServiceDirectory) { |
| PingerImpl pinger_impl; |
| fidl::BindingSet<fuchsia::modular::test::harness::Pinger> pinger_bindings; |
| |
| AddEnvService<fuchsia::modular::test::harness::Pinger>(pinger_bindings.GetHandler(&pinger_impl)); |
| |
| modular_testing::TestHarnessBuilder builder; |
| builder.AddServiceFromServiceDirectory<fuchsia::modular::test::harness::Pinger>( |
| env_service_dir()); |
| builder.BuildAndRun(test_harness()); |
| |
| fuchsia::modular::test::harness::PingerPtr pinger; |
| test_harness()->ConnectToEnvironmentService(fuchsia::modular::test::harness::Pinger::Name_, |
| pinger.NewRequest().TakeChannel()); |
| |
| pinger->Ping(); |
| RunLoopUntil([&] { return pinger_impl.pinged(); }); |
| } |