| // 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 SRC_MODULAR_LIB_MODULAR_TEST_HARNESS_CPP_TEST_HARNESS_IMPL_H_ |
| #define SRC_MODULAR_LIB_MODULAR_TEST_HARNESS_CPP_TEST_HARNESS_IMPL_H_ |
| |
| #include <fuchsia/devicesettings/cpp/fidl.h> |
| #include <fuchsia/modular/cpp/fidl.h> |
| #include <fuchsia/modular/testing/cpp/fidl.h> |
| #include <fuchsia/settings/cpp/fidl.h> |
| #include <fuchsia/sys/cpp/fidl.h> |
| #include <lib/modular/cpp/agent.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> |
| |
| #include <set> |
| |
| #include "src/modular/lib/lifecycle/cpp/lifecycle_impl.h" |
| |
| namespace modular_testing { |
| |
| constexpr char kSessionAgentFakeInterceptionUrl[] = |
| "fuchsia-pkg://example.com/FAKE_SESSION_AGENT_PKG/fake_session_agent.cmx"; |
| |
| // Provides the |TestHarness| service. |
| class TestHarnessImpl final : fuchsia::modular::testing::TestHarness, |
| public modular::LifecycleImpl::Delegate { |
| 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. |
| // |
| // |on_exit| is called if a running session instance terminates, or if the TestHarness interface |
| // is closed, or terminates. 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, fit::function<void()> on_exit); |
| |
| virtual ~TestHarnessImpl() override; |
| |
| // Not copyable. |
| TestHarnessImpl(const TestHarnessImpl&) = delete; |
| void operator=(const TestHarnessImpl&) = delete; |
| |
| // |request| is served by this class. The TestHarness FIDL interface is the way to interact with |
| // the TestHarness API. |
| void Bind(fidl::InterfaceRequest<fuchsia::modular::testing::TestHarness> request); |
| |
| // Terminate the running instance of the test harness. If there is a running session, it is asked |
| // to terminate. |
| // |
| // |modular::LifecycleImpl::Delegate| |
| void Terminate() override; |
| |
| private: |
| class InterceptedComponentImpl; |
| class InterceptedSessionAgent; |
| |
| // Services requested using |TestHarness.ConnectToService()| 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.ConnectToService()| 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<sys::ComponentContext> component_context; |
| std::unique_ptr<sys::testing::InterceptedComponent> intercepted_component; |
| std::unique_ptr<::modular::Agent> agent; |
| }; |
| |
| // |fuchsia::modular::testing::TestHarness| |
| void Run(fuchsia::modular::testing::TestHarnessSpec spec) 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; |
| |
| // |fuchsia::modular::testing::TestHarness| |
| void ParseConfig(std::string config, ParseConfigCallback callback) 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(); |
| |
| // Populates the test harness environment with services described by |
| // |spec_.env_services|. |
| zx_status_t PopulateEnvServices(sys::testing::EnvironmentServices* env_services); |
| |
| // Injects services into the test harness environment according to |
| // |spec_.env_services.services_from_components| and |
| // |spec_.env_services_to_inject|. |
| // |
| // Injected service names are inserted into |added_svcs|. |
| zx_status_t PopulateEnvServicesWithComponents(sys::testing::EnvironmentServices* env_services, |
| std::set<std::string>* added_svcs); |
| |
| // Injects services into the test harness environment according to |
| // |spec_.env_services.service_dir|. |
| // |
| // Injected service names are inserted into |added_svcs|. |
| zx_status_t PopulateEnvServicesWithServiceDir(sys::testing::EnvironmentServices* env_services, |
| std::set<std::string>* added_svcs); |
| |
| // 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_exit_; |
| |
| // 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_; |
| fuchsia::modular::LifecyclePtr basemgr_lifecycle_; |
| |
| InterceptedSessionAgentInfo intercepted_session_agent_info_; |
| |
| std::unique_ptr<sys::ServiceDirectory> env_service_dir_; |
| |
| friend class TestHarnessImplTest; |
| }; |
| |
| } // namespace modular_testing |
| |
| #endif // SRC_MODULAR_LIB_MODULAR_TEST_HARNESS_CPP_TEST_HARNESS_IMPL_H_ |