simple-cmd 0.0.12-alpha2

command line utility for spawning commands
Documentation
#[cfg(test)]
mod tests {
	use std::convert::Infallible;
	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::{debug, trace, warn};

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

	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);
	}

	#[test]
	fn test_error() {
		init_log!();
		let cmd = Cmd::builder("adb")
			.with_debug(true)
			.arg("push")
			.arg("/var/folders/h5/6_rkm34x20924137hx8_d7jr0000gn/T/1fc0ac62-7bd2-483d-8fcc-47ac37fcdf58")
			.arg("tvlib-aot-client.properties")
			.build();

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

		let error: crate::Error = output.into();
		warn!("error: {}", error);
	}

	#[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_pipe() {
		init_log!();
		let cancel = ctrlc_channel().unwrap();
		let builder = Cmd::builder("adb")
			.args(vec![
				"shell",
				"while true; do screenrecord --bit-rate 4000000 --output-format=h264 --size 1920x1080 -; done",
			])
			.timeout(Some(Duration::from_secs(60)))
			.signal(Some(cancel))
			.with_debug(true);

		let command1 = builder.build();

		let mut command2 = Command::new("ffplay");
		command2.args(vec![
			"-loglevel",
			"verbose",
			"-stats",
			"-an",
			"-autoexit",
			"-framerate",
			"30",
			"-probesize",
			"600",
			"-vf",
			"scale=1024:-1",
			"-sync",
			"video",
			"-",
		]);
		command2.stdout(Stdio::piped());

		let result = command1.pipe(command2).unwrap();

		println!();
		println!("result: {:?}", result);
	}

	#[test]
	fn test_pipe2() {
		init_log!();
		let command1 = Cmd::builder("adb")
			.args(vec![
				"shell", "dumpsys", "power",
			])
			.with_debug(true)
			.build();

		let command2 = Cmd::builder("sed")
			.arg("-n")
			.arg("s/mWakefulness=\\(\\S*\\)/\\1/p")
			.with_debug(true)
			.stdout(Some(Stdio::piped()))
			.build();

		let result = command1.pipe(command2).unwrap();
		println!();
		println!("result: {:?}", result);

		let output = result.stdout.as_str().unwrap().trim().to_lowercase();
		assert!(output == "awake" || output == "asleep");
	}
}