| // Copyright 2020 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 "src/sys/sysmgr/app.h" | |
| #include <fuchsia/sys/cpp/fidl.h> | |
| #include <lib/fidl/cpp/interface_ptr.h> | |
| #include <lib/fidl/cpp/string.h> | |
| #include <lib/gtest/real_loop_fixture.h> | |
| #include <lib/sys/cpp/component_context.h> | |
| #include <lib/sys/cpp/testing/component_interceptor.h> | |
| #include <gtest/gtest.h> | |
| #include "src/lib/files/file.h" | |
| #include "src/lib/files/scoped_temp_dir.h" | |
| #include "src/lib/fxl/strings/substitute.h" | |
| namespace sysmgr { | |
| namespace { | |
| class TestSysmgr : public ::testing::Test { | |
| protected: | |
| // Makes a Config object with contents filled with |json| and returns its paths. | |
| // This method should only be called once in a test case. | |
| ::sysmgr::Config NewConfig(const std::string& json) { | |
| std::string tmp_dir_path; | |
| tmp_dir_.NewTempDir(&tmp_dir_path); | |
| const std::string json_file = fxl::Substitute("$0/sysmgr.config", tmp_dir_path); | |
| ZX_ASSERT(files::WriteFile(json_file, json.data(), json.size())); | |
| Config config; | |
| config.ParseFromDirectory(tmp_dir_path); | |
| ZX_ASSERT(!config.HasError()); | |
| return config; | |
| } | |
| files::ScopedTempDir tmp_dir_; | |
| }; | |
| // Smoke test that LaunchComponent doesn't crash. | |
| TEST_F(TestSysmgr, LaunchComponent) { | |
| async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); | |
| ::sysmgr::Config config = NewConfig(R"({})"); | |
| auto component_context = sys::ComponentContext::CreateAndServeOutgoingDirectory(); | |
| ::sysmgr::App app(std::move(config), component_context->svc(), &loop); | |
| auto launch_info = | |
| fuchsia::sys::LaunchInfo{.url = "fuchsia-pkg://example.com/pkg#meta/component.cmx"}; | |
| app.LaunchComponent(std::move(launch_info), nullptr, nullptr); | |
| } | |
| // Test that a critical component crashing too many times results in sysmgr exiting. | |
| // This is accomplished by configuring a component which doesn't exist as a critical component. | |
| TEST_F(TestSysmgr, LaunchCrashingCriticalComponent) { | |
| async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); | |
| ::sysmgr::Config config = NewConfig(R"({ | |
| "services": { | |
| "example.random.service": "fuchsia-pkg://example.com/pkg#meta/component.cmx" | |
| }, | |
| "startup_services": ["example.random.service"], | |
| "critical_components": ["fuchsia-pkg://example.com/pkg#meta/component.cmx"] | |
| })"); | |
| auto component_context = sys::ComponentContext::CreateAndServeOutgoingDirectory(); | |
| ::sysmgr::App app(std::move(config), component_context->svc(), &loop); | |
| auto launch_info = | |
| fuchsia::sys::LaunchInfo{.url = "fuchsia-pkg://example.com/pkg#meta/component.cmx"}; | |
| loop.Run(); | |
| // Here, the component should launch because it provides a startup_service. | |
| // Because the component URL doesn't exist, sysmgr should exit its loop from faling to launch | |
| // repeatedly. | |
| } | |
| TEST_F(TestSysmgr, MaxRetries) { | |
| constexpr char kFakeUrl[] = "fuchsia-pkg://example.com/pkg#meta/component.cmx"; | |
| constexpr size_t kExpectedMaxRetries = 3; | |
| async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); | |
| // component.cmx doesn't launch on startup from this point. | |
| ::sysmgr::Config config = NewConfig(R"({ | |
| "services": { | |
| "example.random.service": "fuchsia-pkg://example.com/pkg#meta/component.cmx" | |
| }, | |
| "startup_services": ["example.random.service"], | |
| "critical_components": ["fuchsia-pkg://example.com/pkg#meta/component.cmx"] | |
| })"); | |
| auto component_context = sys::ComponentContext::CreateAndServeOutgoingDirectory(); | |
| auto incoming_svc = component_context->svc(); | |
| auto interceptor = ::sys::testing::ComponentInterceptor::CreateWithEnvironmentLoader( | |
| incoming_svc->Connect<fuchsia::sys::Environment>(), loop.dispatcher()); | |
| auto env = ::sys::testing::EnclosingEnvironment::Create( | |
| "test_harness", incoming_svc->Connect<fuchsia::sys::Environment>(), | |
| interceptor.MakeEnvironmentServices(incoming_svc->Connect<fuchsia::sys::Environment>())); | |
| size_t num_launches = 0; | |
| ASSERT_TRUE(interceptor.InterceptURL( | |
| kFakeUrl, "", | |
| [&num_launches](fuchsia::sys::StartupInfo startup_info, | |
| std::unique_ptr<sys::testing::InterceptedComponent> component) { | |
| num_launches++; | |
| // immediately exit the component. | |
| })); | |
| ::sysmgr::App app(std::move(config), env->service_directory(), &loop); | |
| loop.Run(); | |
| // sysmgr exits the loop from failing to restart |kFakeUrl| repeatedly. | |
| EXPECT_EQ(kExpectedMaxRetries, num_launches - 1); | |
| } | |
| } // namespace | |
| } // namespace sysmgr |