From fafb89d838573ddd5076c48b46bb33dde6aba1c3 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 13 Nov 2024 12:52:12 +0100 Subject: [PATCH] Prevent ANSI colors & styles to appear in JSON output. --- crates/aiken-lang/src/test_framework.rs | 115 ++++++++++-------- crates/aiken-project/src/telemetry/json.rs | 5 +- .../aiken-project/src/telemetry/terminal.rs | 6 +- 3 files changed, 72 insertions(+), 54 deletions(-) diff --git a/crates/aiken-lang/src/test_framework.rs b/crates/aiken-lang/src/test_framework.rs index 84396e75..2aa97e5a 100644 --- a/crates/aiken-lang/src/test_framework.rs +++ b/crates/aiken-lang/src/test_framework.rs @@ -1201,28 +1201,50 @@ impl TryFrom for Assertion { } } +pub struct AssertionStyleOptions<'a> { + red: Box String + 'a>, + bold: Box String + 'a>, +} + +impl<'a> AssertionStyleOptions<'a> { + pub fn new(stream: Option<&'a Stream>) -> Self { + match stream { + Some(stream) => Self { + red: Box::new(|s| { + s.if_supports_color(stream.to_owned(), |s| s.red()) + .to_string() + }), + bold: Box::new(|s| { + s.if_supports_color(stream.to_owned(), |s| s.bold()) + .to_string() + }), + }, + None => Self { + red: Box::new(|s| s), + bold: Box::new(|s| s), + }, + } + } +} + impl Assertion { #[allow(clippy::just_underscores_and_digits)] - pub fn to_string(&self, stream: Stream, expect_failure: bool) -> String { - let red = |s: &str| { - format!("× {s}") - .if_supports_color(stream, |s| s.red()) - .if_supports_color(stream, |s| s.bold()) - .to_string() - }; + pub fn to_string(&self, expect_failure: bool, style: &AssertionStyleOptions) -> String { + let red = |s: &str| style.red.as_ref()(s.to_string()); + let x = |s: &str| style.red.as_ref()(style.bold.as_ref()(format!("× {s}"))); // head did not map to a constant if self.head.is_err() { - return red("program failed"); + return x("program failed"); } // any value in tail did not map to a constant if self.tail.is_err() { - return red("program failed"); + return x("program failed"); } - fn fmt_side(side: &UntypedExpr, stream: Stream) -> String { - let __ = "│".if_supports_color(stream, |s| s.red()); + fn fmt_side(side: &UntypedExpr, red: &dyn Fn(&str) -> String) -> String { + let __ = red("│"); Formatter::new() .expr(side, false) @@ -1233,20 +1255,17 @@ impl Assertion { .join("\n") } - let left = fmt_side(self.head.as_ref().unwrap(), stream); + let left = fmt_side(self.head.as_ref().unwrap(), &red); let tail = self.tail.as_ref().unwrap(); - let right = fmt_side(tail.first(), stream); + let right = fmt_side(tail.first(), &red); format!( "{}{}{}", - red("expected"), + x("expected"), if expect_failure && self.bin_op == BinOp::Or { - " neither\n" - .if_supports_color(stream, |s| s.red()) - .if_supports_color(stream, |s| s.bold()) - .to_string() + x(" neither\n") } else { "\n".to_string() }, @@ -1254,34 +1273,34 @@ impl Assertion { match self.bin_op { BinOp::And => [ left, - red("and"), + x("and"), [ - tail.mapped_ref(|s| fmt_side(s, stream)) - .join(format!("\n{}\n", red("and")).as_str()), + tail.mapped_ref(|s| fmt_side(s, &red)) + .join(format!("\n{}\n", x("and")).as_str()), if tail.len() > 1 { - red("to not all be true") + x("to not all be true") } else { - red("to not both be true") + x("to not both be true") }, ] .join("\n"), ], BinOp::Or => [ left, - red("nor"), + x("nor"), [ - tail.mapped_ref(|s| fmt_side(s, stream)) - .join(format!("\n{}\n", red("nor")).as_str()), - red("to be true"), + tail.mapped_ref(|s| fmt_side(s, &red)) + .join(format!("\n{}\n", x("nor")).as_str()), + x("to be true"), ] .join("\n"), ], - BinOp::Eq => [left, red("to not equal"), right], - BinOp::NotEq => [left, red("to not be different"), right], - BinOp::LtInt => [left, red("to not be lower than"), right], - BinOp::LtEqInt => [left, red("to not be lower than or equal to"), right], - BinOp::GtInt => [left, red("to not be greater than"), right], - BinOp::GtEqInt => [left, red("to not be greater than or equal to"), right], + BinOp::Eq => [left, x("to not equal"), right], + BinOp::NotEq => [left, x("to not be different"), right], + BinOp::LtInt => [left, x("to not be lower than"), right], + BinOp::LtEqInt => [left, x("to not be lower than or equal to"), right], + BinOp::GtInt => [left, x("to not be greater than"), right], + BinOp::GtEqInt => [left, x("to not be greater than or equal to"), right], _ => unreachable!("unexpected non-boolean binary operator in assertion?"), } .join("\n") @@ -1289,34 +1308,34 @@ impl Assertion { match self.bin_op { BinOp::And => [ left, - red("and"), + x("and"), [ - tail.mapped_ref(|s| fmt_side(s, stream)) - .join(format!("\n{}\n", red("and")).as_str()), + tail.mapped_ref(|s| fmt_side(s, &red)) + .join(format!("\n{}\n", x("and")).as_str()), if tail.len() > 1 { - red("to all be true") + x("to all be true") } else { - red("to both be true") + x("to both be true") }, ] .join("\n"), ], BinOp::Or => [ left, - red("or"), + x("or"), [ - tail.mapped_ref(|s| fmt_side(s, stream)) - .join(format!("\n{}\n", red("or")).as_str()), - red("to be true"), + tail.mapped_ref(|s| fmt_side(s, &red)) + .join(format!("\n{}\n", x("or")).as_str()), + x("to be true"), ] .join("\n"), ], - BinOp::Eq => [left, red("to equal"), right], - BinOp::NotEq => [left, red("to not equal"), right], - BinOp::LtInt => [left, red("to be lower than"), right], - BinOp::LtEqInt => [left, red("to be lower than or equal to"), right], - BinOp::GtInt => [left, red("to be greater than"), right], - BinOp::GtEqInt => [left, red("to be greater than or equal to"), right], + BinOp::Eq => [left, x("to equal"), right], + BinOp::NotEq => [left, x("to not equal"), right], + BinOp::LtInt => [left, x("to be lower than"), right], + BinOp::LtEqInt => [left, x("to be lower than or equal to"), right], + BinOp::GtInt => [left, x("to be greater than"), right], + BinOp::GtEqInt => [left, x("to be greater than or equal to"), right], _ => unreachable!("unexpected non-boolean binary operator in assertion?"), } .join("\n") diff --git a/crates/aiken-project/src/telemetry/json.rs b/crates/aiken-project/src/telemetry/json.rs index 84c5f971..2e1cdf7f 100644 --- a/crates/aiken-project/src/telemetry/json.rs +++ b/crates/aiken-project/src/telemetry/json.rs @@ -3,9 +3,8 @@ use aiken_lang::{ ast::OnTestFailure, expr::UntypedExpr, format::Formatter, - test_framework::{PropertyTestResult, TestResult, UnitTestResult}, + test_framework::{AssertionStyleOptions, PropertyTestResult, TestResult, UnitTestResult}, }; -use owo_colors::Stream::Stderr; use serde_json::json; #[derive(Debug, Default, Clone, Copy)] @@ -53,7 +52,7 @@ fn fmt_test_json(result: &TestResult) -> serde_json::V if !result.is_success() { if let Some(assertion) = assertion { test["assertion"] = json!({ - "message": assertion.to_string(Stderr, false), + "message": assertion.to_string(false, &AssertionStyleOptions::new(None)), "expected_to_fail": matches!(unit_test.on_test_failure, OnTestFailure::SucceedEventually | OnTestFailure::SucceedImmediately), }); } diff --git a/crates/aiken-project/src/telemetry/terminal.rs b/crates/aiken-project/src/telemetry/terminal.rs index 43eeaabf..b7d472e3 100644 --- a/crates/aiken-project/src/telemetry/terminal.rs +++ b/crates/aiken-project/src/telemetry/terminal.rs @@ -4,7 +4,7 @@ use aiken_lang::{ ast::OnTestFailure, expr::UntypedExpr, format::Formatter, - test_framework::{PropertyTestResult, TestResult, UnitTestResult}, + test_framework::{AssertionStyleOptions, PropertyTestResult, TestResult, UnitTestResult}, }; use owo_colors::{OwoColorize, Stream::Stderr}; use uplc::machine::cost_model::ExBudget; @@ -288,12 +288,12 @@ fn fmt_test( test = format!( "{test}\n{}", assertion.to_string( - Stderr, match unit_test.on_test_failure { OnTestFailure::FailImmediately => false, OnTestFailure::SucceedEventually | OnTestFailure::SucceedImmediately => true, - } + }, + &AssertionStyleOptions::new(Some(&Stderr)) ), ); }