use std::env;
use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use serde_json as json;
const TEST_FILE_VAR: &str = "TEST_FILE_PATH";
macro_rules! check_output {
($path: expr, $output:expr) => {
if !$output.status.success() {
std::io::stdout().write_all(&$output.stdout).unwrap();
std::io::stderr().write_all(&$output.stderr).unwrap();
panic!("{:?} failed: {:?}", $path, $output.status);
}
};
}
fn build_examples() -> Vec<(String, PathBuf)> {
let build = Command::new(env::var("CARGO").unwrap())
.arg("build")
.arg("--quiet")
.arg("--examples")
.args(["--message-format", "json"])
.output()
.unwrap();
check_output!("cargo build", build);
let mut examples = Vec::new();
for line in build.stdout.split(|c| *c == b'\n') {
if let Ok(msg) = json::from_slice::<json::Value>(line) {
if msg["reason"] == "compiler-artifact" {
let target = &msg["target"];
if target["kind"][0] == "example" {
let name = target["name"].as_str();
let file = msg["filenames"][0].as_str();
if let (Some(name), Some(file)) = (name, file) {
examples.push((name.to_string(), file.to_string().into()));
}
}
}
}
}
examples.sort();
examples
}
fn create_runner_file(target: &Path) -> PathBuf {
let rc_path = target.join("init.sh");
let mut output = BufWriter::new(File::create(&rc_path).unwrap());
macro_rules! w {
($($t:tt)*) => {
writeln!(&mut output, $($t)*).unwrap();
}
}
w!("exec 2>&1");
w!("load_example() {{");
w!("\tcase \"$1\" in");
for (name, path) in build_examples() {
w!("\t\t{}) enable -f '{}' {} ;;", name, path.display(), name);
}
w!("\t\t*) echo \"missing $1 example\"; return 1 ;;");
w!("\tesac");
w!("}}");
w!("source ${}", TEST_FILE_VAR);
rc_path
}
#[test]
fn check_examples() {
let target = {
let mut target = match env::var_os("CARGO_TARGET_DIR") {
Some(t) => PathBuf::from(t),
None => {
let mut target = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
target.push("target");
target
}
};
target.push("examples");
target.push(env::var_os("CARGO_PKG_NAME").unwrap());
std::fs::create_dir_all(&target).unwrap();
target
};
let test_runner = create_runner_file(&target);
let mut failed = 0;
for source in fs::read_dir("tests/examples").unwrap() {
let path = source.unwrap().path();
if path.extension() != Some(OsStr::new("sh")) {
continue;
}
let expected_output = {
let mut exp_path = path.clone();
exp_path.set_extension("output");
fs::read_to_string(exp_path).unwrap_or_default()
};
let bash = Command::new("bash")
.env("LC_ALL", "C")
.env("MALLOC_CHECK_", "2")
.env(TEST_FILE_VAR, &path)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.arg(&test_runner)
.spawn()
.unwrap();
let output = bash.wait_with_output().unwrap();
check_output!(path, output);
let test_output = String::from_utf8(output.stdout)
.unwrap()
.replace(test_runner.to_str().unwrap_or_default(), "$RUNNER");
if test_output != expected_output {
let test_name = path.file_name().unwrap();
let mut output_copy = target.join(test_name);
output_copy.set_extension("current-output");
failed += 1;
eprintln!("### {}: failed", path.display());
eprintln!("=== OUTPUT ({})\n{}\n", output_copy.display(), test_output);
eprintln!("=== EXPECTED\n{}\n", expected_output);
fs::write(output_copy, test_output).unwrap();
}
}
assert_eq!(failed, 0);
}