From 87546e0abd9fb7d428c6541c936cee4404156e5a Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 14 Dec 2022 18:41:31 +0100 Subject: [PATCH] Return non-zero exit code on test failure And integrated test results with miette report. --- crates/cli/src/lib.rs | 23 +++++++++---- crates/project/src/error.rs | 11 ++++++- crates/project/src/lib.rs | 64 +++++++++++++++++++++++++----------- crates/project/src/script.rs | 10 +++++- 4 files changed, 80 insertions(+), 28 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 384bd78d..46885746 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -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(()) } diff --git a/crates/project/src/error.rs b/crates/project/src/error.rs index a1bc12f9..8bfb0a1d 100644 --- a/crates/project/src/error.rs +++ b/crates/project/src/error.rs @@ -28,7 +28,7 @@ pub enum Error { #[error(transparent)] StandardIo(#[from] io::Error), - #[error("Syclical module imports")] + #[error("Cyclical module imports")] ImportCycle { modules: Vec }, /// 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, } } } diff --git a/crates/project/src/lib.rs b/crates/project/src/lib.rs index e9fad487..bca79397 100644 --- a/crates/project/src/lib.rs +++ b/crates/project/src/lib.rs @@ -144,32 +144,49 @@ 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 = 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 }); + Ok(()) } - CodeGenMode::NoOp => (), + CodeGenMode::NoOp => Ok(()), } - - Ok(()) } fn read_source_files(&mut self) -> Result<(), Error> { @@ -307,7 +324,7 @@ where fn validate_validators( &self, checked_modules: &mut CheckedModules, - ) -> Result, Error> { + ) -> Result, 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, 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); } diff --git a/crates/project/src/script.rs b/crates/project/src/script.rs index d11683da..01b3c313 100644 --- a/crates/project/src/script.rs +++ b/crates/project/src/script.rs @@ -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, } impl Script { - pub fn new(module: String, name: String, program: Program) -> Script { + pub fn new( + input_path: PathBuf, + module: String, + name: String, + program: Program, + ) -> Script { Script { + input_path, module, name, program,