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 TypedTypeAlias = TypeAlias<Arc<Type>>;
|
||||||
pub type UntypedTypeAlias = TypeAlias<()>;
|
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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct TypeAlias<T> {
|
pub struct TypeAlias<T> {
|
||||||
pub alias: String,
|
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::{
|
use std::{
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
io,
|
io,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
use uplc::machine::cost_model::ExBudget;
|
||||||
use aiken_lang::{ast::Span, parser::error::ParseError, tipo};
|
|
||||||
use miette::{
|
|
||||||
Diagnostic, EyreContext, LabeledSpan, MietteHandlerOpts, NamedSource, RgbColors, SourceCode,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(thiserror::Error)]
|
#[derive(thiserror::Error)]
|
||||||
|
@ -75,7 +80,11 @@ pub enum Error {
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("{} failed", name)]
|
#[error("{} failed", name)]
|
||||||
TestFailure { name: String, path: PathBuf },
|
TestFailure {
|
||||||
|
name: String,
|
||||||
|
path: PathBuf,
|
||||||
|
evaluation_hint: Option<EvalHint>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
@ -231,7 +240,34 @@ impl Diagnostic for Error {
|
||||||
Error::Format { .. } => None,
|
Error::Format { .. } => None,
|
||||||
Error::ValidatorMustReturnBool { .. } => Some(Box::new("Try annotating the validator's return type with Bool")),
|
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::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},
|
ledger::{addresses::Address, primitives::babbage},
|
||||||
};
|
};
|
||||||
use pallas_traverse::ComputeHash;
|
use pallas_traverse::ComputeHash;
|
||||||
use script::Script;
|
use script::{EvalHint, EvalInfo, Script};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use telemetry::{EvalInfo, EventListener};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
use telemetry::EventListener;
|
||||||
use uplc::{
|
use uplc::{
|
||||||
ast::{Constant, DeBruijn, Program, Term},
|
ast::{Constant, DeBruijn, Program, Term},
|
||||||
machine::cost_model::ExBudget,
|
machine::cost_model::ExBudget,
|
||||||
|
@ -159,6 +164,7 @@ where
|
||||||
Some(Error::TestFailure {
|
Some(Error::TestFailure {
|
||||||
name: e.script.name.clone(),
|
name: e.script.name.clone(),
|
||||||
path: e.script.input_path.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 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);
|
programs.push(script);
|
||||||
}
|
}
|
||||||
|
@ -571,6 +583,25 @@ where
|
||||||
&self.module_types,
|
&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 program = generator.generate(body.clone(), arguments.clone(), false);
|
||||||
|
|
||||||
let script = Script::new(
|
let script = Script::new(
|
||||||
|
@ -578,6 +609,7 @@ where
|
||||||
module_name,
|
module_name,
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
program.try_into().unwrap(),
|
program.try_into().unwrap(),
|
||||||
|
evaluation_hint,
|
||||||
);
|
);
|
||||||
|
|
||||||
programs.push(script);
|
programs.push(script);
|
||||||
|
|
Loading…
Reference in New Issue