Return non-zero exit code on test failure

And integrated test results with miette report.
This commit is contained in:
KtorZ 2022-12-14 18:41:31 +01:00
parent 3a9cc668fc
commit 87546e0abd
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
4 changed files with 80 additions and 28 deletions

View File

@ -1,5 +1,5 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::{env, path::PathBuf}; use std::{env, path::PathBuf, process};
use aiken_project::{ use aiken_project::{
config::Config, config::Config,
@ -36,12 +36,21 @@ where
if let Err(err) = build_result { if let Err(err) = build_result {
err.report(); err.report();
println!("{}", "Summary".purple().bold());
miette::bail!("Failed: {} error(s), {warning_count} warning(s)", err.len(),); println!(
}; " {}, {}",
format!("{} error(s)", err.len()),
println!("\nFinished with {warning_count} warning(s)\n"); format!("{warning_count} warning(s)").yellow(),
);
process::exit(1);
} else {
println!("{}", "Summary".purple().bold());
println!(
" {}, {}",
"0 error",
format!("{warning_count} warning(s)").yellow(),
);
}
Ok(()) Ok(())
} }

View File

@ -28,7 +28,7 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
StandardIo(#[from] io::Error), StandardIo(#[from] io::Error),
#[error("Syclical module imports")] #[error("Cyclical module imports")]
ImportCycle { modules: Vec<String> }, ImportCycle { modules: Vec<String> },
/// Useful for returning many [`Error::Parse`] at once /// Useful for returning many [`Error::Parse`] at once
@ -73,6 +73,9 @@ pub enum Error {
src: String, src: String,
named: NamedSource, named: NamedSource,
}, },
#[error("{} failed", name)]
TestFailure { name: String, path: PathBuf },
} }
impl Error { impl Error {
@ -148,6 +151,7 @@ impl Error {
Error::Type { path, .. } => Some(path.to_path_buf()), Error::Type { path, .. } => Some(path.to_path_buf()),
Error::ValidatorMustReturnBool { path, .. } => Some(path.to_path_buf()), Error::ValidatorMustReturnBool { path, .. } => Some(path.to_path_buf()),
Error::WrongValidatorArity { path, .. } => Some(path.to_path_buf()), Error::WrongValidatorArity { path, .. } => Some(path.to_path_buf()),
Error::TestFailure { path, .. } => Some(path.to_path_buf()),
} }
} }
@ -163,6 +167,7 @@ impl Error {
Error::Type { src, .. } => Some(src.to_string()), Error::Type { src, .. } => Some(src.to_string()),
Error::ValidatorMustReturnBool { src, .. } => Some(src.to_string()), Error::ValidatorMustReturnBool { src, .. } => Some(src.to_string()),
Error::WrongValidatorArity { src, .. } => Some(src.to_string()), Error::WrongValidatorArity { src, .. } => Some(src.to_string()),
Error::TestFailure { .. } => None,
} }
} }
} }
@ -203,6 +208,7 @@ impl Diagnostic for Error {
Error::Format { .. } => None, Error::Format { .. } => None,
Error::ValidatorMustReturnBool { .. } => Some(Box::new("aiken::scripts")), Error::ValidatorMustReturnBool { .. } => Some(Box::new("aiken::scripts")),
Error::WrongValidatorArity { .. } => Some(Box::new("aiken::validators")), Error::WrongValidatorArity { .. } => Some(Box::new("aiken::validators")),
Error::TestFailure { path, .. } => Some(Box::new(path.to_str().unwrap_or(""))),
} }
} }
@ -225,6 +231,7 @@ 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,
} }
} }
@ -244,6 +251,7 @@ impl Diagnostic for Error {
Error::WrongValidatorArity { location, .. } => Some(Box::new( Error::WrongValidatorArity { location, .. } => Some(Box::new(
vec![LabeledSpan::new_with_span(None, *location)].into_iter(), vec![LabeledSpan::new_with_span(None, *location)].into_iter(),
)), )),
Error::TestFailure { .. } => None,
} }
} }
@ -259,6 +267,7 @@ impl Diagnostic for Error {
Error::Format { .. } => None, Error::Format { .. } => None,
Error::ValidatorMustReturnBool { named, .. } => Some(named), Error::ValidatorMustReturnBool { named, .. } => Some(named),
Error::WrongValidatorArity { named, .. } => Some(named), Error::WrongValidatorArity { named, .. } => Some(named),
Error::TestFailure { .. } => None,
} }
} }
} }

View File

@ -144,33 +144,50 @@ where
self.event_listener.handle_event(Event::GeneratingUPLC { self.event_listener.handle_event(Event::GeneratingUPLC {
output_path: self.output_path(), output_path: self.output_path(),
}); });
let programs = self.code_gen(validators, &checked_modules)?; let programs = self.code_gen(validators, &checked_modules)?;
self.write_build_outputs(programs, uplc_dump)?; self.write_build_outputs(programs, uplc_dump)?;
Ok(())
} }
CodeGenMode::Test(match_tests) => { CodeGenMode::Test(match_tests) => {
let tests = let tests = self
self.scripts_gen(&checked_modules, |def| matches!(def, Definition::Test(..)))?; .collect_scripts(&checked_modules, |def| matches!(def, Definition::Test(..)))?;
if !tests.is_empty() { if !tests.is_empty() {
self.event_listener.handle_event(Event::RunningTests); self.event_listener.handle_event(Event::RunningTests);
} }
let results = self.eval_scripts(tests, match_tests); let results = self.eval_scripts(tests, match_tests);
let errors: Vec<Error> = results
.iter()
.filter_map(|e| {
if e.success {
None
} else {
Some(Error::TestFailure {
name: e.script.name.clone(),
path: e.script.input_path.clone(),
})
}
})
.collect();
self.event_listener self.event_listener
.handle_event(Event::FinishedTests { tests: results }); .handle_event(Event::FinishedTests { tests: results });
if !errors.is_empty() {
Err(Error::List(errors))
} else {
Ok(())
}
} }
CodeGenMode::Eval(func_name) => { CodeGenMode::Eval(func_name) => {
let scripts = let scripts = self
self.scripts_gen(&checked_modules, |def| matches!(def, Definition::Fn(..)))?; .collect_scripts(&checked_modules, |def| matches!(def, Definition::Fn(..)))?;
let results = self.eval_scripts(scripts, Some(func_name)); let results = self.eval_scripts(scripts, Some(func_name));
self.event_listener self.event_listener
.handle_event(Event::EvaluatingFunction { results }); .handle_event(Event::EvaluatingFunction { results });
}
CodeGenMode::NoOp => (),
}
Ok(()) Ok(())
} }
CodeGenMode::NoOp => Ok(()),
}
}
fn read_source_files(&mut self) -> Result<(), Error> { fn read_source_files(&mut self) -> Result<(), Error> {
let lib = self.root.join("lib"); let lib = self.root.join("lib");
@ -307,7 +324,7 @@ where
fn validate_validators( fn validate_validators(
&self, &self,
checked_modules: &mut CheckedModules, checked_modules: &mut CheckedModules,
) -> Result<Vec<(String, TypedFunction)>, Error> { ) -> Result<Vec<(PathBuf, String, TypedFunction)>, Error> {
let mut errors = Vec::new(); let mut errors = Vec::new();
let mut validators = Vec::new(); let mut validators = Vec::new();
let mut indices_to_remove = Vec::new(); let mut indices_to_remove = Vec::new();
@ -361,7 +378,11 @@ where
}) })
} }
validators.push((module.name.clone(), func_def.clone())); validators.push((
module.input_path.clone(),
module.name.clone(),
func_def.clone(),
));
indices_to_remove.push(index); indices_to_remove.push(index);
} }
} }
@ -381,7 +402,7 @@ where
fn code_gen( fn code_gen(
&mut self, &mut self,
validators: Vec<(String, TypedFunction)>, validators: Vec<(PathBuf, String, TypedFunction)>,
checked_modules: &CheckedModules, checked_modules: &CheckedModules,
) -> Result<Vec<Script>, Error> { ) -> Result<Vec<Script>, Error> {
let mut programs = Vec::new(); let mut programs = Vec::new();
@ -437,7 +458,7 @@ where
} }
} }
for (module_name, func_def) in validators { for (input_path, module_name, func_def) in validators {
let Function { let Function {
arguments, arguments,
name, name,
@ -456,7 +477,7 @@ where
let program = generator.generate(body, arguments, true); let program = generator.generate(body, arguments, true);
let script = Script::new(module_name, name, program.try_into().unwrap()); let script = Script::new(input_path, module_name, name, program.try_into().unwrap());
programs.push(script); programs.push(script);
} }
@ -465,7 +486,7 @@ where
} }
// TODO: revisit ownership and lifetimes of data in this function // TODO: revisit ownership and lifetimes of data in this function
fn scripts_gen( fn collect_scripts(
&mut self, &mut self,
checked_modules: &CheckedModules, checked_modules: &CheckedModules,
should_collect: fn(&TypedDefinition) -> bool, should_collect: fn(&TypedDefinition) -> bool,
@ -503,12 +524,12 @@ where
func, func,
); );
if should_collect(def) { if should_collect(def) {
scripts.push((module.name.clone(), func)); scripts.push((module.input_path.clone(), module.name.clone(), func));
} }
} }
Definition::Test(func) => { Definition::Test(func) => {
if should_collect(def) { if should_collect(def) {
scripts.push((module.name.clone(), func)); scripts.push((module.input_path.clone(), module.name.clone(), func));
} }
// indices_to_remove.push(index); // indices_to_remove.push(index);
} }
@ -538,7 +559,7 @@ where
// } // }
} }
for (module_name, func_def) in scripts { for (input_path, module_name, func_def) in scripts {
let Function { let Function {
arguments, arguments,
name, name,
@ -557,7 +578,12 @@ where
let program = generator.generate(body.clone(), arguments.clone(), false); let program = generator.generate(body.clone(), arguments.clone(), false);
let script = Script::new(module_name, name.to_string(), program.try_into().unwrap()); let script = Script::new(
input_path,
module_name,
name.to_string(),
program.try_into().unwrap(),
);
programs.push(script); programs.push(script);
} }

View File

@ -1,15 +1,23 @@
use std::path::PathBuf;
use uplc::ast::{NamedDeBruijn, Program}; use uplc::ast::{NamedDeBruijn, Program};
#[derive(Debug)] #[derive(Debug)]
pub struct Script { pub struct Script {
pub input_path: PathBuf,
pub module: String, pub module: String,
pub name: String, pub name: String,
pub program: Program<NamedDeBruijn>, pub program: Program<NamedDeBruijn>,
} }
impl Script { impl Script {
pub fn new(module: String, name: String, program: Program<NamedDeBruijn>) -> Script { pub fn new(
input_path: PathBuf,
module: String,
name: String,
program: Program<NamedDeBruijn>,
) -> Script {
Script { Script {
input_path,
module, module,
name, name,
program, program,