ic-host-process 0.9.6

Admitted host executable resolution, bounded execution and Git observations
Documentation
use super::*;
use crate::tool::{
    CommunicationLimits, ExecutionFailure, SuccessfulExit, communicate_child_with_observer,
};
use std::panic::{AssertUnwindSafe, catch_unwind};

#[test]
fn direct_spawn_preserves_inherited_group_context_and_io() {
    let fixture = Fixture::new();
    let mut command = command("read value; printf '%s|%s|%s' \"$value\" \"$SELECTED\" \"$PWD\"");
    command
        .current_dir(&fixture.root)
        .env_clear()
        .env("SELECTED", "direct")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped());
    let mut child = OwnedChild::spawn_direct(&mut command).unwrap();
    let pid = child.id();
    assert_eq!(
        rustix::process::getpgid(Pid::from_raw(i32::try_from(pid).unwrap())).unwrap(),
        rustix::process::getpgrp()
    );
    let mut observed = Vec::new();
    let evidence = communicate_child_with_observer(
        &mut child,
        Some(b"input\n"),
        crate::test_support::LIMITS,
        SuccessfulExit::Cleanup,
        || false,
        |_, bytes| observed.extend_from_slice(bytes),
    )
    .unwrap();
    assert_eq!(
        observed,
        format!("input|direct|{}", fixture.root.display()).as_bytes()
    );
    assert_eq!(evidence.stdout, observed);
    assert!(evidence.status.unwrap().success());
    assert_reaped(pid);
}

#[test]
fn direct_cleanup_preserves_other_members_of_the_selected_group() {
    let _fixture = Fixture::new();
    let mut keeper_command = command("cat");
    keeper_command.stdin(Stdio::piped()).stdout(Stdio::null());
    let mut keeper = OwnedChild::spawn(&mut keeper_command).unwrap();
    let group = i32::try_from(keeper.id()).unwrap();
    for case in ["success", "cancel", "overflow", "panic", "drop"] {
        let script = if case == "success" {
            "read value; printf ready"
        } else {
            "read value; printf ready; exec sleep 30"
        };
        let mut command = command(script);
        command
            .process_group(group)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped());
        let mut child = OwnedChild::spawn_direct(&mut command).unwrap();
        let pid = child.id();
        assert_eq!(
            rustix::process::getpgid(Pid::from_raw(i32::try_from(pid).unwrap())).unwrap(),
            Pid::from_raw(group).unwrap()
        );
        if case != "drop" {
            let result = catch_unwind(AssertUnwindSafe(|| {
                communicate_child_with_observer(
                    &mut child,
                    Some(b"start\n"),
                    CommunicationLimits {
                        stdout_bytes: if case == "overflow" { 1 } else { 5 },
                        stderr_bytes: 0,
                        timeout: None,
                    },
                    SuccessfulExit::Cleanup,
                    || case == "cancel",
                    |_, _| assert_ne!(case, "panic", "observer panic"),
                )
            }));
            match case {
                "success" => assert_eq!(result.unwrap().unwrap().stdout, b"ready"),
                "cancel" | "overflow" => {
                    let error = result.unwrap().unwrap_err();
                    let execution = error.execution_error().unwrap();
                    assert!(
                        matches!(execution.failure,
                        ExecutionFailure::Cancelled if case == "cancel")
                            || matches!(execution.failure,
                        ExecutionFailure::OutputLimit { .. } if case == "overflow")
                    );
                    assert!(execution.group_error.is_none() && execution.wait_error.is_none());
                }
                "panic" => assert!(result.is_err()),
                _ => unreachable!(),
            }
            assert_reaped(pid);
        }
        drop(child);
        assert_reaped(pid);
        assert!(
            keeper.poll_exit().unwrap().is_none(),
            "{case} signalled another member"
        );
    }
    keeper.terminate().unwrap();
}