Only reify unit tests assertions on failure.

This commit is contained in:
KtorZ 2024-03-07 19:07:55 +01:00
parent 0d599f7e2d
commit 8e558d893f
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
3 changed files with 75 additions and 48 deletions

View File

@ -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<Test>, seed: u32) -> Vec<TestResult<UntypedExpr>> {
fn run_tests(&self, tests: Vec<Test>, seed: u32) -> Vec<TestResult<UntypedExpr, UntypedExpr>> {
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::<Vec<TestResult<PlutusData>>>()
.collect::<Vec<TestResult<(Constant, Rc<Type>), PlutusData>>>()
.into_iter()
.map(|test| test.reify(&data_types))
.collect()

View File

@ -38,7 +38,7 @@ pub enum Event {
RunningTests,
FinishedTests {
seed: u32,
tests: Vec<TestResult<UntypedExpr>>,
tests: Vec<TestResult<UntypedExpr, UntypedExpr>>,
},
WaitingForBuildDirLock,
ResolvingPackages {
@ -266,7 +266,7 @@ impl EventListener for Terminal {
}
fn fmt_test(
result: &TestResult<UntypedExpr>,
result: &TestResult<UntypedExpr, UntypedExpr>,
max_mem: usize,
max_cpu: usize,
max_iter: usize,
@ -324,16 +324,16 @@ 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 { "" },
);
}
}
_ => (),
}
@ -396,7 +396,7 @@ fn fmt_test(
test
}
fn fmt_test_summary<T>(tests: &[&TestResult<T>], styled: bool) -> String {
fn fmt_test_summary<T>(tests: &[&TestResult<T, T>], 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<T>(tests: &[&TestResult<T>], styled: bool) -> String {
)
}
fn group_by_module<T>(results: &Vec<TestResult<T>>) -> BTreeMap<String, Vec<&TestResult<T>>> {
fn group_by_module<T>(results: &Vec<TestResult<T, T>>) -> BTreeMap<String, Vec<&TestResult<T, T>>> {
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<T>(xs: &[TestResult<T>]) -> (usize, usize, usize) {
fn find_max_execution_units<T>(xs: &[TestResult<T, T>]) -> (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 {

View File

@ -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<Name>,
pub assertion: Option<Assertion<UntypedExpr>>,
pub assertion: Option<Assertion<(Constant, Rc<Type>)>>,
}
unsafe impl Send for UnitTest {}
impl UnitTest {
pub fn run<T>(self) -> TestResult<T> {
pub fn run<T>(self) -> TestResult<(Constant, Rc<Type>), T> {
let mut eval_result = Program::<NamedDeBruijn>::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<PlutusData> {
pub fn run<U>(self, seed: u32) -> TestResult<U, PlutusData> {
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<T> {
UnitTestResult(UnitTestResult),
pub enum TestResult<U, T> {
UnitTestResult(UnitTestResult<U>),
PropertyTestResult(PropertyTestResult<T>),
}
unsafe impl<T> Send for TestResult<T> {}
unsafe impl<U, T> Send for TestResult<U, T> {}
impl TestResult<PlutusData> {
impl TestResult<(Constant, Rc<Type>), PlutusData> {
pub fn reify(
self,
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
) -> TestResult<UntypedExpr> {
) -> TestResult<UntypedExpr, UntypedExpr> {
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<PlutusData> {
}
}
impl<T> TestResult<T> {
impl<U, T> TestResult<U, T> {
pub fn is_success(&self) -> bool {
match self {
TestResult::UnitTestResult(UnitTestResult { success, .. }) => *success,
@ -766,15 +762,52 @@ impl<T> TestResult<T> {
}
#[derive(Debug)]
pub struct UnitTestResult {
pub struct UnitTestResult<T> {
pub success: bool,
pub spent_budget: ExBudget,
pub output: Option<Term<NamedDeBruijn>>,
pub logs: Vec<String>,
pub test: UnitTest,
pub assertion: Option<Assertion<T>>,
}
unsafe impl Send for UnitTestResult {}
unsafe impl<T> Send for UnitTestResult<T> {}
impl UnitTestResult<(Constant, Rc<Type>)> {
pub fn reify(
self,
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
) -> UnitTestResult<UntypedExpr> {
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<T> {
@ -791,17 +824,10 @@ impl PropertyTestResult<PlutusData> {
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
) -> PropertyTestResult<UntypedExpr> {
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]