Only reify unit tests assertions on failure.
This commit is contained in:
parent
0d599f7e2d
commit
8e558d893f
|
@ -38,7 +38,7 @@ use aiken_lang::{
|
||||||
expr::UntypedExpr,
|
expr::UntypedExpr,
|
||||||
gen_uplc::CodeGenerator,
|
gen_uplc::CodeGenerator,
|
||||||
line_numbers::LineNumbers,
|
line_numbers::LineNumbers,
|
||||||
tipo::TypeInfo,
|
tipo::{Type, TypeInfo},
|
||||||
IdGenerator,
|
IdGenerator,
|
||||||
};
|
};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
@ -55,11 +55,12 @@ use std::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::BufReader,
|
io::BufReader,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
rc::Rc,
|
||||||
};
|
};
|
||||||
use telemetry::EventListener;
|
use telemetry::EventListener;
|
||||||
use test_framework::{Test, TestResult};
|
use test_framework::{Test, TestResult};
|
||||||
use uplc::{
|
use uplc::{
|
||||||
ast::{Name, Program},
|
ast::{Constant, Name, Program},
|
||||||
PlutusData,
|
PlutusData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -834,7 +835,7 @@ where
|
||||||
Ok(tests)
|
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::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
let data_types = utils::indexmap::as_ref_values(&self.data_types);
|
let data_types = utils::indexmap::as_ref_values(&self.data_types);
|
||||||
|
@ -845,7 +846,7 @@ where
|
||||||
Test::UnitTest(unit_test) => unit_test.run(),
|
Test::UnitTest(unit_test) => unit_test.run(),
|
||||||
Test::PropertyTest(property_test) => property_test.run(seed),
|
Test::PropertyTest(property_test) => property_test.run(seed),
|
||||||
})
|
})
|
||||||
.collect::<Vec<TestResult<PlutusData>>>()
|
.collect::<Vec<TestResult<(Constant, Rc<Type>), PlutusData>>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|test| test.reify(&data_types))
|
.map(|test| test.reify(&data_types))
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub enum Event {
|
||||||
RunningTests,
|
RunningTests,
|
||||||
FinishedTests {
|
FinishedTests {
|
||||||
seed: u32,
|
seed: u32,
|
||||||
tests: Vec<TestResult<UntypedExpr>>,
|
tests: Vec<TestResult<UntypedExpr, UntypedExpr>>,
|
||||||
},
|
},
|
||||||
WaitingForBuildDirLock,
|
WaitingForBuildDirLock,
|
||||||
ResolvingPackages {
|
ResolvingPackages {
|
||||||
|
@ -266,7 +266,7 @@ impl EventListener for Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_test(
|
fn fmt_test(
|
||||||
result: &TestResult<UntypedExpr>,
|
result: &TestResult<UntypedExpr, UntypedExpr>,
|
||||||
max_mem: usize,
|
max_mem: usize,
|
||||||
max_cpu: usize,
|
max_cpu: usize,
|
||||||
max_iter: usize,
|
max_iter: usize,
|
||||||
|
@ -324,16 +324,16 @@ fn fmt_test(
|
||||||
// Annotations
|
// Annotations
|
||||||
match result {
|
match result {
|
||||||
TestResult::UnitTestResult(UnitTestResult {
|
TestResult::UnitTestResult(UnitTestResult {
|
||||||
test: unit_test, ..
|
assertion: Some(assertion),
|
||||||
|
test: unit_test,
|
||||||
|
..
|
||||||
}) if !result.is_success() => {
|
}) if !result.is_success() => {
|
||||||
if let Some(ref assertion) = unit_test.assertion {
|
|
||||||
test = format!(
|
test = format!(
|
||||||
"{test}\n{}{new_line}",
|
"{test}\n{}{new_line}",
|
||||||
assertion.to_string(Stderr, unit_test.can_error),
|
assertion.to_string(Stderr, unit_test.can_error),
|
||||||
new_line = if result.logs().is_empty() { "\n" } else { "" },
|
new_line = if result.logs().is_empty() { "\n" } else { "" },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,7 +396,7 @@ fn fmt_test(
|
||||||
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| {
|
let (n_passed, n_failed) = tests.iter().fold((0, 0), |(n_passed, n_failed), result| {
|
||||||
if result.is_success() {
|
if result.is_success() {
|
||||||
(n_passed + 1, n_failed)
|
(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();
|
let mut modules = BTreeMap::new();
|
||||||
for r in results {
|
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);
|
xs.push(r);
|
||||||
}
|
}
|
||||||
modules
|
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) =
|
let (max_mem, max_cpu, max_iter) =
|
||||||
xs.iter()
|
xs.iter()
|
||||||
.fold((0, 0, 0), |(max_mem, max_cpu, max_iter), test| match test {
|
.fold((0, 0, 0), |(max_mem, max_cpu, max_iter), test| match test {
|
||||||
|
|
|
@ -49,8 +49,6 @@ impl Test {
|
||||||
module_name: String,
|
module_name: String,
|
||||||
input_path: PathBuf,
|
input_path: PathBuf,
|
||||||
) -> Test {
|
) -> Test {
|
||||||
let data_types = generator.data_types().clone();
|
|
||||||
|
|
||||||
let program = generator.generate_raw(&test.body, &[], &module_name);
|
let program = generator.generate_raw(&test.body, &[], &module_name);
|
||||||
|
|
||||||
let assertion = match test.body.try_into() {
|
let assertion = match test.body.try_into() {
|
||||||
|
@ -65,10 +63,7 @@ impl Test {
|
||||||
.expect("failed to convert assertion operaand to NamedDeBruijn")
|
.expect("failed to convert assertion operaand to NamedDeBruijn")
|
||||||
.eval(ExBudget::max())
|
.eval(ExBudget::max())
|
||||||
.unwrap_constant()
|
.unwrap_constant()
|
||||||
.map(|cst| {
|
.map(|cst| (cst, side.tipo()))
|
||||||
UntypedExpr::reify_constant(&data_types, cst, &side.tipo())
|
|
||||||
.expect("failed to reify assertion operand?")
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Assertion {
|
Some(Assertion {
|
||||||
|
@ -165,13 +160,13 @@ pub struct UnitTest {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub can_error: bool,
|
pub can_error: bool,
|
||||||
pub program: Program<Name>,
|
pub program: Program<Name>,
|
||||||
pub assertion: Option<Assertion<UntypedExpr>>,
|
pub assertion: Option<Assertion<(Constant, Rc<Type>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for UnitTest {}
|
unsafe impl Send for UnitTest {}
|
||||||
|
|
||||||
impl 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())
|
let mut eval_result = Program::<NamedDeBruijn>::try_from(self.program.clone())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.eval(ExBudget::max());
|
.eval(ExBudget::max());
|
||||||
|
@ -184,6 +179,7 @@ impl UnitTest {
|
||||||
spent_budget: eval_result.cost(),
|
spent_budget: eval_result.cost(),
|
||||||
logs: eval_result.logs(),
|
logs: eval_result.logs(),
|
||||||
output: eval_result.result().ok(),
|
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
|
/// 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.
|
/// 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 n = PropertyTest::MAX_TEST_RUN;
|
||||||
|
|
||||||
let (counterexample, iterations) = match self.run_n_times(n, Prng::from_seed(seed), None) {
|
let (counterexample, iterations) = match self.run_n_times(n, Prng::from_seed(seed), None) {
|
||||||
|
@ -679,20 +675,20 @@ impl<'a> Counterexample<'a> {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TestResult<T> {
|
pub enum TestResult<U, T> {
|
||||||
UnitTestResult(UnitTestResult),
|
UnitTestResult(UnitTestResult<U>),
|
||||||
PropertyTestResult(PropertyTestResult<T>),
|
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(
|
pub fn reify(
|
||||||
self,
|
self,
|
||||||
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
|
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
|
||||||
) -> TestResult<UntypedExpr> {
|
) -> TestResult<UntypedExpr, UntypedExpr> {
|
||||||
match self {
|
match self {
|
||||||
TestResult::UnitTestResult(test) => TestResult::UnitTestResult(test),
|
TestResult::UnitTestResult(test) => TestResult::UnitTestResult(test.reify(data_types)),
|
||||||
TestResult::PropertyTestResult(test) => {
|
TestResult::PropertyTestResult(test) => {
|
||||||
TestResult::PropertyTestResult(test.reify(data_types))
|
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 {
|
pub fn is_success(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
TestResult::UnitTestResult(UnitTestResult { success, .. }) => *success,
|
TestResult::UnitTestResult(UnitTestResult { success, .. }) => *success,
|
||||||
|
@ -766,15 +762,52 @@ impl<T> TestResult<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UnitTestResult {
|
pub struct UnitTestResult<T> {
|
||||||
pub success: bool,
|
pub success: bool,
|
||||||
pub spent_budget: ExBudget,
|
pub spent_budget: ExBudget,
|
||||||
pub output: Option<Term<NamedDeBruijn>>,
|
pub output: Option<Term<NamedDeBruijn>>,
|
||||||
pub logs: Vec<String>,
|
pub logs: Vec<String>,
|
||||||
pub test: UnitTest,
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct PropertyTestResult<T> {
|
pub struct PropertyTestResult<T> {
|
||||||
|
@ -791,17 +824,10 @@ impl PropertyTestResult<PlutusData> {
|
||||||
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
|
data_types: &IndexMap<&DataTypeKey, &TypedDataType>,
|
||||||
) -> PropertyTestResult<UntypedExpr> {
|
) -> PropertyTestResult<UntypedExpr> {
|
||||||
PropertyTestResult {
|
PropertyTestResult {
|
||||||
counterexample: match self.counterexample {
|
counterexample: self.counterexample.map(|counterexample| {
|
||||||
None => None,
|
UntypedExpr::reify_data(data_types, counterexample, &self.test.fuzzer.type_info)
|
||||||
Some(counterexample) => Some(
|
.expect("Failed to reify counterexample?")
|
||||||
UntypedExpr::reify_data(
|
}),
|
||||||
data_types,
|
|
||||||
counterexample,
|
|
||||||
&self.test.fuzzer.type_info,
|
|
||||||
)
|
|
||||||
.expect("Failed to reify counterexample?"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
iterations: self.iterations,
|
iterations: self.iterations,
|
||||||
test: self.test,
|
test: self.test,
|
||||||
}
|
}
|
||||||
|
@ -1266,7 +1292,7 @@ mod test {
|
||||||
}
|
}
|
||||||
"#});
|
"#});
|
||||||
|
|
||||||
assert!(prop.run(42).is_success());
|
assert!(prop.run::<()>(42).is_success());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in New Issue