use std::path::{Path, PathBuf};
fn repo(rel: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join(rel)
}
fn ae_files(dir: &str) -> Vec<PathBuf> {
let mut out: Vec<PathBuf> = std::fs::read_dir(repo(dir))
.unwrap_or_else(|e| panic!("read {dir}: {e}"))
.filter_map(|e| e.ok().map(|e| e.path()))
.filter(|p| p.extension().is_some_and(|x| x == "ae"))
.collect();
out.sort();
out
}
fn parse_failures(files: &[PathBuf]) -> Vec<(String, String)> {
let mut bad = Vec::new();
for path in files {
let src = match std::fs::read_to_string(path) {
Ok(s) => s,
Err(e) => {
bad.push((path.display().to_string(), format!("unreadable: {e}")));
continue;
}
};
if let Err(e) = aethershell::parser::parse_program(&src) {
let first = e.to_string().lines().take(2).collect::<Vec<_>>().join(" ");
bad.push((
path.file_name().unwrap().to_string_lossy().to_string(),
first,
));
}
}
bad
}
fn report(kind: &str, bad: &[(String, String)]) -> String {
let mut s = format!("{} {kind} file(s) do not parse:\n", bad.len());
for (f, e) in bad {
s.push_str(&format!(" {f}\n {e}\n"));
}
s
}
#[test]
fn every_example_parses() {
let files = ae_files("examples");
assert!(
files.len() > 20,
"only {} examples found — this test is looking in the wrong place",
files.len()
);
let bad = parse_failures(&files);
assert!(bad.is_empty(), "{}", report("example", &bad));
}
#[test]
fn every_tracked_test_script_parses() {
let mut files = ae_files("test-scripts");
files.extend(ae_files("test-scripts/integration"));
assert!(
files.len() >= 8,
"only {} scripts found — this test is looking in the wrong place",
files.len()
);
let bad = parse_failures(&files);
assert!(bad.is_empty(), "{}", report("test-script", &bad));
}
#[test]
fn every_stdlib_file_parses() {
let files = ae_files("lib");
assert!(
!files.is_empty(),
"no .ae files in lib/ — this test is looking in the wrong place",
);
let bad = parse_failures(&files);
assert!(bad.is_empty(), "{}", report("stdlib", &bad));
}
#[test]
fn block_comments_are_not_valid_and_no_shipped_script_uses_them() {
assert!(
aethershell::parser::parse_program("/* nope */\n1\n").is_err(),
"`/* */` now parses — if block comments were added, this test and the \
comment rules in editors/vscode should learn about them together"
);
assert!(aethershell::parser::parse_program("# yes\n1\n").is_ok());
assert!(aethershell::parser::parse_program("// yes\n1\n").is_ok());
let mut offenders = Vec::new();
for dir in ["examples", "lib"] {
for path in ae_files(dir) {
if std::fs::read_to_string(&path)
.unwrap_or_default()
.contains("/*")
{
offenders.push(path.display().to_string());
}
}
}
assert!(
offenders.is_empty(),
"these shipped scripts contain `/*`, which the parser rejects: {offenders:#?}"
);
}