summaryrefslogtreecommitdiff
path: root/lambda-calcul/rust/src/tester.rs
diff options
context:
space:
mode:
Diffstat (limited to 'lambda-calcul/rust/src/tester.rs')
-rw-r--r--lambda-calcul/rust/src/tester.rs100
1 files changed, 100 insertions, 0 deletions
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<String> = 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] <directory>+
+
+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<Vec<PathBuf>, String> {
+ let mut files: Vec<PathBuf> = 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<PathBuf>) -> Result<Vec<TestRun>, 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))
+ }
+}