blob: 92b4fff8ec712208356350009ac7ede994db43d8 [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.
#![cfg(test)]
use fdio::SpawnOptions;
use fuchsia_runtime as runtime;
use fuchsia_zircon::{self as zx, AsHandleRef, Task};
use std::ffi::{CStr, CString};
use std::mem::size_of_val;
#[test]
fn zx_singlethreaded() {
run_test_with_args(&["--spawn"], true);
run_test_with_args(&["--spawn", "--job"], true);
run_test_with_args(&["--spawn", "--kill"], true);
run_test_with_args(&["--spawn", "--job", "--kill"], true);
}
#[test]
fn zx_singlethreaded_with_panic() {
run_test_with_args(&["--spawn", "--panic"], false);
run_test_with_args(&["--spawn", "--panic", "--job"], false);
run_test_with_args(&["--spawn", "--panic", "--kill"], false);
run_test_with_args(&["--spawn", "--panic", "--job", "--kill"], false);
}
#[test]
fn zx_multithreaded() {
run_test_with_args(&["--spawn", "--spawn-on-thread"], true);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--job"], true);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--kill"], true);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--job", "--kill"], true);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--wait"], true);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--wait", "--job"], true);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--wait", "--kill"], true);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--wait", "--job", "--kill"], true);
}
#[test]
fn zx_multithreaded_with_panic() {
run_test_with_args(&["--spawn", "--spawn-on-thread", "--panic"], false);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--panic", "--job"], false);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--panic", "--kill"], false);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--panic", "--job", "--kill"], false);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--wait", "--panic"], false);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--wait", "--panic", "--job"], false);
run_test_with_args(&["--spawn", "--spawn-on-thread", "--wait", "--panic", "--kill"], false);
run_test_with_args(
&["--spawn", "--spawn-on-thread", "--wait", "--panic", "--job", "--kill"],
false,
);
}
#[test]
#[should_panic(expected = "did not exit")]
fn zx_singlethreaded_unscoped() {
run_test_with_args(&["--spawn", "--unscoped"], true);
}
#[test]
#[should_panic(expected = "did not exit")]
fn zx_singlethreaded_unscoped_with_panic() {
run_test_with_args(&["--spawn", "--unscoped", "--panic"], false);
}
fn run_test_with_args(args: &[&str], expect_success: bool) {
let args = args.iter().map(|s| CString::new(*s).unwrap()).collect::<Vec<_>>();
let args = args.iter().map(|s| s.as_c_str()).collect::<Vec<_>>();
run_test_with_c_args(&args, expect_success);
}
fn run_test_with_c_args(args: &[&CStr], expect_success: bool) {
println!("running spawner with args: {:?}", args);
let child_job = runtime::job_default().create_child_job().unwrap();
let bin = c"/pkg/bin/scoped_task_test_spawner";
let args: Vec<&CStr> = Some(bin).iter().chain(args.into_iter()).copied().collect();
let process = fdio::spawn(
&child_job,
// Avoid cloning stdio to prevent hangs in the case of a test failure.
SpawnOptions::CLONE_ALL, // - SpawnOptions::CLONE_STDIO,
bin,
&args,
)
.expect("could not spawn");
process
.wait_handle(zx::Signals::PROCESS_TERMINATED, zx::Time::INFINITE)
.expect("could not wait");
let info = process.info().unwrap();
if expect_success {
assert_eq!(0, info.return_code);
} else {
assert_ne!(0, info.return_code);
}
check_all_processes_terminated(&child_job);
}
fn check_all_processes_terminated(job: &zx::Job) {
use fuchsia_zircon::sys::*;
let mut koids: [zx_koid_t; 32] = Default::default();
let mut actual: usize = 0;
let mut avail: usize = 0;
let status = unsafe {
zx_object_get_info(
job.raw_handle(),
ZX_INFO_JOB_PROCESSES,
koids.as_mut_ptr() as *mut u8,
size_of_val(&koids),
&mut actual as *mut usize,
&mut avail as *mut usize,
)
};
assert_eq!(zx::Status::OK, zx::Status::from_raw(status));
assert_eq!(actual, avail, "too many child processes");
println!("process_koids={:?}", &koids[0..actual]);
for koid in &koids[0..actual] {
let process: zx::Process = unsafe {
let mut handle: zx_handle_t = Default::default();
let status = zx::Status::from_raw(zx_object_get_child(
job.raw_handle(),
*koid,
ZX_RIGHT_SAME_RIGHTS,
&mut handle as *mut zx_handle_t,
));
if status == zx::Status::NOT_FOUND {
// The process object can get cleaned up before we create a handle to it.
println!("object for process koid {} not found, continuing", koid);
continue;
}
assert_eq!(zx::Status::OK, status);
zx::Handle::from_raw(handle)
}
.into();
let info = process.info().unwrap();
if !zx::ProcessInfoFlags::from_bits(info.flags)
.unwrap()
.contains(zx::ProcessInfoFlags::EXITED)
{
process.kill().unwrap();
}
assert!(
zx::ProcessInfoFlags::from_bits(info.flags)
.unwrap()
.contains(zx::ProcessInfoFlags::EXITED),
"process koid {} did not exit",
koid
);
}
// Recurse into sub-jobs.
let status = unsafe {
zx_object_get_info(
job.raw_handle(),
ZX_INFO_JOB_CHILDREN,
koids.as_mut_ptr() as *mut u8,
size_of_val(&koids),
&mut actual as *mut usize,
&mut avail as *mut usize,
)
};
assert_eq!(zx::Status::OK, zx::Status::from_raw(status));
assert_eq!(actual, avail, "too many child jobs");
println!("job_koids={:?}", &koids[0..actual]);
for koid in &koids[0..actual] {
let job: zx::Job = unsafe {
let mut handle: zx_handle_t = Default::default();
let status = zx::Status::from_raw(zx_object_get_child(
job.raw_handle(),
*koid,
ZX_RIGHT_SAME_RIGHTS,
&mut handle as *mut zx_handle_t,
));
if status == zx::Status::NOT_FOUND {
// The job object can get cleaned up before we create a handle to it.
println!("object for job koid {} not found, continuing", koid);
continue;
}
assert_eq!(zx::Status::OK, status);
zx::Handle::from_raw(handle)
}
.into();
check_all_processes_terminated(&job);
}
}