diff --git a/tests/programs/io/read_and_print.hvm b/tests/programs/io/read_and_print.hvm new file mode 100644 index 00000000..d60a16b5 --- /dev/null +++ b/tests/programs/io/read_and_print.hvm @@ -0,0 +1,117 @@ +@IO_T/Call = (a (b (c (d ((1 (a (b (c (d e))))) e))))) + +@IO_T/Done = (a (b ((0 (a (b c))) c))) + +@IO_T/MAGIC = (13683217 16719857) + +@IO_T/bind = ((@IO_T/bind__C2 a) a) + +@IO_T/bind__C0 = (* (a ((a b) b))) + +@IO_T/bind__C1 = (* (* (a (b ((c d) (e g)))))) + & @IO_T/Call ~ (@IO_T/MAGIC (a (b ((c f) g)))) + & @IO_T/bind ~ (d (e f)) + +@IO_T/bind__C2 = (?((@IO_T/bind__C0 @IO_T/bind__C1) a) a) + +@IO_T/wrap = a + & @IO_T/Done ~ (@IO_T/MAGIC a) + +@String/Cons = (a (b ((1 (a (b c))) c))) + +@String/Nil = ((0 a) a) + +@call_io = (a (b c)) + & @IO_T/Call ~ (@IO_T/MAGIC (a (b (@call_io__C0 c)))) + +@call_io__C0 = a + & @IO_T/Done ~ (@IO_T/MAGIC a) + +@main = a + & @IO_T/bind ~ (@main__C7 (@main__C6 a)) + +@main__C0 = i + & @call_io ~ (e ((1 h) i)) + & @String/Cons ~ (87 (d e)) + & @String/Cons ~ (82 (c d)) + & @String/Cons ~ (73 (b c)) + & @String/Cons ~ (84 (a b)) + & @String/Cons ~ (69 (@String/Nil a)) + & @String/Cons ~ (39 (g h)) + & @String/Cons ~ (33 (f g)) + & @String/Cons ~ (10 (@String/Nil f)) + +@main__C1 = e + & @String/Cons ~ (87 (d e)) + & @String/Cons ~ (82 (c d)) + & @String/Cons ~ (73 (b c)) + & @String/Cons ~ (84 (a b)) + & @String/Cons ~ (69 (@String/Nil a)) + +@main__C2 = (* a) + & @IO_T/bind ~ (@main__C0 ((* 42) a)) + +@main__C3 = n + & @call_io ~ (e ((1 m) n)) + & @String/Cons ~ (87 (d e)) + & @String/Cons ~ (82 (c d)) + & @String/Cons ~ (73 (b c)) + & @String/Cons ~ (84 (a b)) + & @String/Cons ~ (69 (@String/Nil a)) + & @String/Cons ~ (72 (l m)) + & @String/Cons ~ (101 (k l)) + & @String/Cons ~ (108 (j k)) + & @String/Cons ~ (108 (i j)) + & @String/Cons ~ (111 (h i)) + & @String/Cons ~ (44 (g h)) + & @String/Cons ~ (32 (f g)) + & @String/Cons ~ (39 (@String/Nil f)) + +@main__C4 = (a d) + & @IO_T/bind ~ (@main__C3 ((* c) d)) + & @IO_T/bind ~ (b (@main__C2 c)) + & @call_io ~ (@main__C1 ((1 a) b)) + +@main__C5 = j + & @call_io ~ (i (0 j)) + & @String/Cons ~ (82 (h i)) + & @String/Cons ~ (69 (g h)) + & @String/Cons ~ (65 (f g)) + & @String/Cons ~ (68 (e f)) + & @String/Cons ~ (95 (d e)) + & @String/Cons ~ (76 (c d)) + & @String/Cons ~ (73 (b c)) + & @String/Cons ~ (78 (a b)) + & @String/Cons ~ (69 (@String/Nil a)) + +@main__C6 = (* a) + & @IO_T/bind ~ (@main__C5 (@main__C4 a)) + +@main__C7 = y + & @call_io ~ (e ((1 x) y)) + & @String/Cons ~ (87 (d e)) + & @String/Cons ~ (82 (c d)) + & @String/Cons ~ (73 (b c)) + & @String/Cons ~ (84 (a b)) + & @String/Cons ~ (69 (@String/Nil a)) + & @String/Cons ~ (87 (w x)) + & @String/Cons ~ (104 (v w)) + & @String/Cons ~ (97 (u v)) + & @String/Cons ~ (116 (t u)) + & @String/Cons ~ (32 (s t)) + & @String/Cons ~ (105 (r s)) + & @String/Cons ~ (115 (q r)) + & @String/Cons ~ (32 (p q)) + & @String/Cons ~ (121 (o p)) + & @String/Cons ~ (111 (n o)) + & @String/Cons ~ (117 (m n)) + & @String/Cons ~ (114 (l m)) + & @String/Cons ~ (32 (k l)) + & @String/Cons ~ (110 (j k)) + & @String/Cons ~ (97 (i j)) + & @String/Cons ~ (109 (h i)) + & @String/Cons ~ (101 (g h)) + & @String/Cons ~ (63 (f g)) + & @String/Cons ~ (10 (@String/Nil f)) + +@test-io = 1 diff --git a/tests/programs/f24.hvm b/tests/programs/numerics/f24.hvm similarity index 100% rename from tests/programs/f24.hvm rename to tests/programs/numerics/f24.hvm diff --git a/tests/programs/i24.hvm b/tests/programs/numerics/i24.hvm similarity index 100% rename from tests/programs/i24.hvm rename to tests/programs/numerics/i24.hvm diff --git a/tests/programs/u24.hvm b/tests/programs/numerics/u24.hvm similarity index 100% rename from tests/programs/u24.hvm rename to tests/programs/numerics/u24.hvm diff --git a/tests/run.rs b/tests/run.rs index 743c0add..2f881ad9 100644 --- a/tests/run.rs +++ b/tests/run.rs @@ -1,10 +1,16 @@ +use std::{ + collections::HashMap, + error::Error, + ffi::OsStr, + fs, + io::{Read, Write}, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; use hvm::ast::Tree; use insta::assert_snapshot; use TSPL::Parser; -use std::{ - collections::HashMap, error::Error, ffi::OsStr, fs, io::Read, path::{Path, PathBuf}, process::{Command, Stdio} -}; #[test] fn test_run_programs() { @@ -25,33 +31,65 @@ fn manifest_relative(sub: &str) -> PathBuf { } fn test_file(path: &Path) { - if fs::read_to_string(path).unwrap().contains("@test-skip = 1") { + let contents = fs::read_to_string(path).unwrap(); + if contents.contains("@test-skip = 1") { println!("skipping {path:?}"); return; } + if contents.contains("@test-io = 1") { + test_io_file(path); + return; + } + println!("testing {path:?}..."); - let rust_output = execute_hvm(&["run".as_ref(), path.as_os_str()]).unwrap(); + let rust_output = execute_hvm(&["run".as_ref(), path.as_os_str()], false).unwrap(); assert_snapshot!(rust_output); + println!(" testing {path:?}, C..."); - let c_output = execute_hvm(&["run-c".as_ref(), path.as_os_str()]).unwrap(); + let c_output = execute_hvm(&["run-c".as_ref(), path.as_os_str()], false).unwrap(); assert_eq!(c_output, rust_output, "{path:?}: C output does not match rust output"); + if cfg!(feature = "cuda") { println!(" testing {path:?}, CUDA..."); - let cuda_output = execute_hvm(&["run-cu".as_ref(), path.as_os_str()]).unwrap(); - assert_eq!(cuda_output, rust_output, "{path:?}: CUDA output does not match rust output"); + let cuda_output = execute_hvm(&["run-cu".as_ref(), path.as_os_str()], false).unwrap(); + assert_eq!( + cuda_output, rust_output, + "{path:?}: CUDA output does not match rust output" + ); + } +} + +fn test_io_file(path: &Path) { + println!(" testing (io) {path:?}, C..."); + let c_output = execute_hvm(&["run-c".as_ref(), path.as_os_str()], true).unwrap(); + assert_snapshot!(c_output); + + if cfg!(feature = "cuda") { + println!(" testing (io) {path:?}, CUDA..."); + let cuda_output = execute_hvm(&["run-cu".as_ref(), path.as_os_str()], true).unwrap(); + assert_eq!(cuda_output, c_output, "{path:?}: CUDA output does not match C output"); } } -fn execute_hvm(args: &[&OsStr]) -> Result> { +fn execute_hvm(args: &[&OsStr], send_io: bool) -> Result> { // Spawn the command - let mut child = - Command::new(env!("CARGO_BIN_EXE_hvm")).args(args).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?; + let mut child = Command::new(env!("CARGO_BIN_EXE_hvm")) + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; // Capture the output of the command let mut stdout = child.stdout.take().ok_or("Couldn't capture stdout!")?; let mut stderr = child.stderr.take().ok_or("Couldn't capture stderr!")?; // Wait for the command to finish and get the exit status + if send_io { + let mut stdin = child.stdin.take().ok_or("Couldn't capture stdin!")?; + stdin.write_all(b"io from the tests\n")?; + drop(stdin); + } let status = child.wait()?; // Read the output @@ -62,22 +100,27 @@ fn execute_hvm(args: &[&OsStr]) -> Result> { Ok(if !status.success() { format!("exited with code {status}:\n{output}") } else { - parse_output(&output).unwrap_or_else(|err| { - panic!("error parsing output:\n{err}\n\n{output}") - }) + parse_output(&output).unwrap_or_else(|err| panic!("error parsing output:\n{err}\n\n{output}")) }) } fn parse_output(output: &str) -> Result { - let mut parser = hvm::ast::CoreParser::new(output); - parser.consume("Result:")?; - let mut tree = parser.parse_tree()?; - normalize_vars(&mut tree, &mut HashMap::new()); - // TODO: include iteration count in snapshot once consistent - // parser.consume("- ITRS:")?; - // let itrs = parser.parse_u64()?; - // Ok(format!("Result: {}\n- ITRS: {}", tree.show(), itrs)) - Ok(format!("Result: {}", tree.show())) + let mut lines = Vec::new(); + + for line in output.lines() { + if line.starts_with("Result:") { + let mut parser = hvm::ast::CoreParser::new(line); + parser.consume("Result:")?; + let mut tree = parser.parse_tree()?; + normalize_vars(&mut tree, &mut HashMap::new()); + lines.push(format!("Result: {}", tree.show())); + } else if !line.starts_with("- ITRS:") && !line.starts_with("- TIME:") && !line.starts_with("- MIPS:") { + // TODO: include iteration count in snapshot once consistent + lines.push(line.to_string()) + } + } + + Ok(lines.join("\n")) } fn normalize_vars(tree: &mut Tree, vars: &mut HashMap) { @@ -85,7 +128,7 @@ fn normalize_vars(tree: &mut Tree, vars: &mut HashMap) { Tree::Var { nam } => { let next_var = vars.len(); *nam = format!("x{}", vars.entry(std::mem::take(nam)).or_insert(next_var)); - }, + } Tree::Era | Tree::Ref { .. } | Tree::Num { .. } => {} Tree::Con { fst, snd } | Tree::Dup { fst, snd } | Tree::Opr { fst, snd } | Tree::Swi { fst, snd } => { normalize_vars(fst, vars); diff --git a/tests/snapshots/run__file@f24.hvm.snap b/tests/snapshots/run__file@numerics__f24.hvm.snap similarity index 100% rename from tests/snapshots/run__file@f24.hvm.snap rename to tests/snapshots/run__file@numerics__f24.hvm.snap diff --git a/tests/snapshots/run__file@i24.hvm.snap b/tests/snapshots/run__file@numerics__i24.hvm.snap similarity index 100% rename from tests/snapshots/run__file@i24.hvm.snap rename to tests/snapshots/run__file@numerics__i24.hvm.snap diff --git a/tests/snapshots/run__file@u24.hvm.snap b/tests/snapshots/run__file@numerics__u24.hvm.snap similarity index 100% rename from tests/snapshots/run__file@u24.hvm.snap rename to tests/snapshots/run__file@numerics__u24.hvm.snap diff --git a/tests/snapshots/run__io_file@io__read_and_print.hvm.snap b/tests/snapshots/run__io_file@io__read_and_print.hvm.snap new file mode 100644 index 00000000..e00390ac --- /dev/null +++ b/tests/snapshots/run__io_file@io__read_and_print.hvm.snap @@ -0,0 +1,8 @@ +--- +source: tests/run.rs +expression: c_output +input_file: tests/programs/io/read_and_print.hvm +--- +What is your name? +Hello, 'io from the tests'! +Result: 42