blob: 883f084383896f3a649e1e2b051e124236fdc535 [file] [log] [blame]
// 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 <fuchsia/process/lifecycle/cpp/fidl.h>
#include <fuchsia/sys/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/sys/cpp/testing/test_with_environment.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/trace-provider/provider.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <src/lib/files/directory.h>
#include "src/sys/appmgr/appmgr.h"
#include "src/sys/appmgr/moniker.h"
constexpr char kSysmgrUrl[] =
"fuchsia-pkg://fuchsia.com/appmgr-lifecycle-tests#meta/test-sysmgr.cmx";
constexpr char kLifecyleComponentUrl[] =
"fuchsia-pkg://fuchsia.com/appmgr-lifecycle-tests#meta/test-lifecycle-component.cmx";
constexpr char kRootRealm[] = "app";
class AppmgrLifecycleTest : public sys::testing::TestWithEnvironment,
public fuchsia::sys::internal::LogConnectionListener {
public:
AppmgrLifecycleTest() = default;
~AppmgrLifecycleTest() = default;
zx_status_t stop_callback_status_ = ZX_ERR_BAD_STATE;
void SetUp() override {
FX_LOGS(INFO) << "Setting up AppmgrLifecycleTest";
std::unordered_set<component::Moniker> lifecycle_allowlist;
lifecycle_allowlist.insert(
component::Moniker{.url = kLifecyleComponentUrl, .realm_path = {kRootRealm}});
fuchsia::sys::ServiceListPtr root_realm_services(new fuchsia::sys::ServiceList);
zx::channel trace_client, trace_server;
zx_status_t status = zx::channel::create(0, &trace_client, &trace_server);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to create tracing channel: " << status;
return;
}
trace::TraceProvider trace_provider(std::move(trace_client), dispatcher());
fuchsia::sys::LoaderPtr loader = real_services()->Connect<fuchsia::sys::Loader>();
zx::channel appmgr_service_request;
appmgr_services_ = sys::ServiceDirectory::CreateWithRequest(&appmgr_service_request);
component::AppmgrArgs args{
.pa_directory_request = appmgr_service_request.release(),
.lifecycle_request = appmgr_lifecycle_.NewRequest().TakeChannel().release(),
.lifecycle_allowlist = std::move(lifecycle_allowlist),
.root_realm_services = std::move(root_realm_services),
.environment_services = real_services(),
.sysmgr_url = kSysmgrUrl,
.sysmgr_args = {},
.loader = std::optional<fuchsia::sys::LoaderPtr>(std::move(loader)),
.run_virtual_console = false,
.trace_server_channel = std::move(trace_server),
.stop_callback = [this](zx_status_t status) { stop_callback_status_ = status; }};
appmgr_ = std::make_unique<component::Appmgr>(dispatcher(), std::move(args));
log_connector_.set_error_handler(
[](zx_status_t status) { FX_PLOGS(INFO, status) << "Failed to connect to appmgr logs"; });
status = appmgr_services_->Connect(log_connector_.NewRequest(),
"appmgr_svc/fuchsia.sys.internal.LogConnector");
FX_CHECK(status == ZX_OK);
// Simulate the archivist connecting to the log listener so that appmgr will launch sysmgr
log_connector_->TakeLogConnectionListener(log_binding_.GetHandler(this));
}
// |fuchsia::sys::internal::LogConnectionListener|
void OnNewConnection(fuchsia::sys::internal::LogConnection connection) override {}
std::unique_ptr<component::Appmgr> appmgr_;
std::shared_ptr<sys::ServiceDirectory> appmgr_services_;
fidl::BindingSet<fuchsia::sys::internal::LogConnectionListener> log_binding_;
fuchsia::sys::internal::LogConnectorPtr log_connector_;
fuchsia::process::lifecycle::LifecyclePtr appmgr_lifecycle_;
};
TEST_F(AppmgrLifecycleTest, LifecycleComponentGetsShutdownSignal) {
// Launch TestLifecycleComponent
zx::channel svc_request;
auto svc_dir = sys::ServiceDirectory::CreateWithRequest(&svc_request);
FX_CHECK(svc_request.is_valid());
fuchsia::sys::LaunchInfo launch_info;
launch_info.url = kLifecyleComponentUrl;
launch_info.directory_request = std::move(svc_request);
fuchsia::sys::ComponentControllerPtr controller;
bool lifecycle_component_running = false;
bool lifecycle_component_terminated = false;
bool appmgr_terminated = false;
controller.events().OnDirectoryReady = [&] {
FX_LOGS(INFO) << "TestLifecycleComponent launch complete.";
lifecycle_component_running = true;
};
controller.events().OnTerminated = [&](int64_t return_code,
fuchsia::sys::TerminationReason reason) {
FX_LOGS(INFO) << "TestLifecycleComponent termination complete.";
lifecycle_component_terminated = true;
};
appmgr_->RootRealm()->CreateComponent(std::move(launch_info), controller.NewRequest());
RunLoopUntil([&lifecycle_component_running] { return lifecycle_component_running; });
appmgr_lifecycle_.set_error_handler([&](zx_status_t status) {
FX_PLOGS(INFO, status) << "Appmgr Lifecycle channel closed.";
ASSERT_EQ(status, ZX_OK);
appmgr_terminated = true;
});
appmgr_lifecycle_->Stop();
RunLoopUntil([&] { return lifecycle_component_terminated && appmgr_terminated; });
ASSERT_EQ(ZX_OK, stop_callback_status_);
}
// Test that appmgr terminates if none of the components in the allowlist expose
// the lifecycle protocol.
TEST_F(AppmgrLifecycleTest, LifecycleNoShutdownComponents) {
bool appmgr_terminated = false;
appmgr_lifecycle_.set_error_handler([&](zx_status_t status) {
FX_PLOGS(INFO, status) << "Appmgr Lifecycle channel closed.";
ASSERT_EQ(status, ZX_OK);
appmgr_terminated = true;
});
appmgr_lifecycle_->Stop();
RunLoopUntil([&] { return appmgr_terminated; });
ASSERT_EQ(ZX_OK, stop_callback_status_);
}