use std::{
io::Read,
process::{Child, Command, ExitStatus},
thread,
time::Duration,
};
use os_pipe::pipe;
use rstest::rstest;
use serial_test::serial;
struct RunResult {
exit_status: ExitStatus,
output: String,
}
fn run(args: &[&str], terminate: bool) -> RunResult {
let (mut reader, mut handle) = run_command(args);
if terminate {
#[cfg(target_family = "unix")]
wait_and_terminate(&handle);
}
let mut probe_run_output = String::new();
reader.read_to_string(&mut probe_run_output).unwrap();
let exit_status = handle.wait().unwrap();
let output = truncate_output(probe_run_output);
RunResult {
exit_status,
output,
}
}
#[cfg(target_family = "unix")]
fn wait_and_terminate(handle: &Child) {
thread::sleep(Duration::from_secs(5));
nix::sys::signal::kill(
nix::unistd::Pid::from_raw(handle.id() as i32),
nix::sys::signal::Signal::SIGINT,
)
.expect("cannot send ctrl-c");
}
fn run_command(args: &[&str]) -> (os_pipe::PipeReader, Child) {
let mut cmd = vec!["run", "--", "--chip", "nRF52840_xxAA", "--shorten-paths"];
cmd.extend(&args[1..]);
let path = format!("tests/test_elfs/{}", args[0]);
cmd.push(path.as_str());
let (reader, writer) = pipe().unwrap();
let handle = Command::new("cargo")
.args(cmd)
.stdout(writer.try_clone().unwrap())
.stderr(writer)
.spawn()
.unwrap();
(reader, handle)
}
fn truncate_output(probe_run_output: String) -> String {
probe_run_output
.lines()
.filter(|line| {
!line.starts_with(" Finished")
&& !line.starts_with(" Running `")
&& !line.starts_with(" Blocking waiting for file lock ")
&& !line.starts_with(" Compiling probe-run v")
&& !line.starts_with("└─ ") })
.map(|line| format!("{line}\n"))
.collect()
}
#[rstest]
#[case::successful_run_has_no_backtrace("hello-rzcobs", true)]
#[case::raw_encoding("hello-raw", true)]
#[case::successful_run_can_enforce_backtrace("hello-rzcobs --backtrace=always", true)]
#[case::stack_overflow_is_reported_as_such("overflow-rzcobs", false)]
#[case::panic_is_reported_as_such("panic-rzcobs", false)]
#[should_panic] #[case::panic_verbose("panic-rzcobs --verbose", false)]
#[case::unsuccessful_run_can_suppress_backtrace("panic-rzcobs --backtrace=never", false)]
#[case::stack_overflow_can_suppress_backtrace("overflow-rzcobs --backtrace=never", false)]
#[case::canary("overflow-no-flip-link", false)]
#[serial]
#[ignore = "requires the target hardware to be present"]
fn snapshot_test(#[case] args: &str, #[case] success: bool) {
let args = args.split(' ').collect::<Vec<_>>();
let run_result = run(args.as_slice(), false);
assert_eq!(success, run_result.exit_status.success());
insta::assert_snapshot!(run_result.output);
}
#[test]
#[serial]
#[ignore = "requires the target hardware to be present"]
#[cfg(target_family = "unix")]
fn ctrl_c_by_user_is_reported_as_such() {
let args = &["silent-loop-rzcobs"];
let run_result = run(args, true);
assert!(!run_result.exit_status.success());
insta::assert_snapshot!(run_result.output);
}
#[rstest]
#[case::without_time(&["levels-rzcobs", "--log-format", "[{L}] Location<{f}:{l}> {s}"])]
#[case::with_time(&["levels-with-timestamp", "--log-format", "{t} [{L}] Location<{f}:{l}> {s}"])]
#[case::with_time_but_no_impl(&["levels-rzcobs", "--log-format", "{t} [{L}] Location<{f}:{l}> {s}"])]
#[case::without_time_but_with_impl(&["levels-with-timestamp", "--log-format", "[{L}] Location<{f}:{l}> {s}"])]
#[case::host_without_time(&["levels-rzcobs", "--host-log-format", "[{L}] Location<{f}:{l}> {s}"])]
#[case::host_with_timestamp(&["levels-with-timestamp", "--host-log-format", "{t} [{L}] Location<{f}:{l}> {s}"])]
#[serial]
#[ignore = "requires the target hardware to be present"]
fn log_format(#[case] args: &[&str]) {
let run_result = run(args, false);
assert!(run_result.exit_status.success());
insta::assert_snapshot!(run_result.output);
}