Collect and display evaluation hints on test failures.
This commit is contained in:
parent
7b22b63ad8
commit
978a6c6981
|
@ -85,6 +85,21 @@ pub struct Function<T, Expr> {
|
|||
pub type TypedTypeAlias = TypeAlias<Arc<Type>>;
|
||||
pub type UntypedTypeAlias = TypeAlias<()>;
|
||||
|
||||
impl TypedFunction {
|
||||
pub fn test_hint(&self) -> Option<(BinOp, Box<TypedExpr>, Box<TypedExpr>)> {
|
||||
match &self.body {
|
||||
TypedExpr::BinOp {
|
||||
name,
|
||||
tipo,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} if tipo == &bool() => Some((name.clone(), left.clone(), right.clone())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TypeAlias<T> {
|
||||
pub alias: String,
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
use crate::{pretty, script::EvalHint};
|
||||
use aiken_lang::{
|
||||
ast::{BinOp, Span},
|
||||
parser::error::ParseError,
|
||||
tipo,
|
||||
};
|
||||
use miette::{
|
||||
Diagnostic, EyreContext, LabeledSpan, MietteHandlerOpts, NamedSource, RgbColors, SourceCode,
|
||||
};
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use aiken_lang::{ast::Span, parser::error::ParseError, tipo};
|
||||
use miette::{
|
||||
Diagnostic, EyreContext, LabeledSpan, MietteHandlerOpts, NamedSource, RgbColors, SourceCode,
|
||||
};
|
||||
use uplc::machine::cost_model::ExBudget;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(thiserror::Error)]
|
||||
|
@ -75,7 +80,11 @@ pub enum Error {
|
|||
},
|
||||
|
||||
#[error("{} failed", name)]
|
||||
TestFailure { name: String, path: PathBuf },
|
||||
TestFailure {
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
evaluation_hint: Option<EvalHint>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Error {
|
||||
|
@ -231,7 +240,34 @@ impl Diagnostic for Error {
|
|||
Error::Format { .. } => None,
|
||||
Error::ValidatorMustReturnBool { .. } => Some(Box::new("Try annotating the validator's return type with Bool")),
|
||||
Error::WrongValidatorArity { .. } => Some(Box::new("Validators require a minimum number of arguments please add the missing arguments.\nIf you don't need one of the required arguments use an underscore `_datum`.")),
|
||||
Error::TestFailure { .. } => None,
|
||||
Error::TestFailure { evaluation_hint, .. } =>{
|
||||
match evaluation_hint {
|
||||
None => None,
|
||||
Some(hint) => {
|
||||
let budget = ExBudget { mem: i64::MAX, cpu: i64::MAX, };
|
||||
let left = pretty::boxed("left", match hint.left.eval(budget) {
|
||||
(Ok(term), _, _) => format!("{term}"),
|
||||
(Err(err), _, _) => format!("{err}"),
|
||||
});
|
||||
let right = pretty::boxed("right", match hint.right.eval(budget) {
|
||||
(Ok(term), _, _) => format!("{term}"),
|
||||
(Err(err), _, _) => format!("{err}"),
|
||||
});
|
||||
let msg = match hint.bin_op {
|
||||
BinOp::And => Some(format!("{left}\n\nand\n\n{right}\n\nshould both be true.")),
|
||||
BinOp::Or => Some(format!("{left}\n\nor\n\n{right}\n\nshould be true.")),
|
||||
BinOp::Eq => Some(format!("{left}\n\nshould be equal to\n\n{right}")),
|
||||
BinOp::NotEq => Some(format!("{left}\n\nshould not be equal to\n\n{right}")),
|
||||
BinOp::LtInt => Some(format!("{left}\n\nshould be lower than\n\n{right}")),
|
||||
BinOp::LtEqInt => Some(format!("{left}\n\nshould be lower than or equal to\n\n{right}")),
|
||||
BinOp::GtEqInt => Some(format!("{left}\n\nshould be greater than\n\n{right}")),
|
||||
BinOp::GtInt => Some(format!("{left}\n\nshould be greater than or equal to\n\n{right}")),
|
||||
_ => None
|
||||
}?;
|
||||
Some(Box::new(msg))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,9 +25,14 @@ use pallas::{
|
|||
ledger::{addresses::Address, primitives::babbage},
|
||||
};
|
||||
use pallas_traverse::ComputeHash;
|
||||
use script::Script;
|
||||
use script::{EvalHint, EvalInfo, Script};
|
||||
use serde_json::json;
|
||||
use telemetry::{EvalInfo, EventListener};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use telemetry::EventListener;
|
||||
use uplc::{
|
||||
ast::{Constant, DeBruijn, Program, Term},
|
||||
machine::cost_model::ExBudget,
|
||||
|
@ -159,6 +164,7 @@ where
|
|||
Some(Error::TestFailure {
|
||||
name: e.script.name.clone(),
|
||||
path: e.script.input_path.clone(),
|
||||
evaluation_hint: e.script.evaluation_hint.clone(),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -472,7 +478,13 @@ where
|
|||
|
||||
let program = generator.generate(body, arguments, true);
|
||||
|
||||
let script = Script::new(input_path, module_name, name, program.try_into().unwrap());
|
||||
let script = Script::new(
|
||||
input_path,
|
||||
module_name,
|
||||
name,
|
||||
program.try_into().unwrap(),
|
||||
None,
|
||||
);
|
||||
|
||||
programs.push(script);
|
||||
}
|
||||
|
@ -571,6 +583,25 @@ where
|
|||
&self.module_types,
|
||||
);
|
||||
|
||||
let evaluation_hint = if let Some((bin_op, left_src, right_src)) = func_def.test_hint()
|
||||
{
|
||||
let left = CodeGenerator::new(&functions, &data_types, &self.module_types)
|
||||
.generate(*left_src, vec![], false)
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let right = CodeGenerator::new(&functions, &data_types, &self.module_types)
|
||||
.generate(*right_src, vec![], false)
|
||||
.try_into()
|
||||
.unwrap();
|
||||
Some(EvalHint {
|
||||
bin_op,
|
||||
left,
|
||||
right,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let program = generator.generate(body.clone(), arguments.clone(), false);
|
||||
|
||||
let script = Script::new(
|
||||
|
@ -578,6 +609,7 @@ where
|
|||
module_name,
|
||||
name.to_string(),
|
||||
program.try_into().unwrap(),
|
||||
evaluation_hint,
|
||||
);
|
||||
|
||||
programs.push(script);
|
||||
|
|
Loading…
Reference in New Issue