simple-cmd 0.0.22

command line utility for spawning commands
Documentation
#[cfg(test)]
mod tests {
    use std::convert::Infallible;
    use std::io::BufRead;
    use std::path::Path;
    use std::process::{Command, Stdio};
    use std::sync::Once;
    use std::thread;
    use std::thread::sleep;
    use std::time::{Duration, Instant};

    use crossbeam_channel::{bounded, Receiver};
    use tracing::trace;

    use crate::{Cmd, Vec8ToString};
    use crate::debug::CommandDebug;
    use crate::prelude::OutputExt;

    static INIT: Once = Once::new();

    macro_rules! init_log {
		() => {
			INIT.call_once(|| {
				let subscriber = tracing_subscriber::fmt()
					.compact()
					.with_file(false)
					.with_line_number(false)
					.with_max_level(tracing::Level::TRACE)
					.with_thread_ids(false)
					.with_thread_names(true)
					.finish();
				tracing::subscriber::set_global_default(subscriber).unwrap();
			})
		};
	}

    #[allow(dead_code)]
    fn ctrlc_channel() -> Result<Receiver<()>, ctrlc::Error> {
        let (sender, receiver) = bounded(1);
        ctrlc::set_handler(move || {
            println!("sending CTRL+C to ctrl_channel");
            let _ = sender.send(());
        })?;
        Ok(receiver)
    }

    fn cancel_signal(timeout: Duration) -> Result<Receiver<()>, Infallible> {
        let (s, r) = bounded(1);
        thread::spawn(move || {
            sleep(timeout);
            trace!("sending CTRL+C signal...");
            let _ = s.send(());
        });

        Ok(r)
    }

    #[test]
    fn test_simple() {
        init_log!();
        let cmd = Cmd::builder("ls").with_debug(true).build();
        let output = cmd.output().expect("failed to wait for command");

        trace!("output: {:#?}", output);

        assert!(output.success());
        assert!(!output.error());
        assert!(!output.interrupt());
        assert!(output.signal().is_none());
    }

    #[test]
    fn test_current_dir() {
        init_log!();
        let cmd = Cmd::builder("ls")
            .args(["-la"])
            .current_dir("/Users/alessandro/Documents/Projects")
            .with_debug(true);
        assert_eq!(Some(Path::new("/Users/alessandro/Documents/Projects")), cmd.get_current_dir());

        let output = cmd.build().output().expect("failed to run command");
        for line in output.stdout.lines().into_iter() {
            println!("{}", line.unwrap());
        }
    }

    #[test]
    fn test_error() {
        init_log!();
        let cmd = Cmd::builder("sleep")
            .with_debug(true)
            .arg("10")
            .with_timeout(Duration::from_secs(5))
            .build();

        let output = cmd.output().expect("failed to wait for command");
        println!("output: {:#?}", output);

        assert!(output.error());
        assert!(!output.success());
        assert!(output.has_signal());
        assert!(!output.interrupt());
        assert!(!output.has_stdout());
    }

    #[test]
    fn test_sleep() {
        init_log!();
        let cmd = Cmd::builder("sleep")
            .arg("1")
            .timeout(Some(Duration::from_millis(100)))
            .with_debug(true)
            .build();
        let output = cmd.output().expect("failed to wait for command");
        trace!("output: {:#?}", output);

        assert!(!output.status.success());
        assert!(!output.interrupt());
        assert!(output.kill());
    }

    #[test]
    fn test_run() {
        init_log!();
        let now = Instant::now();
        let pool = threadpool::Builder::new().num_threads(2).build();

        pool.execute(move || {
            let _r = Cmd::builder("sleep").arg("2").build().run();
            trace!("cmd 1 done");
        });

        pool.execute(move || {
            let _r = Cmd::builder("sleep").arg("2").build().run();
            trace!("cmd 2 done");
        });

        pool.execute(move || {
            let _r = Cmd::builder("sleep").arg("2").build().run();
            trace!("cmd 3 done");
        });

        pool.execute(move || {
            let _r = Cmd::builder("sleep").arg("2").build().run();
            trace!("cmd 4 done");
        });

        pool.join();

        let elapsed = now.elapsed();

        trace!("done in {:?}ms", elapsed.as_millis());
        debug_assert!(
            elapsed < Duration::from_secs(2),
            "Expected less than 2 seconds, but got {:?}",
            elapsed
        );
    }

    #[test]
    fn test_cancel_signal() {
        init_log!();
        let cancel_signal = cancel_signal(Duration::from_secs(1)).unwrap();
        let cmd = Cmd::builder("sleep")
            .arg("2")
            .with_debug(true)
            .signal(Some(cancel_signal))
            .build();
        let output = cmd.output().expect("failed to wait for command");
        trace!("output: {:#?}", output);

        assert!(!output.status.success());
        assert!(output.kill());
        assert!(!output.interrupt());
    }

    #[test]
    fn test_to_command() {
        init_log!();
        let builder = Cmd::builder("sleep").arg("1");
        let mut cmd: Command = builder.into();
        cmd.debug();
        let _ = cmd.output().unwrap();
    }

    #[test]
    fn test_cmd_to_string() {
        let builder = Cmd::builder("sleep").args(vec![
            "1", "2",
        ]);
        let cmd_string = builder.to_string();
        assert_eq!(cmd_string, "sleep 1 2".to_string());
    }

    #[test]
    fn test_git_shortlog() {
        init_log!();
        let builder = Cmd::builder("git")
            .current_dir("/Users/alessandro/Documents/git/swisscom/aot-lib")
            .with_arg("--no-pager")
            .with_args(&["shortlog", "-sne", "--all"])
            .with_debug(true)
            .with_timeout(Duration::from_secs(2));

        let command = builder.build();
        let output = command.output();
        println!("output: {:?}", output);

        if let Ok(output) = output {
            for line in output.stdout.lines().into_iter() {
                println!("line: {:?}", line);
            }
        }
    }

    #[test]
    fn test_pipe() {
        init_log!();
        let builder = Cmd::builder("echo").args(&["hello pretty world"]).with_debug(true);

        let command1 = builder.build();

        let mut command2 = Command::new("sed");
        command2.args(&["s/pretty/_/"]);
        command2.stdout(Stdio::piped());

        let result = command1.pipe(command2).unwrap();
        let output = result.stdout.as_str().unwrap().trim();

        assert!(result.success());
        assert_eq!("hello _ world", output);
    }
}