| // 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. |
| |
| #ifndef LIB_MODULAR_TEST_HARNESS_CPP_TEST_HARNESS_IMPL_H_ |
| #define LIB_MODULAR_TEST_HARNESS_CPP_TEST_HARNESS_IMPL_H_ |
| |
| #include <fuchsia/auth/account/cpp/fidl.h> |
| #include <fuchsia/devicesettings/cpp/fidl.h> |
| #include <fuchsia/modular/cpp/fidl.h> |
| #include <fuchsia/modular/testing/cpp/fidl.h> |
| #include <fuchsia/setui/cpp/fidl.h> |
| #include <fuchsia/sys/cpp/fidl.h> |
| #include <lib/app_driver/cpp/agent_driver.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <lib/sys/cpp/testing/component_interceptor.h> |
| #include <lib/sys/cpp/testing/enclosing_environment.h> |
| #include <lib/vfs/cpp/pseudo_dir.h> |
| |
| namespace modular::testing { |
| |
| // Provides the |TestHarness| service. |
| class TestHarnessImpl final : fuchsia::modular::testing::TestHarness { |
| public: |
| // |parent_env| is the environment under which a new hermetic test harness |
| // environment is launched. |parent_env| must outlive this instance, otherwise |
| // the test harness environment dies. |
| // |
| // |test_harness_request| is implemented by this class. The TestHarness |
| // FIDL interface is the way to interact with the TestHarness API. |
| // |
| // |on_exit| is called when the TestHarness interface is closed. |
| // This can happen if the TestHarness client drops their side of the |
| // connection, or this class closes it due to an error; In this case, the |
| // error is sent as an epitaph. See the |TestHarness| protocol documentation |
| // for more details. |
| TestHarnessImpl(const fuchsia::sys::EnvironmentPtr& parent_env, |
| fidl::InterfaceRequest<TestHarness> test_harness_request, |
| fit::function<void()> on_disconnected); |
| |
| virtual ~TestHarnessImpl() override; |
| |
| // Not copyable. |
| TestHarnessImpl(const TestHarnessImpl&) = delete; |
| void operator=(const TestHarnessImpl&) = delete; |
| |
| private: |
| class InterceptedComponentImpl; |
| class InterceptedSessionAgent; |
| |
| // Services requested using |TestHarness.GetService()| are provided by a |
| // session agent which is started as part of the test harness' modular runtime |
| // instance. This session agent is intercepted and implemented by the |
| // |InterceptedSessionAgent| inner class. This struct holds state or the |
| // intercepted session agent implementation. |
| struct InterceptedSessionAgentInfo { |
| // Service requests from |TestHarness.GetService()| may be issued before the |
| // session agent, which provides these services, has been initialized. These |
| // service requests are buffered here until the session agent has been |
| // initialized. |
| // |
| // Flushed using FlushBufferedSessionAgentServices(). |
| struct BufferedServiceRequest { |
| std::string service_name; |
| zx::channel service_request; |
| }; |
| std::vector<BufferedServiceRequest> buffered_service_requests; |
| |
| // The session agent's intercepted state that we must keep around to keep |
| // the component alive: |
| std::unique_ptr<component::StartupContext> component_context; |
| std::unique_ptr<sys::testing::InterceptedComponent> intercepted_component; |
| std::unique_ptr<::modular::AgentDriver<InterceptedSessionAgent>> |
| agent_driver; |
| }; |
| |
| // |fuchsia::modular::testing::TestHarness| |
| void Run(fuchsia::modular::testing::TestHarnessSpec spec) override; |
| |
| // |fuchsia::modular::testing::TestHarness| |
| void GetService( |
| fuchsia::modular::testing::TestHarnessService service) override; |
| |
| // |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; |
| |
| [[nodiscard]] static std::unique_ptr<vfs::PseudoDir> MakeBasemgrConfigDir( |
| const fuchsia::modular::testing::TestHarnessSpec& spec); |
| |
| // Helper class |
| fuchsia::modular::testing::InterceptedComponentPtr |
| AddInterceptedComponentBinding( |
| std::unique_ptr<sys::testing::InterceptedComponent> |
| intercepted_component); |
| |
| // Use this helper method for checking fatal errors. |
| // |
| // If |status| is ZX_OK, returns |false|. |
| // If |status| is not ZX_OK, the TestHarness binding is closed with |status| |
| // as the epitaph, and returns |true|. |
| bool CloseBindingIfError(zx_status_t status); |
| |
| // Sets up component intercept specified in |
| // |TestHarnessSpec.components_to_intercept|. |
| [[nodiscard]] zx_status_t SetupComponentInterception(); |
| |
| // Sets up interception for the session agent which is launched as part of the |
| // modular runtime. This session agent provides the services for |
| // |TestHarness.ConnectToModularService()|. |
| [[nodiscard]] zx_status_t SetupFakeSessionAgent(); |
| |
| // Sets up the enclosing environment with services specified in |
| // |TestHarnessSpec.injected_services|, and removes them from the supplied |
| // |default_injected_services|. |
| // |
| // |default_injected_services| maps service name => component URL which serves |
| // it. |
| void InjectServicesIntoEnvironment( |
| sys::testing::EnvironmentServices* env_services, |
| std::map<std::string, std::string>* default_injected_services); |
| |
| // Buffers service request from |GetService()|. |
| // FlushBufferedSessionAgentServices() processes these services once the |
| // session agent supplying these services comes alive. |
| template <typename Interface> |
| void BufferSessionAgentService(fidl::InterfaceRequest<Interface> request) { |
| intercepted_session_agent_info_.buffered_service_requests.push_back( |
| InterceptedSessionAgentInfo::BufferedServiceRequest{ |
| Interface::Name_, request.TakeChannel()}); |
| |
| FlushBufferedSessionAgentServices(); |
| } |
| |
| // Processes the service requests which are buffered from |GetService()|. |
| void FlushBufferedSessionAgentServices(); |
| |
| // The test harness environment is a child of |parent_env_|. |
| const fuchsia::sys::EnvironmentPtr& parent_env_; // Not owned. |
| |
| fidl::Binding<fuchsia::modular::testing::TestHarness> binding_; |
| fuchsia::modular::testing::TestHarnessSpec spec_; |
| |
| fit::function<void()> on_disconnected_; |
| |
| // This map manages InterceptedComponent bindings (and their implementations). |
| // When a |InterceptedComponent| connection is closed, it is automatically |
| // removed from this map (and its impl is deleted as well). |
| // |
| // The key is the raw-pointer backing the unique_ptr value. |
| std::map<InterceptedComponentImpl*, std::unique_ptr<InterceptedComponentImpl>> |
| intercepted_component_impls_; |
| |
| // |interceptor_| must outlive |enclosing_env_|. |
| sys::testing::ComponentInterceptor interceptor_; |
| std::unique_ptr<sys::testing::EnclosingEnvironment> enclosing_env_; |
| std::unique_ptr<vfs::PseudoDir> basemgr_config_dir_; |
| fuchsia::sys::ComponentControllerPtr basemgr_ctrl_; |
| |
| InterceptedSessionAgentInfo intercepted_session_agent_info_; |
| |
| friend class TestHarnessImplTest; |
| }; |
| |
| } // namespace modular::testing |
| |
| #endif // LIB_MODULAR_TEST_HARNESS_CPP_TEST_HARNESS_IMPL_H_ |