procspawn 1.0.2

thread::spawn just with processes
Documentation
use std::env;
use std::thread;
use std::time::Duration;

use procspawn::{self, spawn};

procspawn::enable_test_support!();

#[test]
fn test_basic() {
    let handle = spawn(true, |b| !b);
    let value = handle.join().unwrap();

    assert!(!value);
}

#[test]
fn test_panic() {
    let handle = spawn::<_, ()>((), |()| panic!("something went wrong"));
    let err = handle.join().unwrap_err();

    let panic_info = err.panic_info().unwrap();
    assert_eq!(panic_info.message(), "something went wrong");
    assert!(panic_info.backtrace().is_some());

    let loc = panic_info.location().unwrap();
    assert_eq!(loc.line(), 19);
    assert_eq!(loc.column(), 42);
    assert!(loc.file().contains("test_basic.rs"));
}

#[test]
fn test_kill() {
    let mut handle = spawn((), |()| {
        thread::sleep(Duration::from_secs(10));
    });
    handle.kill().unwrap();
    let err = handle.join().unwrap_err();
    dbg!(&err);
    assert!(err.is_remote_close());
}

#[test]
fn test_bad_roundtrip_does_not_hang() {
    let mut handle = spawn((), |()| BadRoundtrip);
    let err = handle.join_timeout(Duration::from_secs(2)).unwrap_err();
    assert!(!err.is_timeout());
}

#[test]
fn test_envvar() {
    let val = procspawn::Builder::new()
        .env("FOO", "42")
        .spawn(23, |val| {
            env::var("FOO").unwrap().parse::<i32>().unwrap() + val
        })
        .join()
        .unwrap();
    assert_eq!(val, 42 + 23);
}

#[test]
fn test_env_remove() {
    let removed = procspawn::Builder::new()
        .env_remove("PATH")
        .spawn((), |()| env::var_os("PATH").is_none())
        .join()
        .unwrap();
    assert!(removed);
}

#[test]
fn test_env_clear() {
    let env = procspawn::Builder::new()
        .env_clear()
        .env("PROCSPAWN_TEST_ENV", "present")
        .spawn((), |()| {
            (env::var_os("PATH"), env::var("PROCSPAWN_TEST_ENV").unwrap())
        })
        .join()
        .unwrap();
    assert_eq!(env, (None, "present".to_string()));
}

#[test]
fn test_nested() {
    let five = spawn(5, |x| {
        println!("1");
        let x = spawn(x, |y| {
            println!("2");
            y
        })
        .join()
        .unwrap();
        println!("3");
        x
    })
    .join()
    .unwrap();
    println!("4");
    assert_eq!(five, 5);
}

#[test]
fn test_timeout() {
    let mut handle = spawn((), |()| {
        thread::sleep(Duration::from_secs(10));
    });

    let err = handle.join_timeout(Duration::from_millis(100)).unwrap_err();
    assert!(err.is_timeout());

    let mut handle = spawn((), |()| {
        thread::sleep(Duration::from_millis(100));
        42
    });

    let val = handle.join_timeout(Duration::from_secs(2)).unwrap();
    assert_eq!(val, 42);
}

#[test]
fn test_timeout_can_be_retried() {
    let mut handle = spawn((), |()| {
        thread::sleep(Duration::from_millis(200));
        42
    });

    for _ in 0..2 {
        let err = handle.join_timeout(Duration::from_millis(10)).unwrap_err();
        assert!(err.is_timeout());
    }

    assert_eq!(handle.join_timeout(Duration::from_secs(2)).unwrap(), 42);
}

#[derive(Debug)]
struct BadRoundtrip;

impl serde::Serialize for BadRoundtrip {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_u8(42)
    }
}

impl<'de> serde::Deserialize<'de> for BadRoundtrip {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        <String as serde::Deserialize>::deserialize(deserializer)?;
        Ok(BadRoundtrip)
    }
}