From a84afd1f305b6e113fa3fda5ead19d8483cd2e5e Mon Sep 17 00:00:00 2001 From: subramaniak Date: Thu, 17 Sep 2026 06:00:42 +0000 Subject: [PATCH 1/3] chore(bazel): bump minimum Bazel version to 8.6.0 --- .bazelversion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelversion b/.bazelversion index e7fdef7..acd405b 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -8.4.2 +8.6.0 From 55966c2c3d5eb49165766fee5cc214096237fcbb Mon Sep 17 00:00:00 2001 From: subramaniak Date: Thu, 17 Sep 2026 06:13:21 +0000 Subject: [PATCH 2/3] feat: add basic C++ and Rust usage examples for test_scenarios --- examples/BUILD | 24 ++++++ examples/README.md | 38 +++++++++ score/test_scenarios_cpp/BUILD | 8 ++ score/test_scenarios_cpp/examples/basic.cpp | 93 +++++++++++++++++++++ score/test_scenarios_rust/BUILD | 9 +- score/test_scenarios_rust/examples/basic.rs | 86 +++++++++++++++++++ 6 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 examples/BUILD create mode 100644 examples/README.md create mode 100644 score/test_scenarios_cpp/examples/basic.cpp create mode 100644 score/test_scenarios_rust/examples/basic.rs diff --git a/examples/BUILD b/examples/BUILD new file mode 100644 index 0000000..c05ec12 --- /dev/null +++ b/examples/BUILD @@ -0,0 +1,24 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +alias( + name = "cpp_basic", + actual = "//score/test_scenarios_cpp:basic", + visibility = ["//visibility:public"], +) + +alias( + name = "rust_basic", + actual = "//score/test_scenarios_rust:basic", + visibility = ["//visibility:public"], +) diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..6a581b4 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,38 @@ +# testing_tools Examples + +Example programs demonstrating `test_scenarios_cpp` and `test_scenarios_rust`. C++ examples are +located in [score/test_scenarios_cpp/examples](../score/test_scenarios_cpp/examples), and Rust +examples in [score/test_scenarios_rust/examples](../score/test_scenarios_rust/examples). In this +directory there are Bazel aliases for convenience. + +## Running Examples + +```bash +bazel run //examples:cpp_basic -- --list-scenarios +bazel run //examples:cpp_basic -- --name version.parse --input 8.6.0 +bazel run //examples:cpp_basic -- --name version.satisfies_minimum --input 8.6.0 +bazel run //examples:cpp_basic -- --name version.satisfies_minimum --input 8.4.2 + +bazel run //examples:rust_basic -- --list-scenarios +bazel run //examples:rust_basic -- --name version.parse --input 8.6.0 +bazel run //examples:rust_basic -- --name version.satisfies_minimum --input 8.6.0 +bazel run //examples:rust_basic -- --name version.satisfies_minimum --input 8.4.2 +``` + +## Available Examples + +### basic (C++ and Rust) + +A foundational example demonstrating the core building blocks of the library: + +- Implementing `Scenario` — `version.parse` parses a `major.minor.patch` string and reports its + components (failing genuinely on a malformed version); `version.satisfies_minimum` parses the + input the same way and fails genuinely if it's below this repo's own minimum supported Bazel + version, `8.6.0` (e.g. `--input 8.4.2` fails, `--input 8.6.0` succeeds) +- Grouping scenarios with `ScenarioGroupImpl`, including a nested group (`version`) to show how + nesting produces dotted scenario names (`version.parse`, `version.satisfies_minimum`) +- Wiring a `TestContext` and running it through `run_cli_app`, which provides + `--list-scenarios`, `--name`, and `--input` for free + +**Key concepts:** `Scenario`, `ScenarioGroupImpl` (including nested groups), `TestContext`, +`run_cli_app`, CLI argument handling, error propagation diff --git a/score/test_scenarios_cpp/BUILD b/score/test_scenarios_cpp/BUILD index 983eaf9..800e562 100644 --- a/score/test_scenarios_cpp/BUILD +++ b/score/test_scenarios_cpp/BUILD @@ -10,6 +10,7 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") load("@rules_cc//cc:cc_library.bzl", "cc_library") load("@rules_cc//cc:cc_test.bzl", "cc_test") load("//tools/lint:linters.bzl", "clang_tidy_test") @@ -39,6 +40,13 @@ cc_library( deps = ["@nlohmann_json//:json"], ) +cc_binary( + name = "basic", + srcs = ["examples/basic.cpp"], + visibility = ["//visibility:public"], + deps = [":test_scenarios_cpp"], +) + cc_test( name = "tests", srcs = [ diff --git a/score/test_scenarios_cpp/examples/basic.cpp b/score/test_scenarios_cpp/examples/basic.cpp new file mode 100644 index 0000000..1180259 --- /dev/null +++ b/score/test_scenarios_cpp/examples/basic.cpp @@ -0,0 +1,93 @@ +// ******************************************************************************* +// Copyright (c) 2026 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +constexpr int kMinMajor = 8; +constexpr int kMinMinor = 6; +constexpr int kMinPatch = 0; + +std::tuple parse_version(const std::string& input) { + int major = 0; + int minor = 0; + int patch = 0; + char dot1 = 0; + char dot2 = 0; + std::istringstream iss{input}; + iss >> major >> dot1 >> minor >> dot2 >> patch; + if (iss.fail() || dot1 != '.' || dot2 != '.' || !iss.eof()) { + throw std::runtime_error{"'" + input + "' is not a valid major.minor.patch version"}; + } + return {major, minor, patch}; +} + +// Parses "major.minor.patch" and reports the components. +class ParseScenario final : public Scenario { + public: + std::string name() const override { return "parse"; } + + void run(const std::string& input) const override { + auto [major, minor, patch] = parse_version(input); + std::cout << "major=" << major << " minor=" << minor << " patch=" << patch << std::endl; + } +}; + +// Fails if the input version is below the minimum supported version. +class SatisfiesMinimumScenario final : public Scenario { + public: + std::string name() const override { return "satisfies_minimum"; } + + void run(const std::string& input) const override { + const std::tuple version{parse_version(input)}; + const std::tuple minimum{kMinMajor, kMinMinor, kMinPatch}; + if (version < minimum) { + throw std::runtime_error{"'" + input + "' is below the minimum supported version 8.6.0"}; + } + std::cout << "'" << input << "' satisfies the minimum supported version 8.6.0" << std::endl; + } +}; + +} // namespace + +int main(int argc, char* argv[]) { + std::vector raw_arguments{argv, argv + argc}; + + ScenarioGroup::Ptr version_group{new ScenarioGroupImpl{ + "version", + std::vector{std::make_shared(), + std::make_shared()}, + std::vector{}}}; + ScenarioGroup::Ptr root_group{new ScenarioGroupImpl{ + "root", std::vector{}, std::vector{version_group}}}; + TestContext test_context{root_group}; + + try { + run_cli_app(raw_arguments, test_context); + return 0; + } catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + return 1; + } +} diff --git a/score/test_scenarios_rust/BUILD b/score/test_scenarios_rust/BUILD index 03e6f16..10dc1cc 100644 --- a/score/test_scenarios_rust/BUILD +++ b/score/test_scenarios_rust/BUILD @@ -10,7 +10,7 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") rust_library( name = "test_scenarios_rust", @@ -22,6 +22,13 @@ rust_library( ], ) +rust_binary( + name = "basic", + srcs = ["examples/basic.rs"], + visibility = ["//visibility:public"], + deps = [":test_scenarios_rust"], +) + rust_test( name = "tests", crate = ":test_scenarios_rust", diff --git a/score/test_scenarios_rust/examples/basic.rs b/score/test_scenarios_rust/examples/basic.rs new file mode 100644 index 0000000..27e56af --- /dev/null +++ b/score/test_scenarios_rust/examples/basic.rs @@ -0,0 +1,86 @@ +// ******************************************************************************* +// Copyright (c) 2026 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ******************************************************************************* + +use std::process::ExitCode; + +use test_scenarios_rust::cli::run_cli_app; +use test_scenarios_rust::scenario::{Scenario, ScenarioGroupImpl}; +use test_scenarios_rust::test_context::TestContext; + +const MIN_VERSION: (u32, u32, u32) = (8, 6, 0); + +fn parse_version(input: &str) -> Result<(u32, u32, u32), String> { + let parts: Vec<&str> = input.split('.').collect(); + let err = || format!("'{input}' is not a valid major.minor.patch version"); + if parts.len() != 3 { + return Err(err()); + } + let mut numbers = [0u32; 3]; + for (i, part) in parts.iter().enumerate() { + numbers[i] = part.parse::().map_err(|_| err())?; + } + Ok((numbers[0], numbers[1], numbers[2])) +} + +/// Parses "major.minor.patch" and reports the components. +struct ParseScenario; + +impl Scenario for ParseScenario { + fn name(&self) -> &str { + "parse" + } + + fn run(&self, input: &str) -> Result<(), String> { + let (major, minor, patch) = parse_version(input)?; + println!("major={major} minor={minor} patch={patch}"); + Ok(()) + } +} + +/// Fails if the input version is below the minimum supported version. +struct SatisfiesMinimumScenario; + +impl Scenario for SatisfiesMinimumScenario { + fn name(&self) -> &str { + "satisfies_minimum" + } + + fn run(&self, input: &str) -> Result<(), String> { + let version = parse_version(input)?; + if version < MIN_VERSION { + return Err(format!("'{input}' is below the minimum supported version 8.6.0")); + } + println!("'{input}' satisfies the minimum supported version 8.6.0"); + Ok(()) + } +} + +fn main() -> ExitCode { + let raw_arguments: Vec = std::env::args().collect(); + + let version_group = ScenarioGroupImpl::new( + "version", + vec![Box::new(ParseScenario), Box::new(SatisfiesMinimumScenario)], + Vec::new(), + ); + let root_group = ScenarioGroupImpl::new("root", Vec::new(), vec![Box::new(version_group)]); + let test_context = TestContext::new(Box::new(root_group)); + + match run_cli_app(&raw_arguments, &test_context) { + Ok(()) => ExitCode::SUCCESS, + Err(e) => { + eprintln!("{e}"); + ExitCode::FAILURE + } + } +} From 3ec7773bbbaa37f165a7a346d633b913e4935402 Mon Sep 17 00:00:00 2001 From: subramaniak Date: Fri, 18 Sep 2026 13:53:18 +0000 Subject: [PATCH 3/3] fix: align example deps with kyron/persistency usage --- examples/README.md | 30 +++++---- score/test_scenarios_cpp/BUILD | 5 +- score/test_scenarios_cpp/examples/basic.cpp | 69 +++++++++++---------- score/test_scenarios_rust/BUILD | 7 ++- score/test_scenarios_rust/Cargo.lock | 2 + score/test_scenarios_rust/Cargo.toml | 2 + score/test_scenarios_rust/examples/basic.rs | 64 ++++++++++--------- 7 files changed, 100 insertions(+), 79 deletions(-) diff --git a/examples/README.md b/examples/README.md index 6a581b4..ef842cb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -9,14 +9,14 @@ directory there are Bazel aliases for convenience. ```bash bazel run //examples:cpp_basic -- --list-scenarios -bazel run //examples:cpp_basic -- --name version.parse --input 8.6.0 -bazel run //examples:cpp_basic -- --name version.satisfies_minimum --input 8.6.0 -bazel run //examples:cpp_basic -- --name version.satisfies_minimum --input 8.4.2 +bazel run //examples:cpp_basic -- --name list.enumerate --input '{"items":["a","b","c"]}' +bazel run //examples:cpp_basic -- --name list.require_non_empty --input '{"items":["a"]}' +bazel run //examples:cpp_basic -- --name list.require_non_empty --input '{"items":[]}' bazel run //examples:rust_basic -- --list-scenarios -bazel run //examples:rust_basic -- --name version.parse --input 8.6.0 -bazel run //examples:rust_basic -- --name version.satisfies_minimum --input 8.6.0 -bazel run //examples:rust_basic -- --name version.satisfies_minimum --input 8.4.2 +bazel run //examples:rust_basic -- --name list.enumerate --input '{"items":["a","b","c"]}' +bazel run //examples:rust_basic -- --name list.require_non_empty --input '{"items":["a"]}' +bazel run //examples:rust_basic -- --name list.require_non_empty --input '{"items":[]}' ``` ## Available Examples @@ -25,14 +25,18 @@ bazel run //examples:rust_basic -- --name version.satisfies_minimum --input 8.4. A foundational example demonstrating the core building blocks of the library: -- Implementing `Scenario` — `version.parse` parses a `major.minor.patch` string and reports its - components (failing genuinely on a malformed version); `version.satisfies_minimum` parses the - input the same way and fails genuinely if it's below this repo's own minimum supported Bazel - version, `8.6.0` (e.g. `--input 8.4.2` fails, `--input 8.6.0` succeeds) -- Grouping scenarios with `ScenarioGroupImpl`, including a nested group (`version`) to show how - nesting produces dotted scenario names (`version.parse`, `version.satisfies_minimum`) +- Implementing `Scenario` — both scenarios parse a JSON `{"items": [...]}` input (failing + genuinely on malformed JSON or a missing `items` field). `list.enumerate` logs each item with + its index via structured tracing; `list.require_non_empty` fails genuinely if `items` is empty, + otherwise logs the count +- Structured logging via the library's own tracing support (`TRACING_INFO` in C++, + `tracing::info!` over a subscriber built with `create_tracing_subscriber()` in Rust), with + timestamps from the library's monotonic clock +- Grouping scenarios with `ScenarioGroupImpl`, including a nested group (`list`) to show how + nesting produces dotted scenario names (`list.enumerate`, `list.require_non_empty`) - Wiring a `TestContext` and running it through `run_cli_app`, which provides `--list-scenarios`, `--name`, and `--input` for free **Key concepts:** `Scenario`, `ScenarioGroupImpl` (including nested groups), `TestContext`, -`run_cli_app`, CLI argument handling, error propagation +`run_cli_app`, structured tracing with a monotonic clock, JSON input parsing, CLI argument +handling, error propagation diff --git a/score/test_scenarios_cpp/BUILD b/score/test_scenarios_cpp/BUILD index 800e562..6df3fb4 100644 --- a/score/test_scenarios_cpp/BUILD +++ b/score/test_scenarios_cpp/BUILD @@ -44,7 +44,10 @@ cc_binary( name = "basic", srcs = ["examples/basic.cpp"], visibility = ["//visibility:public"], - deps = [":test_scenarios_cpp"], + deps = [ + ":test_scenarios_cpp", + "@nlohmann_json//:json", + ], ) cc_test( diff --git a/score/test_scenarios_cpp/examples/basic.cpp b/score/test_scenarios_cpp/examples/basic.cpp index 1180259..2ecc2db 100644 --- a/score/test_scenarios_cpp/examples/basic.cpp +++ b/score/test_scenarios_cpp/examples/basic.cpp @@ -14,58 +14,59 @@ #include #include #include +#include +#include + +#include #include #include -#include #include #include -#include #include namespace { -constexpr int kMinMajor = 8; -constexpr int kMinMinor = 6; -constexpr int kMinPatch = 0; +const std::string kTargetName{"examples::basic::list"}; -std::tuple parse_version(const std::string& input) { - int major = 0; - int minor = 0; - int patch = 0; - char dot1 = 0; - char dot2 = 0; - std::istringstream iss{input}; - iss >> major >> dot1 >> minor >> dot2 >> patch; - if (iss.fail() || dot1 != '.' || dot2 != '.' || !iss.eof()) { - throw std::runtime_error{"'" + input + "' is not a valid major.minor.patch version"}; +std::vector parse_items(const std::string& input) { + nlohmann::json parsed; + try { + parsed = nlohmann::json::parse(input); + } catch (const nlohmann::json::exception& e) { + throw std::runtime_error{"invalid input: " + std::string{e.what()}}; } - return {major, minor, patch}; + if (!parsed.contains("items")) { + throw std::runtime_error{"invalid input: missing 'items' field"}; + } + return parsed.at("items").get>(); } -// Parses "major.minor.patch" and reports the components. -class ParseScenario final : public Scenario { +// Logs each item with its index via structured tracing. +class EnumerateScenario final : public Scenario { public: - std::string name() const override { return "parse"; } + std::string name() const override { return "enumerate"; } void run(const std::string& input) const override { - auto [major, minor, patch] = parse_version(input); - std::cout << "major=" << major << " minor=" << minor << " patch=" << patch << std::endl; + auto items{parse_items(input)}; + for (std::size_t index = 0; index < items.size(); ++index) { + TRACING_INFO(kTargetName, std::pair{std::string{"index"}, index}, + std::pair{std::string{"item"}, items[index]}); + } } }; -// Fails if the input version is below the minimum supported version. -class SatisfiesMinimumScenario final : public Scenario { +// Fails if the input has no items. +class RequireNonEmptyScenario final : public Scenario { public: - std::string name() const override { return "satisfies_minimum"; } + std::string name() const override { return "require_non_empty"; } void run(const std::string& input) const override { - const std::tuple version{parse_version(input)}; - const std::tuple minimum{kMinMajor, kMinMinor, kMinPatch}; - if (version < minimum) { - throw std::runtime_error{"'" + input + "' is below the minimum supported version 8.6.0"}; + auto items{parse_items(input)}; + if (items.empty()) { + throw std::runtime_error{"items must not be empty"}; } - std::cout << "'" << input << "' satisfies the minimum supported version 8.6.0" << std::endl; + TRACING_INFO(kTargetName, std::pair{std::string{"count"}, items.size()}); } }; @@ -74,13 +75,13 @@ class SatisfiesMinimumScenario final : public Scenario { int main(int argc, char* argv[]) { std::vector raw_arguments{argv, argv + argc}; - ScenarioGroup::Ptr version_group{new ScenarioGroupImpl{ - "version", - std::vector{std::make_shared(), - std::make_shared()}, + ScenarioGroup::Ptr list_group{new ScenarioGroupImpl{ + "list", + std::vector{std::make_shared(), + std::make_shared()}, std::vector{}}}; ScenarioGroup::Ptr root_group{new ScenarioGroupImpl{ - "root", std::vector{}, std::vector{version_group}}}; + "root", std::vector{}, std::vector{list_group}}}; TestContext test_context{root_group}; try { diff --git a/score/test_scenarios_rust/BUILD b/score/test_scenarios_rust/BUILD index 10dc1cc..8908563 100644 --- a/score/test_scenarios_rust/BUILD +++ b/score/test_scenarios_rust/BUILD @@ -26,7 +26,12 @@ rust_binary( name = "basic", srcs = ["examples/basic.rs"], visibility = ["//visibility:public"], - deps = [":test_scenarios_rust"], + deps = [ + ":test_scenarios_rust", + "@score_crates//:serde", + "@score_crates//:serde_json", + "@score_crates//:tracing", + ], ) rust_test( diff --git a/score/test_scenarios_rust/Cargo.lock b/score/test_scenarios_rust/Cargo.lock index aa92dbb..e044baa 100644 --- a/score/test_scenarios_rust/Cargo.lock +++ b/score/test_scenarios_rust/Cargo.lock @@ -146,6 +146,8 @@ dependencies = [ name = "test_scenarios_rust" version = "0.3.1" dependencies = [ + "serde", + "serde_json", "tracing", "tracing-subscriber", ] diff --git a/score/test_scenarios_rust/Cargo.toml b/score/test_scenarios_rust/Cargo.toml index c3100d3..ec5625b 100644 --- a/score/test_scenarios_rust/Cargo.toml +++ b/score/test_scenarios_rust/Cargo.toml @@ -4,5 +4,7 @@ version = "0.3.1" edition = "2021" [dependencies] +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["json"] } diff --git a/score/test_scenarios_rust/examples/basic.rs b/score/test_scenarios_rust/examples/basic.rs index 27e56af..ba34c0f 100644 --- a/score/test_scenarios_rust/examples/basic.rs +++ b/score/test_scenarios_rust/examples/basic.rs @@ -13,67 +13,71 @@ use std::process::ExitCode; -use test_scenarios_rust::cli::run_cli_app; +use serde::Deserialize; +use tracing::info; + +use test_scenarios_rust::cli::{create_tracing_subscriber, run_cli_app}; use test_scenarios_rust::scenario::{Scenario, ScenarioGroupImpl}; use test_scenarios_rust::test_context::TestContext; -const MIN_VERSION: (u32, u32, u32) = (8, 6, 0); +#[derive(Deserialize)] +struct ItemsInput { + items: Vec, +} -fn parse_version(input: &str) -> Result<(u32, u32, u32), String> { - let parts: Vec<&str> = input.split('.').collect(); - let err = || format!("'{input}' is not a valid major.minor.patch version"); - if parts.len() != 3 { - return Err(err()); - } - let mut numbers = [0u32; 3]; - for (i, part) in parts.iter().enumerate() { - numbers[i] = part.parse::().map_err(|_| err())?; +impl ItemsInput { + fn parse(input: &str) -> Result { + serde_json::from_str(input).map_err(|e| format!("invalid input: {e}")) } - Ok((numbers[0], numbers[1], numbers[2])) } -/// Parses "major.minor.patch" and reports the components. -struct ParseScenario; +/// Logs each item with its index via structured tracing. +struct EnumerateScenario; -impl Scenario for ParseScenario { +impl Scenario for EnumerateScenario { fn name(&self) -> &str { - "parse" + "enumerate" } fn run(&self, input: &str) -> Result<(), String> { - let (major, minor, patch) = parse_version(input)?; - println!("major={major} minor={minor} patch={patch}"); + let parsed = ItemsInput::parse(input)?; + for (index, item) in parsed.items.iter().enumerate() { + info!(index, item = item.as_str()); + } Ok(()) } } -/// Fails if the input version is below the minimum supported version. -struct SatisfiesMinimumScenario; +/// Fails if the input has no items. +struct RequireNonEmptyScenario; -impl Scenario for SatisfiesMinimumScenario { +impl Scenario for RequireNonEmptyScenario { fn name(&self) -> &str { - "satisfies_minimum" + "require_non_empty" } fn run(&self, input: &str) -> Result<(), String> { - let version = parse_version(input)?; - if version < MIN_VERSION { - return Err(format!("'{input}' is below the minimum supported version 8.6.0")); + let parsed = ItemsInput::parse(input)?; + if parsed.items.is_empty() { + return Err("items must not be empty".to_string()); } - println!("'{input}' satisfies the minimum supported version 8.6.0"); + info!(count = parsed.items.len()); Ok(()) } } fn main() -> ExitCode { + tracing::subscriber::set_global_default(create_tracing_subscriber()) + .expect("Setting default subscriber failed!"); + let raw_arguments: Vec = std::env::args().collect(); - let version_group = ScenarioGroupImpl::new( - "version", - vec![Box::new(ParseScenario), Box::new(SatisfiesMinimumScenario)], + let list_group = ScenarioGroupImpl::new( + "list", + vec![Box::new(EnumerateScenario), Box::new(RequireNonEmptyScenario)], Vec::new(), ); - let root_group = ScenarioGroupImpl::new("root", Vec::new(), vec![Box::new(version_group)]); + let root_group = ScenarioGroupImpl::new("root", Vec::new(), vec![Box::new(list_group)]); let test_context = TestContext::new(Box::new(root_group)); match run_cli_app(&raw_arguments, &test_context) {