Return non-zero exit code on test failure
And integrated test results with miette report.
This commit is contained in:
parent
3a9cc668fc
commit
87546e0abd
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue