From f59763da6712a09e71def448f6110bd272a59c2c Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 15 Jan 2018 23:42:09 -0700 Subject: [PATCH] start adding output checks to test --- src/main.rs | 28 +--- src/prolog/io.rs | 6 +- src/prolog/machine/mod.rs | 6 +- src/test_utils.rs | 177 +++++++++++++++++++++++++ src/tests.rs | 270 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 459 insertions(+), 28 deletions(-) create mode 100644 src/test_utils.rs create mode 100644 src/tests.rs diff --git a/src/main.rs b/src/main.rs index 0b52d046..1a9671ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,16 @@ #[macro_use] extern crate lazy_static; extern crate termion; + mod prolog; +#[macro_use] mod test_utils; use prolog::io::*; use prolog::machine::*; use prolog::parser::toplevel::*; #[cfg(test)] -mod tests { - use super::*; - use prolog::codegen::*; - - fn submit(wam: &mut Machine, buffer: &str) -> bool { - wam.reset(); - - match parse_code(buffer.trim(), wam.op_dir()) { - Ok(tl) => - match eval(wam, &tl) { - EvalSession::InitialQuerySuccess(_, _) | - EvalSession::EntrySuccess | - EvalSession::SubsequentQuerySuccess => - true, - _ => false - }, - Err(e) => panic!("parse error: {:?}", e) - } - } - +mod tests; + /* #[test] fn test_queries_on_facts() { let mut wam = Machine::new(); @@ -146,7 +130,7 @@ mod tests { submit(&mut wam, "p(X, a). p(X, Y) :- q(Y), p(X, X)."); assert_eq!(submit(&mut wam, "?- p(X, Y)."), true); // infinite. - assert_eq!(submit(&mut wam, "?- p(X, b)."), false); // infinite. + assert_eq!(submit(&mut wam, "?- p(X, b)."), false); . submit(&mut wam, "p(a, z). p(X, Y) :- q(Y), p(X, Y)."); @@ -791,7 +775,7 @@ mod tests { assert_eq!(submit(&mut wam, "?- X is 10 rem -3, X = 1."), true); assert_eq!(submit(&mut wam, "?- X is 10 mod -3, X is -2."), true); } -} +} */ fn process_buffer(wam: &mut Machine, buffer: &str) { diff --git a/src/prolog/io.rs b/src/prolog/io.rs index 5a573f68..d7e870f8 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -441,9 +441,11 @@ pub fn print(wam: &mut Machine, result: EvalSession) { loop { let mut result = EvalSession::QueryFailure; - let bindings = wam.heap_view::(&heap_locs); + let mut output = PrinterOutputter::new(); + + let bindings = wam.heap_view(&heap_locs, output).result(); - let stdin = stdin(); + let stdin = stdin(); let mut stdout = stdout().into_raw_mode().unwrap(); write!(stdout, "{}", bindings).unwrap(); diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index 9a260a82..e3128fe8 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -349,11 +349,9 @@ impl Machine { } } - pub fn heap_view(&self, var_dir: &HeapVarDict) -> Outputter::Output + pub fn heap_view(&self, var_dir: &HeapVarDict, mut output: Outputter) -> Outputter where Outputter: HeapCellValueOutputter { - let mut output = Outputter::new(); - for (var, addr) in var_dir { output.begin_new_var(); @@ -363,7 +361,7 @@ impl Machine { output = self.ms.print_term(addr, TermFormatter {}, output); } - output.result() + output } pub fn or_stack_is_empty(&self) -> bool { diff --git a/src/test_utils.rs b/src/test_utils.rs new file mode 100644 index 00000000..744d0167 --- /dev/null +++ b/src/test_utils.rs @@ -0,0 +1,177 @@ +use prolog::codegen::*; +use prolog::fixtures::*; +use prolog::heap_print::*; +use prolog::io::*; +use prolog::machine::*; +use prolog::parser::toplevel::*; + +use std::collections::HashSet; + +pub struct TestOutputter { + contents: Vec +} + +impl HeapCellValueOutputter for TestOutputter { + type Output = HashSet; + + fn new() -> Self { + TestOutputter { contents: vec![] } + } + + fn append(&mut self, contents: &str) { + if let Some(ref mut result) = self.contents.last_mut() { + **result += contents; + } + } + + fn begin_new_var(&mut self) { + self.contents.push(String::new()); + } + + fn result(self) -> Self::Output { + self.contents.into_iter().collect() + } + + fn ends_with(&self, s: &str) -> bool { + if let Some(ref result) = self.contents.last() { + result.ends_with(s) + } else { + false + } + } + + fn len(&self) -> usize { + if let Some(ref result) = self.contents.last() { + result.len() + } else { + 0 + } + } + + fn truncate(&mut self, len: usize) { + if let Some(ref mut result) = self.contents.last_mut() { + result.truncate(len); + } + } +} + +pub fn collect_test_output<'a>(wam: &mut Machine, alloc_locs: AllocVarDict<'a>, + mut heap_locs: HeapVarDict<'a>) + -> HashSet +{ + let mut output = TestOutputter::new(); + output = wam.heap_view(&heap_locs, output); + + while let EvalSession::SubsequentQuerySuccess = wam.continue_query(&alloc_locs, &mut heap_locs) + { + output = wam.heap_view(&heap_locs, output); + } + + output.result() +} + +pub fn collect_test_output_with_limit<'a>(wam: &mut Machine, alloc_locs: AllocVarDict<'a>, + mut heap_locs: HeapVarDict<'a>, limit: usize) + -> HashSet +{ + let mut output = TestOutputter::new(); + output = wam.heap_view(&heap_locs, output); + + let mut count = 1; + + if count == limit { + return output.result(); + } + + while let EvalSession::SubsequentQuerySuccess = wam.continue_query(&alloc_locs, &mut heap_locs) + { + output = wam.heap_view(&heap_locs, output); + + count += 1; + + if count == limit { + break; + } + } + + output.result() +} + +pub fn submit(wam: &mut Machine, buffer: &str) -> bool +{ + wam.reset(); + + match parse_code(buffer.trim(), wam.op_dir()) { + Ok(tl) => + match eval(wam, &tl) { + EvalSession::InitialQuerySuccess(_, _) | + EvalSession::EntrySuccess | + EvalSession::SubsequentQuerySuccess => + true, + _ => false + }, + Err(e) => panic!("parse error: {:?}", e) + } +} + +pub fn submit_query(wam: &mut Machine, buffer: &str, result: HashSet) -> bool +{ + wam.reset(); + + match parse_code(buffer.trim(), wam.op_dir()) { + Ok(tl) => + match eval(wam, &tl) { + EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => + result == collect_test_output(wam, alloc_locs, heap_locs), + EvalSession::EntrySuccess => true, + _ => false + }, + Err(e) => panic!("parse error: {:?}", e) + } +} + +pub fn submit_query_with_limit(wam: &mut Machine, buffer: &str, + result: HashSet, limit: usize) + -> bool +{ + wam.reset(); + + match parse_code(buffer.trim(), wam.op_dir()) { + Ok(tl) => + match eval(wam, &tl) { + EvalSession::InitialQuerySuccess(alloc_locs, heap_locs) => + result == collect_test_output_with_limit(wam, alloc_locs, + heap_locs, limit), + EvalSession::EntrySuccess => true, + _ => false + }, + Err(e) => panic!("parse error: {:?}", e) + } +} + +macro_rules! expand_strs { + ($arr:expr) => ( + $arr.into_iter().map(|s| String::from(*s)).collect() + ) +} + +macro_rules! assert_prolog_success_with_limit { + ($wam:expr, $buf:expr, $res:expr, $limit:expr) => ( + assert!(submit_query_with_limit($wam, $buf, expand_strs!($res), $limit)) + ) +} + +macro_rules! assert_prolog_failure { + ($wam: expr, $buf: expr) => ( + assert_eq!(submit($wam, $buf), false) + ) +} + +macro_rules! assert_prolog_success { + ($wam:expr, $query:expr, $res:expr) => ( + assert!(submit_query($wam, $query, expand_strs!($res))) + ); + ($wam:expr, $buf:expr) => ( + assert_eq!(submit($wam, $buf), true) + ) +} diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 00000000..82191113 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,270 @@ +use super::*; +use test_utils::*; + +#[test] +fn test_queries_on_facts() +{ + let mut wam = Machine::new(); + + submit(&mut wam, "p(Z, Z)."); + submit(&mut wam, "clouds(are, nice)."); + + assert_prolog_success!(&mut wam, "?- p(Z, Z).", ["Z = _0"]); + assert_prolog_success!(&mut wam, "?- p(Z, z).", ["Z = z"]); + assert_prolog_success!(&mut wam, "?- p(Z, w).", ["Z = w"]); + + assert_prolog_failure!(&mut wam, "?- p(z, w)."); + + assert_prolog_success!(&mut wam, "?- p(w, w)."); + + assert_prolog_failure!(&mut wam, "?- clouds(Z, Z)."); + + assert_prolog_success!(&mut wam, "?- clouds(are, Z).", ["Z = nice"]); + assert_prolog_success!(&mut wam, "?- clouds(Z, nice).", ["Z = are"]); + + submit(&mut wam, "p(Z, h(Z, W), f(W))."); + + assert_prolog_failure!(&mut wam, "?- p(z, h(z, z), f(w))."); + assert_prolog_success!(&mut wam, "?- p(z, h(z, w), f(w))."); + assert_prolog_success!(&mut wam, "?- p(z, h(z, W), f(w)).", ["W = w"]); + assert_prolog_success!(&mut wam, "?- p(Z, h(Z, w), f(Z)).", ["Z = w"]); + assert_prolog_failure!(&mut wam, "?- p(z, h(Z, w), f(Z))."); + + submit(&mut wam, "p(f(X), h(Y, f(a)), Y)."); + + assert_prolog_success!(&mut wam, "?- p(Z, h(Z, W), f(W)).", ["W = f(a)", "Z = f(f(a))"]); +} + +#[test] +fn test_queries_on_rules() { + let mut wam = Machine::new(); + + submit(&mut wam, "p(X, Y) :- q(X, Z), r(Z, Y)."); + submit(&mut wam, "q(q, s)."); + submit(&mut wam, "r(s, t)."); + + assert_prolog_success!(&mut wam, "?- p(X, Y).", ["Y = t", "X = q"]); + assert_prolog_success!(&mut wam, "?- p(q, t)."); + assert_prolog_failure!(&mut wam, "?- p(t, q)."); + assert_prolog_success!(&mut wam, "?- p(q, T).", ["T = t"]); + assert_prolog_failure!(&mut wam, "?- p(t, t)."); + + submit(&mut wam, "p(X, Y) :- q(f(f(X)), R), r(S, T)."); + submit(&mut wam, "q(f(f(X)), r)."); + + assert_prolog_success!(&mut wam, "?- p(X, Y).", ["X = _0", "Y = _1"]); + + submit(&mut wam, "q(f(f(x)), r)."); + + assert_prolog_success!(&mut wam, "?- p(X, Y).", ["X = x", "Y = _1"]); + + submit(&mut wam, "p(X, Y) :- q(X, Y), r(X, Y)."); + submit(&mut wam, "q(s, t)."); + submit(&mut wam, "r(X, Y) :- r(a)."); + submit(&mut wam, "r(a)."); + + assert_prolog_success!(&mut wam, "?- p(X, Y).", ["X = s", "Y = t"]); + assert_prolog_failure!(&mut wam, "?- p(t, S)."); + assert_prolog_success!(&mut wam, "?- p(s, T).", ["T = t"]); + assert_prolog_success!(&mut wam, "?- p(S, t).", ["S = s"]); + + submit(&mut wam, "p(f(f(a), g(b), X), g(b), h) :- q(X, Y)."); + submit(&mut wam, "q(X, Y)."); + + assert_prolog_success!(&mut wam, "?- p(f(X, Y, Z), g(b), h).", ["Z = _3", "Y = g(b)", "X = f(a)"]); + assert_prolog_failure!(&mut wam, "?- p(f(X, g(Y), Z), g(Z), X)."); + assert_prolog_success!(&mut wam, "?- p(f(X, g(Y), Z), g(Z), h).", ["Z = b", "Y = b", "X = f(a)"]); + assert_prolog_success!(&mut wam, "?- p(Z, Y, X).", ["X = h", "Y = g(b)", "Z = f(f(a), g(b), _7)"]); + assert_prolog_success!(&mut wam, "?- p(f(X, Y, Z), Y, h).", ["Y = g(b)", "Z = _3", "X = f(a)"]); + + submit(&mut wam, "p(_, f(_, Y, _)) :- h(Y)."); + submit(&mut wam, "h(y)."); + + assert_prolog_success!(&mut wam, "?- p(_, f(_, Y, _)).", ["Y = y"]); + assert_prolog_success!(&mut wam, "?- p(_, f(_, y, _))."); + assert_prolog_failure!(&mut wam, "?- p(_, f(_, z, _))."); +} + +#[test] +fn test_queries_on_predicates() { + let mut wam = Machine::new(); + + submit(&mut wam, "p(X, a). p(b, X)."); + + assert_prolog_success!(&mut wam, "?- p(x, Y).", ["Y = a"]); + assert_prolog_success!(&mut wam, "?- p(X, a).", ["X = _0", // 1st case + "X = b"]); // 2nd case. + assert_prolog_success!(&mut wam, "?- p(b, X).", ["X = a", // 1st case + "X = _0"]); // 2nd case. + assert_prolog_success!(&mut wam, "?- p(X, X).", ["X = a", + "X = b"]); + assert_prolog_success!(&mut wam, "?- p(b, a)."); + assert_prolog_failure!(&mut wam, "?- p(a, b)."); + + submit(&mut wam, "p(X, Y, a). p(X, a, Y). p(X, Y, a)."); + + assert_prolog_success!(&mut wam, "?- p(c, d, X).", ["X = a", + "X = a"]); + assert_prolog_success!(&mut wam, "?- p(a, a, a)."); + assert_prolog_failure!(&mut wam, "?- p(b, c, d)."); + + submit(&mut wam, "p(X, a). p(X, Y) :- q(Z), p(X, X)."); + + assert_prolog_success!(&mut wam, "?- p(X, Y).", ["X = _0", "Y = a"]); + assert_prolog_success!(&mut wam, "?- p(x, a)."); + assert_prolog_success!(&mut wam, "?- p(X, a).", ["X = _0"]); + assert_prolog_failure!(&mut wam, "?- p(X, b)."); + + submit(&mut wam, "q(z)."); + + assert_prolog_success_with_limit!(&mut wam, "?- p(X, b).", ["X = a", + "X = a", + "X = a"], + 3); + assert_prolog_success!(&mut wam, "?- p(x, a)."); + assert_prolog_success_with_limit!(&mut wam, "?- p(X, Y).", ["X = _0", "Y = a", + "Y = _1", "X = a", + "Y = _1", "X = a"], + 3); + + submit(&mut wam, "p(X, a). p(X, Y) :- q(Y), p(X, X)."); + + assert_prolog_success_with_limit!(&mut wam, "?- p(X, Y).", ["X = _0", "Y = a", + "Y = z", "X = a"], + 2); + assert_prolog_failure!(&mut wam, "?- p(X, b)."); + + submit(&mut wam, "p(a, z). p(X, Y) :- q(Y), p(X, Y)."); + + assert_prolog_success_with_limit!(&mut wam, "?- p(X, Y).", ["X = a", "Y = z", + "X = a", "Y = z"], + 2); + + assert_prolog_success_with_limit!(&mut wam, "?- p(X, z).", ["X = a", + "X = a"], + 2); + assert_prolog_success!(&mut wam, "?- p(a, z)."); + assert_prolog_success_with_limit!(&mut wam, "?- p(a, X).", ["X = z", + "X = z"], + 2); + assert_prolog_failure!(&mut wam, "?- p(b, a)."); + + submit(&mut wam, "p(X, Y, Z) :- q(X), r(Y), s(Z). + p(a, b, Z) :- q(Z)."); + + submit(&mut wam, "q(x)."); + submit(&mut wam, "r(y)."); + submit(&mut wam, "s(z)."); + + assert_prolog_success!(&mut wam, "?- p(X, Y, Z).", ["Y = y", "X = x", "Z = z", + "Y = b", "X = a", "Z = x"]); + assert_prolog_failure!(&mut wam, "?- p(a, b, c)."); + assert_prolog_success!(&mut wam, "?- p(a, b, C).", ["C = x"]); + + submit(&mut wam, "p(X) :- q(X). p(X) :- r(X)."); + submit(&mut wam, "q(X) :- a."); + submit(&mut wam, "r(X) :- s(X, t). r(X) :- t(X, u)."); + + submit(&mut wam, "s(x, t)."); + submit(&mut wam, "t(y, u)."); + + assert_prolog_success!(&mut wam, "?- p(X).", ["X = x", + "X = y"]); + assert_prolog_success!(&mut wam, "?- p(x)."); + assert_prolog_success!(&mut wam, "?- p(y)."); + assert_prolog_failure!(&mut wam, "?- p(z)."); + + submit(&mut wam, "p(f(f(X)), h(W), Y) :- g(W), h(W), f(X). + p(X, Y, Z) :- h(Y), g(W), z(Z)."); + submit(&mut wam, "g(f(X)) :- z(X). g(X) :- h(X)."); + submit(&mut wam, "h(w). h(x). h(z)."); + submit(&mut wam, "f(s)."); + submit(&mut wam, "z(Z)."); + + assert_prolog_success!(&mut wam, "?- p(X, Y, Z).", ["Y = h(w)", "X = f(f(s))", "Z = _2", + "Y = h(x)", "X = f(f(s))", "Z = _2", + "Y = h(z)", "X = f(f(s))", "Z = _2", + "Y = w", "Z = _2", "X = _0", + "Y = w", "Z = _2", "X = _0", + "Y = w", "Z = _2", "X = _0", + "Y = w", "Z = _2", "X = _0", + "Y = x", "Z = _2", "X = _0", + "Y = x", "Z = _2", "X = _0", + "Y = x", "Z = _2", "X = _0", + "Y = x", "Z = _2", "X = _0", + "Y = z", "Z = _2", "X = _0", + "Y = z", "Z = _2", "X = _0", + "Y = z", "Z = _2", "X = _0", + "Y = z", "Z = _2", "X = _0"]); + assert_prolog_success!(&mut wam, "?- p(X, X, Z).", ["Z = _1", "X = w", + "Z = _1", "X = w", + "Z = _1", "X = w", + "Z = _1", "X = w", + "Z = _1", "X = x", + "Z = _1", "X = x", + "Z = _1", "X = x", + "Z = _1", "X = x", + "Z = _1", "X = z", + "Z = _1", "X = z", + "Z = _1", "X = z", + "Z = _1", "X = z"]); + assert_prolog_success!(&mut wam, "?- p(f(f(Z)), Y, Z).", ["Y = h(w)", "Z = s", + "Y = h(x)", "Z = s", + "Y = h(z)", "Z = s", + "Y = w", "Z = _1", + "Y = w", "Z = _1", + "Y = w", "Z = _1", + "Y = w", "Z = _1", + "Y = x", "Z = _1", + "Y = x", "Z = _1", + "Y = x", "Z = _1", + "Y = x", "Z = _1", + "Y = z", "Z = _1", + "Y = z", "Z = _1", + "Y = z", "Z = _1", + "Y = z", "Z = _1"]); + assert_prolog_success!(&mut wam, "?- p(X, X, X).", ["X = w", + "X = w", + "X = w", + "X = w", + "X = x", + "X = x", + "X = x", + "X = x", + "X = z", + "X = z", + "X = z", + "X = z"]); + assert_prolog_success!(&mut wam, "?- p(X, Y, X).", ["Y = h(w)", "X = f(f(s))", + "Y = h(x)", "X = f(f(s))", + "Y = h(z)", "X = f(f(s))", + "Y = w", "X = _0", + "Y = w", "X = _0", + "Y = w", "X = _0", + "Y = w", "X = _0", + "Y = x", "X = _0", + "Y = x", "X = _0", + "Y = x", "X = _0", + "Y = x", "X = _0", + "Y = z", "X = _0", + "Y = z", "X = _0", + "Y = z", "X = _0", + "Y = z", "X = _0"]); + assert_prolog_failure!(&mut wam, "?- p(f(f(X)), h(f(X)), Y)."); + + submit(&mut wam, "p(X) :- f(Y), g(Y), i(X, Y)."); + submit(&mut wam, "g(f(a)). g(f(b)). g(f(c))."); + submit(&mut wam, "f(f(a)). f(f(b)). f(f(c))."); + submit(&mut wam, "i(X, X)."); + + assert_prolog_success!(&mut wam, "?- p(X).", ["X = f(a)", + "X = f(b)", + "X = f(c)"]); + + submit(&mut wam, "p(X) :- f(f(Y)), g(Y, f(Y)), i(X, f(Y))."); + submit(&mut wam, "g(Y, f(Y)) :- g(f(Y))."); + + assert_prolog_success!(&mut wam, "?- p(X).", ["X = f(a)", + "X = f(b)", + "X = f(c)"]); +} -- 2.54.0