use std::ffi::OsStr;
use std::io::{Read, Write};
use std::process::{Child, Stdio};
#[macro_export]
macro_rules! run_current_example {
() => {
$crate::run_current_example!(::std::iter::empty::<&str>())
};
($args:literal) => {
$crate::run_current_example!(str::split_whitespace($args))
};
($args:expr $(,)?) => {
$crate::ExampleChild::run_new(
&::std::env::var("CARGO_PKG_NAME").unwrap(),
&$crate::extract_example_name(file!()).expect("Failed to determine example name."),
$args,
)
};
}
pub struct ExampleChild {
child: Child,
output_buffer: Vec<u8>,
output_len: usize,
}
impl ExampleChild {
pub fn run_new(
pkg_name: &str,
test_name: &str,
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
) -> Self {
let mut cargo_cmd = std::process::Command::new("cargo");
cargo_cmd
.args(["run", "--frozen", "--no-default-features"])
.args(["-p", pkg_name, "--example", test_name]);
if let Some(features) = trybuild_internals_api::features::find()
&& !features.is_empty()
{
cargo_cmd.args(["--features", &features.join(",")]);
}
cargo_cmd
.arg("--")
.args(args)
.env("RUNNING_AS_EXAMPLE_TEST", "1");
log::info!("Running cargo command: {:?}", cargo_cmd);
let child = cargo_cmd
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();
Self {
child,
output_buffer: vec![0; 1024],
output_len: 0,
}
}
pub fn read_string(&mut self, wait_for_string: &str) {
self.read_regex(®ex::escape(wait_for_string));
}
pub fn read_regex(&mut self, wait_for_regex: &str) {
let stdout = self.child.stdout.as_mut().unwrap();
let re = regex::Regex::new(wait_for_regex).unwrap();
while !re.is_match(&String::from_utf8_lossy(
&self.output_buffer[0..self.output_len],
)) {
eprintln!(
"waiting ({}):\n{}",
wait_for_regex,
String::from_utf8_lossy(&self.output_buffer[0..self.output_len])
);
while self.output_buffer.len() - self.output_len < 1024 {
self.output_buffer
.resize(self.output_buffer.len() + 1024, 0);
}
let bytes_read = stdout
.read(&mut self.output_buffer[self.output_len..])
.unwrap();
self.output_len += bytes_read;
if 0 == bytes_read {
panic!("Child process exited before a match was found.");
}
}
}
pub fn write_line(&mut self, line: &str) {
let stdin = self.child.stdin.as_mut().unwrap();
stdin.write_all(line.as_bytes()).unwrap();
stdin.write_all(b"\n").unwrap();
stdin.flush().unwrap();
}
}
impl Drop for ExampleChild {
fn drop(&mut self) {
#[cfg(target_family = "windows")]
let _ = self.child.kill(); #[cfg(not(target_family = "windows"))]
self.child.kill().unwrap();
self.child.wait().unwrap();
}
}
pub fn extract_example_name(file: &str) -> Option<String> {
let pathbuf = std::path::PathBuf::from(file);
let mut path = pathbuf.as_path();
while path.parent()?.file_name()? != "examples" {
path = path.parent().unwrap();
}
Some(path.file_stem()?.to_string_lossy().into_owned())
}