use std::fs::{self, File};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
fn example_scripts(dir: &Path) -> Vec<PathBuf> {
let mut v: Vec<PathBuf> = fs::read_dir(dir)
.expect("examples/ dir")
.flatten()
.map(|e| e.path())
.filter(|p| p.extension().is_some_and(|x| x == "vim"))
.collect();
v.sort();
v
}
fn has_vim_error(line: &str) -> bool {
line.match_indices('E').any(|(i, _)| {
let rest = &line[i + 1..];
let ndigits = rest.chars().take_while(char::is_ascii_digit).count();
ndigits > 0 && rest[ndigits..].starts_with(':')
})
}
#[test]
fn examples_self_tests_pass() {
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
let bin = env!("CARGO_BIN_EXE_viml");
let ex_dir = root.join("examples");
let fixtures = root.join("tests/fixtures");
let scripts = example_scripts(&ex_dir);
assert!(!scripts.is_empty(), "no examples/*.vim scripts found");
let mut failures: Vec<String> = Vec::new();
for script in &scripts {
let stem = script.file_stem().unwrap().to_str().unwrap();
let stdin = match File::open(fixtures.join(format!("{stem}.in"))) {
Ok(f) => Stdio::from(f),
Err(_) => Stdio::null(),
};
let out = Command::new(bin)
.arg(script)
.stdin(stdin)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.expect("spawn viml");
let stderr = String::from_utf8_lossy(&out.stderr);
if !out.status.success() {
failures.push(format!(
"{stem}: exited {:?}\n--- stdout ---\n{}--- stderr ---\n{stderr}",
out.status.code(),
String::from_utf8_lossy(&out.stdout),
));
} else if let Some(err) = stderr.lines().find(|l| has_vim_error(l)) {
failures.push(format!("{stem}: Vim error on stderr: {err}"));
}
}
assert!(
failures.is_empty(),
"example self-test failures:\n\n{}",
failures.join("\n\n")
);
}