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

View File

@ -28,7 +28,7 @@ pub enum Error {
#[error(transparent)]
StandardIo(#[from] io::Error),
#[error("Syclical module imports")]
#[error("Cyclical module imports")]
ImportCycle { modules: Vec<String> },
/// Useful for returning many [`Error::Parse`] at once
@ -73,6 +73,9 @@ pub enum Error {
src: String,
named: NamedSource,
},
#[error("{} failed", name)]
TestFailure { name: String, path: PathBuf },
}
impl Error {
@ -148,6 +151,7 @@ impl Error {
Error::Type { path, .. } => Some(path.to_path_buf()),
Error::ValidatorMustReturnBool { 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::ValidatorMustReturnBool { 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::ValidatorMustReturnBool { .. } => Some(Box::new("aiken::scripts")),
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::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,
}
}
@ -244,6 +251,7 @@ impl Diagnostic for Error {
Error::WrongValidatorArity { location, .. } => Some(Box::new(
vec![LabeledSpan::new_with_span(None, *location)].into_iter(),
)),
Error::TestFailure { .. } => None,
}
}
@ -259,6 +267,7 @@ impl Diagnostic for Error {
Error::Format { .. } => None,
Error::ValidatorMustReturnBool { 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 {
output_path: self.output_path(),
});
let programs = self.code_gen(validators, &checked_modules)?;
self.write_build_outputs(programs, uplc_dump)?;
Ok(())
}
CodeGenMode::Test(match_tests) => {
let tests =
self.scripts_gen(&checked_modules, |def| matches!(def, Definition::Test(..)))?;
let tests = self
.collect_scripts(&checked_modules, |def| matches!(def, Definition::Test(..)))?;
if !tests.is_empty() {
self.event_listener.handle_event(Event::RunningTests);
}
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
.handle_event(Event::FinishedTests { tests: results });
if !errors.is_empty() {
Err(Error::List(errors))
} else {
Ok(())
}
}
CodeGenMode::Eval(func_name) => {
let scripts =
self.scripts_gen(&checked_modules, |def| matches!(def, Definition::Fn(..)))?;
let scripts = self
.collect_scripts(&checked_modules, |def| matches!(def, Definition::Fn(..)))?;
let results = self.eval_scripts(scripts, Some(func_name));
self.event_listener
.handle_event(Event::EvaluatingFunction { results });
}
CodeGenMode::NoOp => (),
}
Ok(())
}
CodeGenMode::NoOp => Ok(()),
}
}
fn read_source_files(&mut self) -> Result<(), Error> {
let lib = self.root.join("lib");
@ -307,7 +324,7 @@ where
fn validate_validators(
&self,
checked_modules: &mut CheckedModules,
) -> Result<Vec<(String, TypedFunction)>, Error> {
) -> Result<Vec<(PathBuf, String, TypedFunction)>, Error> {
let mut errors = Vec::new();
let mut validators = 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);
}
}
@ -381,7 +402,7 @@ where
fn code_gen(
&mut self,
validators: Vec<(String, TypedFunction)>,
validators: Vec<(PathBuf, String, TypedFunction)>,
checked_modules: &CheckedModules,
) -> Result<Vec<Script>, Error> {
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 {
arguments,
name,
@ -456,7 +477,7 @@ where
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);
}
@ -465,7 +486,7 @@ where
}
// TODO: revisit ownership and lifetimes of data in this function
fn scripts_gen(
fn collect_scripts(
&mut self,
checked_modules: &CheckedModules,
should_collect: fn(&TypedDefinition) -> bool,
@ -503,12 +524,12 @@ where
func,
);
if should_collect(def) {
scripts.push((module.name.clone(), func));
scripts.push((module.input_path.clone(), module.name.clone(), func));
}
}
Definition::Test(func) => {
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);
}
@ -538,7 +559,7 @@ where
// }
}
for (module_name, func_def) in scripts {
for (input_path, module_name, func_def) in scripts {
let Function {
arguments,
name,
@ -557,7 +578,12 @@ where
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);
}

View File

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