From 7752d73216578d5961751b5d0535088d384b4aa6 Mon Sep 17 00:00:00 2001 From: Arnaud Bailly Date: Sat, 25 Jan 2025 10:45:41 +0100 Subject: Move λ-calcul workshop code to subdirectory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lambda-calcul/rust/src/tester.rs | 100 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 lambda-calcul/rust/src/tester.rs (limited to 'lambda-calcul/rust/src/tester.rs') diff --git a/lambda-calcul/rust/src/tester.rs b/lambda-calcul/rust/src/tester.rs new file mode 100644 index 0000000..eb66d4e --- /dev/null +++ b/lambda-calcul/rust/src/tester.rs @@ -0,0 +1,100 @@ +use serde::{Deserialize, Serialize}; +use std::{ + fs::{self, read_to_string, File}, + path::PathBuf, + process::{Command, Stdio}, + time::Instant, +}; + +pub fn main() { + let mut args: Vec = std::env::args().collect(); + // name of the process to run + let proc = args.remove(1); + if args.len() > 1 { + let run = traverse(&args) + .and_then(|paths| run_test(&proc, &paths)) + .expect("Failed to traverse directory"); + println!("{}", serde_json::to_string_pretty(&run).unwrap()); + } else { + println!( + r#"Usage: tester [options] + + +Options: + -p, --process The process to run. If the given process is not a + an absolute path, it will be resolved against the + PATH environment variable. + -j, --json Output the results in JSON format (default: false) + -h, --help Display this help message + -v, --version Display the version of the tester +"# + ); + } +} + +fn traverse(args: &[String]) -> Result, String> { + let mut files: Vec = Vec::new(); + for arg in args.iter().skip(1) { + let entries = fs::read_dir(arg).map_err(|e| e.to_string())?; + for entry in entries { + let dir = entry.map_err(|e| e.to_string())?; + let f = dir.metadata().map_err(|e| e.to_string())?; + if f.is_dir() { + files.push(dir.path()); + } + } + } + Ok(files) +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum TestResult { + TestSucceeded, + TestFailed(String, String), +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct TestRun { + file: String, + test_result: TestResult, + duration: u128, +} + +fn run_test(proc: &str, files: &Vec) -> Result, String> { + let mut result = Vec::new(); + for file in files { + let mut inp = file.clone(); + let mut outp = file.clone(); + inp.push("input"); + outp.push("output"); + let (test_result, duration) = run_test_case(proc, &inp, &outp)?; + result.push(TestRun { + file: inp.as_path().to_str().unwrap().to_string(), + test_result, + duration, + }); + } + Ok(result) +} + +fn run_test_case( + proc: &str, + inp: &std::path::PathBuf, + outp: &std::path::PathBuf, +) -> Result<(TestResult, u128), String> { + let input = File::open(inp).map_err(|e| e.to_string())?; + let expected = read_to_string(outp).map_err(|e| e.to_string())?; + let start = Instant::now(); + + let actual = Command::new(proc) + .stdin(Stdio::from(input)) + .output() + .map_err(|e| e.to_string())?; + + let duration = (Instant::now() - start).as_millis(); + if expected.as_bytes() == actual.stdout { + Ok((TestResult::TestSucceeded, duration)) + } else { + let actual_string = String::from_utf8(actual.stdout).map_err(|e| e.to_string())?; + Ok((TestResult::TestFailed(expected, actual_string), duration)) + } +} -- cgit v1.2.3