//! Common integration test utilities.
//!
//! `miltertest` is spawned in a separate thread (not main), rather than have
//! the actual `Milter` be in a spawned thread and `miltertest` in main: just as
//! it is outside of tests.

use std::{
    ffi::OsString,
    path::PathBuf,
    process::{Command, ExitStatus},
    thread::{self, JoinHandle},
    time::Duration,
};

// Integration tests require the `miltertest` program.
const MILTERTEST: &str = "miltertest";

pub fn test_name(test_file_name: &str) -> OsString {
    PathBuf::from(test_file_name)
        .as_path()
        .file_stem()
        .expect("no file stem")
        .to_os_string()
}

pub fn spawn_miltertest_runner(test_file_name: &str) -> JoinHandle<ExitStatus> {
    let _timeout_thread = thread::spawn(|| {
        thread::sleep(Duration::from_secs(15));

        eprintln!("miltertest runner timed out");

        milter::shutdown();
    });

    let file_name = to_miltertest_file_name(test_file_name);

    thread::spawn(move || {
        thread::sleep(Duration::from_millis(100));

        let output = Command::new(MILTERTEST)
            .arg("-s")
            .arg(&file_name)
            .output()
            .expect("miltertest execution failed");

        print_output_stream("STDOUT", output.stdout);
        print_output_stream("STDERR", output.stderr);

        milter::shutdown();

        output.status
    })
}

fn to_miltertest_file_name(file_name: &str) -> OsString {
    let mut path = PathBuf::from(file_name);
    path.set_extension("lua");
    path.into_os_string()
}

fn print_output_stream(name: &str, output: Vec<u8>) {
    if !output.is_empty() {
        let output = String::from_utf8(output).unwrap();

        eprintln!("{}:", name);

        if output.ends_with('\n') {
            eprint!("{}", &output)
        } else {
            eprintln!("{}", &output)
        }
    }
}
