blob: b7a8012ba87ca4fa96efa8e31e58d58469d4d0a8 [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.
#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_