blob: e45935569b6d5deaccfee8301d0916e9eb96a179 [file] [log] [blame]
// Copyright 2024 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.
use component_events::events::*;
use component_events::matcher::*;
use fidl::endpoints::DiscoverableProtocolMarker;
use fuchsia_component_test::{
Capability, ChildOptions, RealmBuilder, RealmBuilderParams, RealmInstance, Ref, Route,
};
use futures::StreamExt;
use test_case::test_case;
use {
fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_decl as fdecl,
fidl_fuchsia_data as fdata,
};
const EXIT_WITH_CODE: &str = "exit_with_code";
async fn build_program(exit_code: i64) -> RealmInstance {
let builder = RealmBuilder::with_params(
RealmBuilderParams::new().from_relative_url("#meta/test_root.cm"),
)
.await
.unwrap();
let program = builder
.add_child_from_decl(
EXIT_WITH_CODE,
cm_rust::ComponentDecl {
program: Some(cm_rust::ProgramDecl {
runner: Some("elf".parse().unwrap()),
info: fdata::Dictionary {
entries: Some(vec![
fdata::DictionaryEntry {
key: "binary".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str(
"bin/exit_with_code".to_string(),
))),
},
fdata::DictionaryEntry {
key: "args".to_string(),
value: Some(Box::new(fdata::DictionaryValue::StrVec(vec![
format!("{}", exit_code),
]))),
},
]),
..Default::default()
},
..Default::default()
}),
exposes: Box::from([cm_rust::ExposeDecl::Protocol(cm_rust::ExposeProtocolDecl {
source: cm_rust::ExposeSource::Framework,
source_name: fcomponent::BinderMarker::PROTOCOL_NAME.parse().unwrap(),
source_dictionary: Default::default(),
target: cm_rust::ExposeTarget::Parent,
target_name: "exit_with_code_binder".parse().unwrap(),
availability: Default::default(),
})]),
..Default::default()
},
ChildOptions::new().environment("elf-env"),
)
.await
.unwrap();
builder
.add_route(
Route::new()
.capability(Capability::protocol_by_name("exit_with_code_binder"))
.from(&program)
.to(Ref::parent()),
)
.await
.unwrap();
builder.build_in_nested_component_manager("#meta/component_manager.cm").await.unwrap()
}
fn exit_code_to_status(exit_code: i64) -> i32 {
if exit_code == 0 {
0
} else {
fcomponent::Error::InstanceDied.into_primitive().try_into().unwrap()
}
}
#[test_case(0)]
#[test_case(1)]
#[test_case(255)]
#[fuchsia::test]
async fn exit_code_event_stream(exit_code: i64) {
let realm = build_program(exit_code).await;
let proxy: fcomponent::EventStreamProxy =
realm.root.connect_to_protocol_at_exposed_dir().unwrap();
proxy.wait_for_ready().await.unwrap();
let mut event_stream = EventStream::new(proxy);
realm.start_component_tree().await.unwrap();
_ = realm
.root
.connect_to_named_protocol_at_exposed_dir::<fcomponent::BinderMarker>(
"exit_with_code_binder",
)
.unwrap();
let stopped = EventMatcher::ok()
.moniker(EXIT_WITH_CODE)
.wait::<Stopped>(&mut event_stream)
.await
.expect("failed to observe Stopped event");
let expected_status = exit_code_to_status(exit_code).into();
assert_eq!(stopped.result().unwrap().status, expected_status);
assert_eq!(stopped.result().unwrap().exit_code, Some(exit_code));
}
#[test_case(0)]
#[test_case(1)]
#[test_case(255)]
#[fuchsia::test]
async fn exit_code_execution_controller(exit_code: i64) {
let realm = build_program(exit_code).await;
realm.start_component_tree().await.unwrap();
let realm_proxy: fcomponent::RealmProxy =
realm.root.connect_to_protocol_at_exposed_dir().unwrap();
let (controller, controller_server_end) =
fidl::endpoints::create_proxy::<fcomponent::ControllerMarker>();
let () = realm_proxy
.open_controller(
&fdecl::ChildRef { name: EXIT_WITH_CODE.to_string(), collection: None },
controller_server_end,
)
.await
.unwrap()
.unwrap();
let (execution_controller, execution_controller_server_end) =
fidl::endpoints::create_proxy::<fcomponent::ExecutionControllerMarker>();
let () = controller
.start(fcomponent::StartChildArgs::default(), execution_controller_server_end)
.await
.unwrap()
.unwrap();
let mut event_stream = execution_controller.take_event_stream();
let event = event_stream.next().await.unwrap().unwrap();
match event {
fcomponent::ExecutionControllerEvent::OnStop { stopped_payload } => {
let expected_status = exit_code_to_status(exit_code);
assert_eq!(stopped_payload.status, Some(expected_status));
assert_eq!(stopped_payload.exit_code, Some(exit_code));
}
_ => unreachable!(),
}
}