diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index e362534f..c4f6a1f0 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -38,7 +38,7 @@ use aiken_lang::{ expr::UntypedExpr, gen_uplc::CodeGenerator, line_numbers::LineNumbers, - tipo::TypeInfo, + tipo::{Type, TypeInfo}, IdGenerator, }; use indexmap::IndexMap; @@ -55,11 +55,12 @@ use std::{ fs::{self, File}, io::BufReader, path::{Path, PathBuf}, + rc::Rc, }; use telemetry::EventListener; use test_framework::{Test, TestResult}; use uplc::{ - ast::{Name, Program}, + ast::{Constant, Name, Program}, PlutusData, }; @@ -834,7 +835,7 @@ where Ok(tests) } - fn run_tests(&self, tests: Vec, seed: u32) -> Vec> { + fn run_tests(&self, tests: Vec, seed: u32) -> Vec> { use rayon::prelude::*; let data_types = utils::indexmap::as_ref_values(&self.data_types); @@ -845,7 +846,7 @@ where Test::UnitTest(unit_test) => unit_test.run(), Test::PropertyTest(property_test) => property_test.run(seed), }) - .collect::>>() + .collect::), PlutusData>>>() .into_iter() .map(|test| test.reify(&data_types)) .collect() diff --git a/crates/aiken-project/src/telemetry.rs b/crates/aiken-project/src/telemetry.rs index 629cb8dc..01e966cd 100644 --- a/crates/aiken-project/src/telemetry.rs +++ b/crates/aiken-project/src/telemetry.rs @@ -38,7 +38,7 @@ pub enum Event { RunningTests, FinishedTests { seed: u32, - tests: Vec>, + tests: Vec>, }, WaitingForBuildDirLock, ResolvingPackages { @@ -266,7 +266,7 @@ impl EventListener for Terminal { } fn fmt_test( - result: &TestResult, + result: &TestResult, max_mem: usize, max_cpu: usize, max_iter: usize, @@ -324,15 +324,15 @@ fn fmt_test( // Annotations match result { TestResult::UnitTestResult(UnitTestResult { - test: unit_test, .. + assertion: Some(assertion), + test: unit_test, + .. }) if !result.is_success() => { - if let Some(ref assertion) = unit_test.assertion { - test = format!( - "{test}\n{}{new_line}", - assertion.to_string(Stderr, unit_test.can_error), - new_line = if result.logs().is_empty() { "\n" } else { "" }, - ); - } + test = format!( + "{test}\n{}{new_line}", + assertion.to_string(Stderr, unit_test.can_error), + new_line = if result.logs().is_empty() { "\n" } else { "" }, + ); } _ => (), } @@ -396,7 +396,7 @@ fn fmt_test( test } -fn fmt_test_summary(tests: &[&TestResult], styled: bool) -> String { +fn fmt_test_summary(tests: &[&TestResult], styled: bool) -> String { let (n_passed, n_failed) = tests.iter().fold((0, 0), |(n_passed, n_failed), result| { if result.is_success() { (n_passed + 1, n_failed) @@ -420,16 +420,16 @@ fn fmt_test_summary(tests: &[&TestResult], styled: bool) -> String { ) } -fn group_by_module(results: &Vec>) -> BTreeMap>> { +fn group_by_module(results: &Vec>) -> BTreeMap>> { let mut modules = BTreeMap::new(); for r in results { - let xs: &mut Vec<&TestResult<_>> = modules.entry(r.module().to_string()).or_default(); + let xs: &mut Vec<&TestResult<_, _>> = modules.entry(r.module().to_string()).or_default(); xs.push(r); } modules } -fn find_max_execution_units(xs: &[TestResult]) -> (usize, usize, usize) { +fn find_max_execution_units(xs: &[TestResult]) -> (usize, usize, usize) { let (max_mem, max_cpu, max_iter) = xs.iter() .fold((0, 0, 0), |(max_mem, max_cpu, max_iter), test| match test { diff --git a/crates/aiken-project/src/test_framework.rs b/crates/aiken-project/src/test_framework.rs index d1819cb9..be1032d0 100644 --- a/crates/aiken-project/src/test_framework.rs +++ b/crates/aiken-project/src/test_framework.rs @@ -49,8 +49,6 @@ impl Test { module_name: String, input_path: PathBuf, ) -> Test { - let data_types = generator.data_types().clone(); - let program = generator.generate_raw(&test.body, &[], &module_name); let assertion = match test.body.try_into() { @@ -65,10 +63,7 @@ impl Test { .expect("failed to convert assertion operaand to NamedDeBruijn") .eval(ExBudget::max()) .unwrap_constant() - .map(|cst| { - UntypedExpr::reify_constant(&data_types, cst, &side.tipo()) - .expect("failed to reify assertion operand?") - }) + .map(|cst| (cst, side.tipo())) }; Some(Assertion { @@ -165,13 +160,13 @@ pub struct UnitTest { pub name: String, pub can_error: bool, pub program: Program, - pub assertion: Option>, + pub assertion: Option)>>, } unsafe impl Send for UnitTest {} impl UnitTest { - pub fn run(self) -> TestResult { + pub fn run(self) -> TestResult<(Constant, Rc), T> { let mut eval_result = Program::::try_from(self.program.clone()) .unwrap() .eval(ExBudget::max()); @@ -184,6 +179,7 @@ impl UnitTest { spent_budget: eval_result.cost(), logs: eval_result.logs(), output: eval_result.result().ok(), + assertion: self.assertion, }) } } @@ -219,7 +215,7 @@ impl PropertyTest { /// Run a property test from a given seed. The property is run at most MAX_TEST_RUN times. It /// may stops earlier on failure; in which case a 'counterexample' is returned. - pub fn run(self, seed: u32) -> TestResult { + pub fn run(self, seed: u32) -> TestResult { let n = PropertyTest::MAX_TEST_RUN; let (counterexample, iterations) = match self.run_n_times(n, Prng::from_seed(seed), None) { @@ -679,20 +675,20 @@ impl<'a> Counterexample<'a> { // ---------------------------------------------------------------------------- #[derive(Debug)] -pub enum TestResult { - UnitTestResult(UnitTestResult), +pub enum TestResult { + UnitTestResult(UnitTestResult), PropertyTestResult(PropertyTestResult), } -unsafe impl Send for TestResult {} +unsafe impl Send for TestResult {} -impl TestResult { +impl TestResult<(Constant, Rc), PlutusData> { pub fn reify( self, data_types: &IndexMap<&DataTypeKey, &TypedDataType>, - ) -> TestResult { + ) -> TestResult { match self { - TestResult::UnitTestResult(test) => TestResult::UnitTestResult(test), + TestResult::UnitTestResult(test) => TestResult::UnitTestResult(test.reify(data_types)), TestResult::PropertyTestResult(test) => { TestResult::PropertyTestResult(test.reify(data_types)) } @@ -700,7 +696,7 @@ impl TestResult { } } -impl TestResult { +impl TestResult { pub fn is_success(&self) -> bool { match self { TestResult::UnitTestResult(UnitTestResult { success, .. }) => *success, @@ -766,15 +762,52 @@ impl TestResult { } #[derive(Debug)] -pub struct UnitTestResult { +pub struct UnitTestResult { pub success: bool, pub spent_budget: ExBudget, pub output: Option>, pub logs: Vec, pub test: UnitTest, + pub assertion: Option>, } -unsafe impl Send for UnitTestResult {} +unsafe impl Send for UnitTestResult {} + +impl UnitTestResult<(Constant, Rc)> { + pub fn reify( + self, + data_types: &IndexMap<&DataTypeKey, &TypedDataType>, + ) -> UnitTestResult { + UnitTestResult { + success: self.success, + spent_budget: self.spent_budget, + output: self.output, + logs: self.logs, + test: self.test, + assertion: self.assertion.and_then(|assertion| { + // No need to spend time/cpu on reifying assertions for successful + // tests since they aren't shown. + if self.success { + return None; + } + + Some(Assertion { + bin_op: assertion.bin_op, + head: assertion.head.map(|(cst, tipo)| { + UntypedExpr::reify_constant(data_types, cst, &tipo) + .expect("failed to reify assertion operand?") + }), + tail: assertion.tail.map(|xs| { + xs.mapped(|(cst, tipo)| { + UntypedExpr::reify_constant(data_types, cst, &tipo) + .expect("failed to reify assertion operand?") + }) + }), + }) + }), + } + } +} #[derive(Debug)] pub struct PropertyTestResult { @@ -791,17 +824,10 @@ impl PropertyTestResult { data_types: &IndexMap<&DataTypeKey, &TypedDataType>, ) -> PropertyTestResult { PropertyTestResult { - counterexample: match self.counterexample { - None => None, - Some(counterexample) => Some( - UntypedExpr::reify_data( - data_types, - counterexample, - &self.test.fuzzer.type_info, - ) - .expect("Failed to reify counterexample?"), - ), - }, + counterexample: self.counterexample.map(|counterexample| { + UntypedExpr::reify_data(data_types, counterexample, &self.test.fuzzer.type_info) + .expect("Failed to reify counterexample?") + }), iterations: self.iterations, test: self.test, } @@ -1266,7 +1292,7 @@ mod test { } "#}); - assert!(prop.run(42).is_success()); + assert!(prop.run::<()>(42).is_success()); } #[test]