netsuke-build 0.1.0-beta1

A YAML-powered Ninja/Jinja hybrid build system.
//! Unit and property tests for Ninja process helpers.

use super::super::NINJA_PROGRAM;
use super::*;
use camino::Utf8PathBuf;
use proptest::prelude::*;
use std::ffi::OsString;
#[cfg(unix)]
use std::path::PathBuf;

#[test]
fn resolve_ninja_program_utf8_prefers_env_override() {
    let resolved = resolve_ninja_program_utf8_with(|_| Some(OsString::from("/opt/ninja")));
    assert_eq!(resolved, Utf8PathBuf::from("/opt/ninja"));
}

#[test]
fn resolve_ninja_program_utf8_defaults_without_override() {
    let resolved = resolve_ninja_program_utf8_with(|_| None);
    assert_eq!(resolved, Utf8PathBuf::from(NINJA_PROGRAM));
}

#[test]
fn resolve_ninja_program_utf8_defaults_for_empty_override() {
    let resolved = resolve_ninja_program_utf8_with(|_| Some(OsString::new()));
    assert_eq!(resolved, Utf8PathBuf::from(NINJA_PROGRAM));
}

#[cfg(unix)]
#[test]
fn resolve_ninja_program_utf8_ignores_invalid_utf8_override() {
    use std::os::unix::ffi::OsStringExt;

    let resolved = resolve_ninja_program_utf8_with(|_| {
        Some(OsString::from_vec(vec![0xff, b'n', b'i', b'n', b'j', b'a']))
    });
    assert_eq!(resolved, Utf8PathBuf::from(NINJA_PROGRAM));
}

proptest! {
    #[test]
    fn resolve_ninja_program_utf8_matches_utf8_env_invariant(
        override_value in prop::option::of(".*")
    ) {
        let env_value = override_value.clone().map(OsString::from);
        let expected = match override_value {
            Some(value) if !value.is_empty() => Utf8PathBuf::from(value),
            _ => Utf8PathBuf::from(NINJA_PROGRAM),
        };

        let resolved = resolve_ninja_program_utf8_with(|_| env_value.clone());

        prop_assert_eq!(resolved, expected);
    }
}

#[test]
fn finalize_streaming_joins_stderr_thread_when_wait_fails() {
    use std::sync::{
        Arc,
        atomic::{AtomicBool, Ordering},
    };
    use std::time::Duration;

    // The thread signals completion only after a short delay, so if
    // `finalize_streaming` failed to join it, the flag would still be unset
    // when the buggy path returned early on the wait error.
    let joined = Arc::new(AtomicBool::new(false));
    let worker_flag = Arc::clone(&joined);
    let err_handle = thread::spawn(move || {
        thread::sleep(Duration::from_millis(100));
        worker_flag.store(true, Ordering::SeqCst);
        ForwardStats::default()
    });

    let wait_result = Err(io::Error::other("simulated wait failure"));
    let result = finalize_streaming(wait_result, ForwardStats::default(), err_handle);

    assert!(
        result.is_err(),
        "a wait() failure must propagate after cleanup"
    );
    assert!(
        joined.load(Ordering::SeqCst),
        "the stderr forwarding thread must be joined even when wait() fails"
    );
}

#[cfg(unix)]
proptest! {
    #[test]
    fn resolve_ninja_program_utf8_falls_back_for_non_utf8_env_values(
        bytes in prop::collection::vec(any::<u8>(), 0..32)
    ) {
        use std::os::unix::ffi::OsStringExt;

        let env_value = OsString::from_vec(bytes);
        let expected = if env_value.as_os_str().is_empty() {
            Utf8PathBuf::from(NINJA_PROGRAM)
        } else {
            Utf8PathBuf::from_path_buf(PathBuf::from(env_value.clone()))
                .unwrap_or_else(|_| Utf8PathBuf::from(NINJA_PROGRAM))
        };

        let resolved = resolve_ninja_program_utf8_with(|_| Some(env_value.clone()));

        prop_assert_eq!(resolved, expected);
    }
}