From b5276dcc61b1cecaf6743af28f9ec0f797bd3adf Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Thu, 3 Sep 2026 12:14:43 -0300 Subject: [PATCH] Fail the test runner when no tests are selected Signed-off-by: Juan Cruz Viotti --- src/lang/test/test.cc | 13 +++++++++++++ test/test/framework/CMakeLists.txt | 5 +++++ test/test/framework/test_empty.cc | 1 + test/test/framework/test_empty.clitest | 7 +++++++ test/test/framework/test_filter_none.clitest | 4 ++-- 5 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 test/test/framework/test_empty.cc create mode 100644 test/test/framework/test_empty.clitest diff --git a/src/lang/test/test.cc b/src/lang/test/test.cc index ad46d968b..45a0966c2 100644 --- a/src/lang/test/test.cc +++ b/src/lang/test/test.cc @@ -158,6 +158,19 @@ auto test_run(int argc, char **argv) -> int { std::cout << "TAP version 14\n"; std::cout << "1.." << selected.size() << "\n"; + // A test binary exists to run tests, so selecting none exits with failure on + // purpose. An empty run means either broken registration or a filter that + // matches nothing, neither of which may pass as a green suite + if (selected.empty()) { + if (needle.empty()) { + print_diagnostic("no tests were registered"); + } else { + print_diagnostic("no tests matched the filter: " + std::string{needle}); + } + + return EXIT_FAILURE; + } + std::size_t number{0}; std::size_t passed{0}; std::size_t failed{0}; diff --git a/test/test/framework/CMakeLists.txt b/test/test/framework/CMakeLists.txt index 5eb22449c..0d7533e36 100644 --- a/test/test/framework/CMakeLists.txt +++ b/test/test/framework/CMakeLists.txt @@ -26,6 +26,10 @@ add_executable(sourcemeta_core_test_dynamic test_dynamic.cc) target_link_libraries(sourcemeta_core_test_dynamic PRIVATE sourcemeta::core::test sourcemeta::core::test_main) +add_executable(sourcemeta_core_test_empty test_empty.cc) +target_link_libraries(sourcemeta_core_test_empty + PRIVATE sourcemeta::core::test sourcemeta::core::test_main) + # The framework's own command line is exercised through the script runner that # this module ships, so the language is proven against the framework hosting it macro(add_framework_test name binary) @@ -45,3 +49,4 @@ add_framework_test(filter_short sourcemeta_core_test_filter) add_framework_test(filter_none sourcemeta_core_test_filter) add_framework_test(fixture sourcemeta_core_test_fixture) add_framework_test(dynamic sourcemeta_core_test_dynamic) +add_framework_test(empty sourcemeta_core_test_empty) diff --git a/test/test/framework/test_empty.cc b/test/test/framework/test_empty.cc new file mode 100644 index 000000000..036fc7278 --- /dev/null +++ b/test/test/framework/test_empty.cc @@ -0,0 +1 @@ +#include diff --git a/test/test/framework/test_empty.clitest b/test/test/framework/test_empty.clitest new file mode 100644 index 000000000..dd804f129 --- /dev/null +++ b/test/test/framework/test_empty.clitest @@ -0,0 +1,7 @@ +RUN STDIN /dev/null IN . INTO result.txt EXPECTING 1 +WRITE expected.txt UNTIL EXPECTED +1> TAP version 14 +1> 1..0 +1> # no tests were registered +EXPECTED +COMPARE result.txt AGAINST expected.txt diff --git a/test/test/framework/test_filter_none.clitest b/test/test/framework/test_filter_none.clitest index f95093c7f..62683b0c7 100644 --- a/test/test/framework/test_filter_none.clitest +++ b/test/test/framework/test_filter_none.clitest @@ -1,7 +1,7 @@ -RUN --filter nonexistent STDIN /dev/null IN . INTO result.txt EXPECTING 0 +RUN --filter nonexistent STDIN /dev/null IN . INTO result.txt EXPECTING 1 WRITE expected.txt UNTIL EXPECTED 1> TAP version 14 1> 1..0 -1> # 0 passed, 0 failed +1> # no tests matched the filter: nonexistent EXPECTED COMPARE result.txt AGAINST expected.txt