Skip to content

Commit c5ad7a6

Browse files
committed
🧪 Generate individual test functions per .ndct file for test isolation
1 parent bda9b7a commit c5ad7a6

2 files changed

Lines changed: 57 additions & 42 deletions

File tree

‎tests/build.rs‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use std::env;
2+
use std::fs;
3+
use std::io::Write;
4+
use std::path::Path;
5+
6+
fn main() {
7+
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
8+
let programs_dir = Path::new(&manifest_dir).join("programs");
9+
10+
let out_dir = env::var("OUT_DIR").unwrap();
11+
let dest_path = Path::new(&out_dir).join("generated_tests.rs");
12+
let mut output = fs::File::create(dest_path).unwrap();
13+
14+
generate_tests(&mut output, &programs_dir, &programs_dir);
15+
16+
println!("cargo:rerun-if-changed=programs");
17+
}
18+
19+
fn generate_tests(output: &mut impl Write, base: &Path, dir: &Path) {
20+
let mut entries: Vec<_> = fs::read_dir(dir)
21+
.unwrap()
22+
.filter_map(|e| e.ok())
23+
.collect();
24+
entries.sort_by_key(|e| e.path());
25+
26+
for entry in entries {
27+
let path = entry.path();
28+
if path.is_dir() {
29+
generate_tests(output, base, &path);
30+
} else if path.extension().map_or(false, |e| e == "ndct") {
31+
let relative = path.strip_prefix(base).unwrap();
32+
let stem = relative
33+
.with_extension("")
34+
.to_string_lossy()
35+
.replace(['/', '\\', '-', ' '], "_");
36+
let test_name = format!("test_{stem}");
37+
let path_str = relative.to_string_lossy();
38+
39+
writeln!(output, "#[test]").unwrap();
40+
writeln!(output, "fn {test_name}() {{").unwrap();
41+
writeln!(
42+
output,
43+
" run_test(std::path::Path::new(env!(\"CARGO_MANIFEST_DIR\")).join(\"programs/{path_str}\")).expect(\"test failed\");",
44+
)
45+
.unwrap();
46+
writeln!(output, "}}").unwrap();
47+
}
48+
}
49+
}

‎tests/src/programs.rs‎

Lines changed: 8 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,13 @@
11
use ndc_lib::interpreter::Interpreter;
22
use owo_colors::OwoColorize;
33
use std::fs;
4-
use std::path::{Path, PathBuf};
4+
use std::path::PathBuf;
55

6-
#[test]
7-
fn example_programs() {
8-
run_dir(Path::new("../tests/programs/"));
9-
}
10-
11-
fn run_dir<P: AsRef<Path>>(dir: P) {
12-
match fs::read_dir(dir) {
13-
Ok(files) => {
14-
// Sort the files in to ensure the tests are executed in the intended order
15-
let mut files = files.collect::<Vec<_>>();
16-
assert_ne!(files.len(), 0, "no files were found at all");
17-
files.sort_unstable_by_key(|r| r.as_ref().map(|e| e.path()).ok());
18-
19-
for file in files {
20-
match file {
21-
Ok(file) if file.path().extension() == Some("ndct".as_ref()) => {
22-
run_test(file.path()).expect("something went wrong while running the test")
23-
}
24-
Ok(file) if file.path().is_dir() => {
25-
run_dir(file.path());
26-
}
27-
_ => panic!("invalid test file: {file:?}"),
28-
}
29-
}
30-
}
31-
Err(err) => panic!("Error reading test programs {err}"),
32-
}
33-
}
6+
include!(concat!(env!("OUT_DIR"), "/generated_tests.rs"));
347

358
fn run_test(path: PathBuf) -> Result<(), std::io::Error> {
36-
let contents = fs::read_to_string(path.clone())?;
9+
let contents = fs::read_to_string(&path)?;
10+
3711
enum Mode {
3812
None,
3913
Program,
@@ -48,15 +22,9 @@ fn run_test(path: PathBuf) -> Result<(), std::io::Error> {
4822

4923
for line in contents.split_inclusive('\n') {
5024
match line {
51-
"--PROGRAM--\n" => {
52-
mode = Mode::Program;
53-
}
54-
"--EXPECT--\n" => {
55-
mode = Mode::Expect;
56-
}
57-
"--EXPECT-ERROR--\n" => {
58-
mode = Mode::ExpectError;
59-
}
25+
"--PROGRAM--\n" => mode = Mode::Program,
26+
"--EXPECT--\n" => mode = Mode::Expect,
27+
"--EXPECT-ERROR--\n" => mode = Mode::ExpectError,
6028
_ => match mode {
6129
Mode::None => panic!("unexpected line in file, not in mode"),
6230
Mode::Program => program.push_str(line),
@@ -65,22 +33,20 @@ fn run_test(path: PathBuf) -> Result<(), std::io::Error> {
6533
},
6634
}
6735
}
68-
// For now let's trim end both result and expect to ensure that any trailing line breaks don't cause issues
36+
6937
print!("Running {path:?}...");
7038

7139
let mut interpreter = Interpreter::new(Vec::new());
7240
let interpreter_result = interpreter.run_str(&program, false);
7341

7442
let program_had_error = interpreter_result.is_err();
75-
7643
let actual_error = interpreter_result.unwrap_or_else(|err| format!("{err:?}"));
7744

7845
let environment = interpreter.environment();
7946
let environment = environment.borrow();
8047
let output = environment
8148
.get_output()
8249
.expect("interpreter must have output in test context");
83-
8450
let output = String::from_utf8(output).expect("test output must be valid UTF-8");
8551

8652
assert!(

0 commit comments

Comments
 (0)