| // Copyright 2018 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_SYS_CPP_TESTING_TEST_WITH_ENVIRONMENT_H_ | |
| #define LIB_SYS_CPP_TESTING_TEST_WITH_ENVIRONMENT_H_ | |
| #include <fuchsia/sys/cpp/fidl.h> | |
| #include <lib/fidl/cpp/binding_set.h> | |
| #include <lib/gtest/real_loop_fixture.h> | |
| #include <lib/sys/cpp/testing/enclosing_environment.h> | |
| #include <lib/sys/cpp/testing/launcher_impl.h> | |
| namespace sys { | |
| namespace testing { | |
| // Combines the return code and termination reason from a Component termination. | |
| struct TerminationResult { | |
| int64_t return_code; | |
| fuchsia::sys::TerminationReason reason; | |
| }; | |
| // Test fixture for tests to run Components inside a new isolated Environment, | |
| // wrapped in a enclosing Environment. | |
| // | |
| // The new isolated Environment, provided to the Component under test, is not | |
| // visible to any real Environments, such as the Environment that the test | |
| // program was launched in. | |
| // | |
| // That isloated environment needs to be created using | |
| // |CreateNewEnclosingEnvironment*| APIs. | |
| // | |
| // The isolated Environment is enclosed in a enclosing Environment, allowing the | |
| // test to provide Loader, Services, and other Directories that are visible to | |
| // Components under test, and only to those Components. | |
| // | |
| // | |
| // This fixture also allows you to create components in the real environment in | |
| // which this test was launched. Those components should only be used to | |
| // validate real system state. | |
| // | |
| // To use this fixture you need to whitelist "fuchsia.sys.Environment" and | |
| // "fuchsia.sys.Loader" in your component manifest file in "sandbox.services". | |
| // It is necessary because this fixture needs to access | |
| // "fuchsia.sys.Environment" and if you create a |EnclosingEnvironment| then it | |
| // will need access to parent's "fuchsia.sys.Loader" to serve it own Loader | |
| // service. | |
| // | |
| // If you are going to create a new EnclosingEnvironment then you also need to | |
| // run your own loop to serve services provided by that environment. So use of | |
| // SyncPtrs is not advisable unless you can run a loop safely in a new thread | |
| // which stops running before you kill your EnclosingEnvironment. | |
| class TestWithEnvironment : public gtest::RealLoopFixture { | |
| protected: | |
| TestWithEnvironment(); | |
| fuchsia::sys::LauncherPtr launcher_ptr() { | |
| fuchsia::sys::LauncherPtr launcher; | |
| real_launcher_.AddBinding(launcher.NewRequest()); | |
| return launcher; | |
| } | |
| const std::shared_ptr<sys::ServiceDirectory>& real_services() { return real_services_; } | |
| const fuchsia::sys::EnvironmentPtr& real_env() { return real_env_; } | |
| // Creates a new enclosing environment inside current real environment with | |
| // the given services. | |
| // | |
| // This environment and components created in it will not have access to any | |
| // of services(except Loader) and resources from the real environment unless | |
| // explicitly allowed by calling AllowPublicService. | |
| // | |
| // After all services are added/passed through to the environment, you must | |
| // call Launch() to actually start it. | |
| std::unique_ptr<EnclosingEnvironment> CreateNewEnclosingEnvironment( | |
| const std::string& label, std::unique_ptr<EnvironmentServices> services, | |
| const fuchsia::sys::EnvironmentOptions& options = {}) const { | |
| return EnclosingEnvironment::Create(label, real_env_, std::move(services), options); | |
| } | |
| // Returns an EnvironmentServices object that the caller can use to pass | |
| // services to a new EnclosingEnvironment. | |
| // | |
| // The returned object has the parent's loader, but no other services by | |
| // default. | |
| std::unique_ptr<EnvironmentServices> CreateServices() { | |
| return EnvironmentServices::Create(real_env_); | |
| } | |
| std::unique_ptr<EnvironmentServices> CreateServicesWithParentOverrides( | |
| EnvironmentServices::ParentOverrides parent_overrides) { | |
| return EnvironmentServices::CreateWithParentOverrides(real_env_, std::move(parent_overrides)); | |
| } | |
| // Creates component in current real environment. This component will have | |
| // access to the services, directories and other resources from the | |
| // environment in which your test was launched. | |
| // | |
| // This should be mostly used for observing the state of system and for | |
| // nothing else. For eg. Try launching "glob" component and validate how it | |
| // behaves in various environments. | |
| void CreateComponentInCurrentEnvironment( | |
| fuchsia::sys::LaunchInfo launch_info, | |
| fidl::InterfaceRequest<fuchsia::sys::ComponentController> request); | |
| // Waits for the environment to start. | |
| // | |
| // You should either use this function to wait or run your own loop if you | |
| // want CreateComponent* to succed on |enclosing_environment|. | |
| void WaitForEnclosingEnvToStart(const EnclosingEnvironment* enclosing_environment) { | |
| RunLoopUntil([enclosing_environment] { return enclosing_environment->is_running(); }); | |
| } | |
| // Run a loop until the given component is terminated or |timeout| elapses. | |
| bool RunComponentUntilTerminated(fuchsia::sys::ComponentControllerPtr component_controller, | |
| TerminationResult* termination_result = nullptr); | |
| private: | |
| std::shared_ptr<sys::ServiceDirectory> real_services_; | |
| fuchsia::sys::EnvironmentPtr real_env_; | |
| LauncherImpl real_launcher_; | |
| }; | |
| } // namespace testing | |
| } // namespace sys | |
| #endif // LIB_SYS_CPP_TESTING_TEST_WITH_ENVIRONMENT_H_ |