blob: c2a4d75a5c36d6c49b09d58e9f1499cb7cd9e3f9 [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.
#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 {
// |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;
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;
// |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
// 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) {
Interface::Name_, request.TakeChannel()});
// 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>>
// |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