diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a72b077..91d856e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ ### Added -- **aiken**: Optionally provide blueprint file location when using `blueprint apply` @Riley-Kilgore +- **aiken**: Optionally provide blueprint file location when using `blueprint apply`. @Riley-Kilgore +- **aiken**: Output test results as structured JSON when the target output is not a TTY terminal. @Riley-Kilgore, @KtorZ ### Changed @@ -12,6 +13,7 @@ - **aiken-project**: Fix `aiken docs` wrongly formatting list constants as tuples. See [#1048](https://github.com/aiken-lang/aiken/issues/1048). @KtorZ - **aiken-project**: Fix `aiken docs` source linking crashing when generating docs for config modules. See [#1044](https://github.com/aiken-lang/aiken/issues/1044). @KtorZ - **aiken-project**: Fix `aiken docs` generating very long lines for constants. @KtorZ +- **aiken-lang**: Leverage [Decision Trees](https://www.cs.tufts.edu/comp/150FP/archive/luc-maranget/jun08.pdf) for compiling pattern matches to UPLC. @MicroProofs ### Removed diff --git a/Cargo.lock b/Cargo.lock index b4b18deb..4729dd39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,6 +58,7 @@ dependencies = [ "aiken-project", "clap", "clap_complete", + "color-print", "hex", "ignore", "indoc", @@ -590,6 +591,27 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "color-print" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "colorchoice" version = "1.0.2" diff --git a/crates/aiken-project/src/telemetry/json.rs b/crates/aiken-project/src/telemetry/json.rs index 5d7e9717..52aa01fa 100644 --- a/crates/aiken-project/src/telemetry/json.rs +++ b/crates/aiken-project/src/telemetry/json.rs @@ -45,16 +45,27 @@ impl EventListener for Json { } fn fmt_test_json(result: &TestResult) -> serde_json::Value { + let on_test_failure = match result { + TestResult::UnitTestResult(UnitTestResult { ref test, .. }) => &test.on_test_failure, + TestResult::PropertyTestResult(PropertyTestResult { ref test, .. }) => { + &test.on_test_failure + } + }; + let mut test = json!({ "title": result.title(), "status": if result.is_success() { "pass" } else { "fail" }, + "on_failure": match on_test_failure { + OnTestFailure::FailImmediately => "fail_immediately" , + OnTestFailure::SucceedEventually => "succeed_eventually" , + OnTestFailure::SucceedImmediately => "succeed_immediately", + } }); match result { TestResult::UnitTestResult(UnitTestResult { spent_budget, assertion, - test: unit_test, .. }) => { test["execution_units"] = json!({ @@ -63,14 +74,8 @@ 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(false, &AssertionStyleOptions::new(None)), - "on_failure": match unit_test.on_test_failure { - OnTestFailure::FailImmediately => "fail_immediately" , - OnTestFailure::SucceedEventually => "succeed_eventually" , - OnTestFailure::SucceedImmediately => "succeed_immediately", - } - }); + test["assertion"] = + json!(assertion.to_string(false, &AssertionStyleOptions::new(None))); } } } @@ -81,7 +86,9 @@ fn fmt_test_json(result: &TestResult) -> serde_json::V .. }) => { test["iterations"] = json!(iterations); - test["labels"] = json!(labels); + if !labels.is_empty() { + test["labels"] = json!(labels); + } test["counterexample"] = match counterexample { Ok(Some(expr)) => json!(Formatter::new().expr(expr, false).to_pretty_string(60)), Ok(None) => json!(null), diff --git a/crates/aiken/Cargo.toml b/crates/aiken/Cargo.toml index a5ad93f3..ea2fe41d 100644 --- a/crates/aiken/Cargo.toml +++ b/crates/aiken/Cargo.toml @@ -30,6 +30,7 @@ clap = { version = "4.1.8", features = [ "string", ] } clap_complete = "4.3.2" +color-print = "0.3.7" hex = "0.4.3" ignore = "0.4.20" indoc = "2.0" diff --git a/crates/aiken/src/cmd/check.rs b/crates/aiken/src/cmd/check.rs index 8062d2ea..b145b8ea 100644 --- a/crates/aiken/src/cmd/check.rs +++ b/crates/aiken/src/cmd/check.rs @@ -12,7 +12,101 @@ use std::{ }; #[derive(clap::Args)] -/// Type-check an Aiken project +#[command( + verbatim_doc_comment, + about = color_print::cstr!(r#" +Type-check an Aiken project and run any tests found. + +Test results are printed as stylized outputs when `stdout` is a TTY-capable terminal. If it +isn't, (e.g. because you are redirecting the output to a file), test results are printed as +a JSON structured object. Use `--help` to see the whole schema. +"#), + after_long_help = color_print::cstr!(r#"Output JSON schema: + type: object + properties: + seed: &type_integer + type: integer + summary: + type: object + properties: &type_summary + total: *type_integer + passed: *type_integer + failed: *type_integer + kind: + type: object + properties: + unit: *type_integer + property: *type_integer + modules: + type: array + items: + type: object + properties: + name: &type_string + type: string + summary: *type_summary + test: + type: array + items: + oneOf: + - type: object + required: + - kind + - title + - status + - on_failure + - execution_units + properties: + kind + type: string + enum: [ "unit" ] + title: *type_string + status: &type_status + type: string + enum: [ "pass", "fail" ] + on_failure: &type_on_failure + type: string + enum: + - fail_immediately + - succeed_immediately + - succeed_eventually + execution_units: + type: object + properties: + mem: *type_integer + cpu: *type_integer + assertion: *type_string + - type: object + required: + - kind + - title + - status + - on_failure + - iterations + - counterexample + properties: + kind + type: string + enum: [ "property" ] + title: *type_string + status: *type_status + on_failure: *type_on_failure + iterations: *type_integer + labels: + type: object + additionalProperties: *type_integer + counterexample: + oneOf: + - *type_string + - type: "null" + - type: object + properties: + error: *type_string + +Note: + You are seeing the extended help. Use `-h` instead of `--help` for a more compact view. +"# +))] pub struct Args { /// Path to project directory: Option, @@ -44,7 +138,7 @@ pub struct Args { /// Only run tests if they match any of these strings. /// You can match a module with `-m aiken/list` or `-m list`. /// You can match a test with `-m "aiken/list.{map}"` or `-m "aiken/option.{flatten_1}"` - #[clap(short, long)] + #[clap(short, long, verbatim_doc_comment)] match_tests: Option>, /// This is meant to be used with `--match-tests`. @@ -76,14 +170,9 @@ pub struct Args { /// Choose the verbosity level of traces: /// - /// - silent: - /// disable traces altogether - /// - /// - compact: - /// only culprit line numbers are shown on failures - /// - /// - verbose: - /// enable full verbose traces as provided by the user or the compiler + /// - silent: disable traces altogether + /// - compact: only culprit line numbers are shown on failures + /// - verbose: enable full verbose traces as provided by the user or the compiler /// /// [optional] #[clap(short, long, value_parser=trace_level_parser(), default_value_t=TraceLevel::Verbose, verbatim_doc_comment)]