From fbda31d9809b821f263afc8defee8de30070c674 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sun, 3 Mar 2024 21:00:51 +0100 Subject: [PATCH] Fix and improve test outputs for prop tests. --- crates/aiken-project/src/error.rs | 81 +++++++++++++++------------ crates/aiken-project/src/pretty.rs | 40 ++++++------- crates/aiken-project/src/telemetry.rs | 58 +++++++++++-------- crates/aiken-project/src/watch.rs | 4 +- 4 files changed, 103 insertions(+), 80 deletions(-) diff --git a/crates/aiken-project/src/error.rs b/crates/aiken-project/src/error.rs index cec336de..94553372 100644 --- a/crates/aiken-project/src/error.rs +++ b/crates/aiken-project/src/error.rs @@ -125,6 +125,12 @@ pub enum Error { impl Error { pub fn report(&self) { + if let Error::TestFailure { verbose, .. } = self { + if !verbose { + return; + } + } + println!("{self:?}") } @@ -320,49 +326,54 @@ impl Diagnostic for Error { Error::Parse { error, .. } => error.kind.help(), Error::Type { error, .. } => error.help(), Error::StandardIo(_) => None, - Error::MissingManifest { .. } => Some(Box::new("Try running `aiken new ` to initialise a project with an example manifest.")), + Error::MissingManifest { .. } => Some(Box::new( + "Try running `aiken new ` to initialise a project with an example manifest.", + )), Error::TomlLoading { .. } => None, Error::Format { .. } => None, - Error::TestFailure { assertion, .. } => match assertion { + Error::TestFailure { assertion, .. } => match assertion { None => None, - Some(hint) => Some(Box::new(hint.to_string())) + Some(hint) => Some(Box::new(hint.to_string())), }, Error::Http(_) => None, Error::ZipExtract(_) => None, Error::JoinError(_) => None, - Error::UnknownPackageVersion{..} => Some(Box::new("Perhaps, double-check the package repository and version?")), - Error::UnableToResolvePackage{..} => Some(Box::new("The network is unavailable and the package isn't in the local cache either. Try connecting to the Internet so I can look it up?")), + Error::UnknownPackageVersion { .. } => Some(Box::new( + "Perhaps, double-check the package repository and version?", + )), + Error::UnableToResolvePackage { .. } => Some(Box::new( + "The network is unavailable and the package isn't in the local cache either. Try connecting to the Internet so I can look it up?", + )), Error::Json(error) => Some(Box::new(format!("{error}"))), - Error::MalformedStakeAddress { error } => Some(Box::new(format!("A stake address must be provided either as a base16-encoded string, or as a bech32-encoded string with the 'stake' or 'stake_test' prefix.{hint}", hint = match error { - Some(error) => format!("\n\nHere's the error I encountered: {error}"), - None => String::new(), - }))), - Error::NoValidatorNotFound { known_validators } => { - Some(Box::new(format!( - "Here's a list of all validators I've found in your project. Please double-check this list against the options that you've provided:\n\n{}", - known_validators - .iter() - .map(|title| format!( - "→ {title}", - title = title.if_supports_color(Stdout, |s| s.purple()) - )) - .collect::>() - .join("\n") - ))) - }, - Error::MoreThanOneValidatorFound { known_validators } => { - Some(Box::new(format!( - "Here's a list of all validators I've found in your project. Select one of them using the appropriate options:\n\n{}", - known_validators - .iter() - .map(|title| format!( - "→ {title}", - title = title.if_supports_color(Stdout, |s| s.purple()) - )) - .collect::>() - .join("\n") - ))) - }, + Error::MalformedStakeAddress { error } => Some(Box::new(format!( + "A stake address must be provided either as a base16-encoded string, or as a bech32-encoded string with the 'stake' or 'stake_test' prefix.{hint}", + hint = match error { + Some(error) => format!("\n\nHere's the error I encountered: {error}"), + None => String::new(), + } + ))), + Error::NoValidatorNotFound { known_validators } => Some(Box::new(format!( + "Here's a list of all validators I've found in your project. Please double-check this list against the options that you've provided:\n\n{}", + known_validators + .iter() + .map(|title| format!( + "→ {title}", + title = title.if_supports_color(Stdout, |s| s.purple()) + )) + .collect::>() + .join("\n") + ))), + Error::MoreThanOneValidatorFound { known_validators } => Some(Box::new(format!( + "Here's a list of all validators I've found in your project. Select one of them using the appropriate options:\n\n{}", + known_validators + .iter() + .map(|title| format!( + "→ {title}", + title = title.if_supports_color(Stdout, |s| s.purple()) + )) + .collect::>() + .join("\n") + ))), Error::Module(e) => e.help(), } } diff --git a/crates/aiken-project/src/pretty.rs b/crates/aiken-project/src/pretty.rs index 03f47917..ae79345a 100644 --- a/crates/aiken-project/src/pretty.rs +++ b/crates/aiken-project/src/pretty.rs @@ -10,11 +10,7 @@ pub fn ansi_len(s: &str) -> usize { pub fn len_longest_line(zero: usize, s: &str) -> usize { s.lines().fold(zero, |max, l| { let n = ansi_len(l); - if n > max { - n - } else { - max - } + if n > max { n } else { max } }) } @@ -73,19 +69,27 @@ pub fn open_box( let top = format!( "{} {}", - border_style("┍━"), + border_style(if footer.is_empty() { + "┝━" + } else { + "┍━" + }), pad_right(format!("{title} "), i - 1, &border_style("━")), ); - let bottom = format!( - "{} {}", - pad_right( - border_style("┕"), - if j < k { 0 } else { j + 1 - k }, - &border_style("━") - ), - footer - ); + let bottom = if footer.is_empty() { + border_style("╽") + } else { + format!( + "{} {}", + pad_right( + border_style("┕"), + if j < k { 0 } else { j + 1 - k }, + &border_style("━") + ), + footer + ) + }; format!("{top}\n{content}\n{bottom}") } @@ -120,11 +124,7 @@ pub fn pad_right(mut text: String, n: usize, delimiter: &str) -> String { } pub fn style_if(styled: bool, s: String, apply_style: fn(String) -> String) -> String { - if styled { - apply_style(s) - } else { - s - } + if styled { apply_style(s) } else { s } } pub fn multiline(max_len: usize, s: String) -> Vec { diff --git a/crates/aiken-project/src/telemetry.rs b/crates/aiken-project/src/telemetry.rs index fa9402b4..7ac5c06a 100644 --- a/crates/aiken-project/src/telemetry.rs +++ b/crates/aiken-project/src/telemetry.rs @@ -1,5 +1,7 @@ -use crate::pretty; -use crate::test_framework::{PropertyTestResult, TestResult, UnitTestResult}; +use crate::{ + pretty, + test_framework::{PropertyTestResult, TestResult, UnitTestResult}, +}; use aiken_lang::{expr::UntypedExpr, format::Formatter}; use owo_colors::{OwoColorize, Stream::Stderr}; use std::{collections::BTreeMap, fmt::Display, path::PathBuf}; @@ -169,7 +171,7 @@ impl EventListener for Terminal { ); } Event::FinishedTests { tests } => { - let (max_mem, max_cpu) = find_max_execution_units(&tests); + let (max_mem, max_cpu, max_iter) = find_max_execution_units(&tests); for (module, results) in &group_by_module(&tests) { let title = module @@ -179,7 +181,7 @@ impl EventListener for Terminal { let tests = results .iter() - .map(|r| fmt_test(r, max_mem, max_cpu, true)) + .map(|r| fmt_test(r, max_mem, max_cpu, max_iter, true)) .collect::>() .join("\n"); @@ -254,6 +256,7 @@ fn fmt_test( result: &TestResult, max_mem: usize, max_cpu: usize, + max_iter: usize, styled: bool, ) -> String { // Status @@ -292,10 +295,10 @@ fn fmt_test( test = pretty::pad_right( format!( "{test} [after {} test{}]", - pretty::pad_left(iterations.to_string(), 3, " "), + pretty::pad_left(iterations.to_string(), max_iter, " "), if *iterations > 1 { "s" } else { "" } ), - 14 + max_mem + max_cpu, + 18 + max_mem + max_cpu + max_iter, " ", ); } @@ -317,7 +320,7 @@ fn fmt_test( { test = format!( "{test}\n{}", - pretty::boxed_with( + pretty::open_box( &pretty::style_if(styled, "counterexample".to_string(), |s| s .if_supports_color(Stderr, |s| s.red()) .if_supports_color(Stderr, |s| s.bold()) @@ -325,6 +328,7 @@ fn fmt_test( &Formatter::new() .expr(counterexample, false) .to_pretty_string(70), + "", |s| s.red().to_string() ) ) @@ -392,23 +396,29 @@ fn group_by_module(results: &Vec>) -> BTreeMap(xs: &[TestResult]) -> (usize, usize) { - let (max_mem, max_cpu) = xs - .iter() - .fold((0, 0), |(max_mem, max_cpu), test| match test { - TestResult::PropertyTestResult(..) => (max_mem, max_cpu), - TestResult::UnitTestResult(UnitTestResult { spent_budget, .. }) => { - if spent_budget.mem >= max_mem && spent_budget.cpu >= max_cpu { - (spent_budget.mem, spent_budget.cpu) - } else if spent_budget.mem > max_mem { - (spent_budget.mem, max_cpu) - } else if spent_budget.cpu > max_cpu { - (max_mem, spent_budget.cpu) - } else { - (max_mem, max_cpu) +fn find_max_execution_units(xs: &[TestResult]) -> (usize, usize, usize) { + let (max_mem, max_cpu, max_iter) = + xs.iter() + .fold((0, 0, 0), |(max_mem, max_cpu, max_iter), test| match test { + TestResult::PropertyTestResult(PropertyTestResult { iterations, .. }) => { + (max_mem, max_cpu, std::cmp::max(max_iter, *iterations)) } - } - }); + TestResult::UnitTestResult(UnitTestResult { spent_budget, .. }) => { + if spent_budget.mem >= max_mem && spent_budget.cpu >= max_cpu { + (spent_budget.mem, spent_budget.cpu, max_iter) + } else if spent_budget.mem > max_mem { + (spent_budget.mem, max_cpu, max_iter) + } else if spent_budget.cpu > max_cpu { + (max_mem, spent_budget.cpu, max_iter) + } else { + (max_mem, max_cpu, max_iter) + } + } + }); - (max_mem.to_string().len(), max_cpu.to_string().len()) + ( + max_mem.to_string().len(), + max_cpu.to_string().len(), + max_iter.to_string().len(), + ) } diff --git a/crates/aiken-project/src/watch.rs b/crates/aiken-project/src/watch.rs index 4e973cba..038ef758 100644 --- a/crates/aiken-project/src/watch.rs +++ b/crates/aiken-project/src/watch.rs @@ -123,7 +123,9 @@ where if errs.iter().any(|e| matches!(e, Error::TestFailure { .. })) { eprintln!( - " ━━━━━━\n ╰─▶ use {} {} to replay", + " {}══╤══\n{} ╰─▶ use {} {} to replay", + if errs.len() > 1 { "═" } else { "" }, + if errs.len() > 1 { " " } else { "" }, "--seed".if_supports_color(Stderr, |s| s.bold()), format!("{seed}").if_supports_color(Stderr, |s| s.bold()) );