diff --git a/CHANGELOG.md b/CHANGELOG.md index a73b91d2b..c203cdf5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - The `--parallel` unsupported-OS warning no longer claims Alpine is excluded: Alpine has been a supported parallel platform since the race conditions were fixed, the message was simply never updated ### Fixed +- An unknown option is now rejected with a clear error and a non-zero exit instead of being silently filed under test paths. A mistyped flag used to degrade the run while still reporting success: `bashunit --parralel tests/` ran sequentially and exited `0`, and `bashunit --filterr foo tests/` swallowed both the flag and its value and ran the whole suite. Applies to `test` and `bench`; the `assert` subcommand parses its own arguments and is unchanged (#871) - The quickstart's sample output showed durations as `16 ms` / `90 ms`; bashunit prints `16ms` / `90ms` - `.env.example` documented `BASHUNIT_SHOW_EXECUTION_TIME` as defaulting to `true`; the actual default has been `auto` since #765 - bashunit now aborts with an actionable error when its scratch directories under `TMPDIR` cannot be created. Previously the run continued with every failure/skip collector writing nowhere, so a failing suite still exited non-zero but lost its assertion detail and leaked raw `src/runner.sh: line N: ...: Not a directory` errors instead of reporting the cause diff --git a/src/main.sh b/src/main.sh index 7ab7f50db..c8cd4bc14 100644 --- a/src/main.sh +++ b/src/main.sh @@ -1,5 +1,15 @@ #!/usr/bin/env bash +## +# Prints an "unknown option" error and exits non-zero. +# Arguments: $1 - the offending argument, $2 - subcommand to name in the hint +## +function bashunit::main::abort_unknown_option() { + printf "%sError: unknown option '%s'. Run 'bashunit %s --help' to list the available options.%s\n" \ + "${_BASHUNIT_COLOR_FAILED}" "$1" "$2" "${_BASHUNIT_COLOR_DEFAULT}" >&2 + exit 1 +} + ## # Validates a `--shard /` spec and exports the parts, or prints an # error and exits non-zero. Requires numeric index/total with 1 <= index <= total. @@ -329,6 +339,13 @@ function bashunit::main::cmd_test() { fi _bashunit_coverage_opt_set=true ;; + -*) + # Anything option-shaped reaching here matched no branch above. It used to + # be filed under test paths, so a typo degraded the run silently and still + # exited 0: `--parralel` ran sequentially, `--filterr x` ran the whole + # suite (#871). Test paths never start with a dash. + bashunit::main::abort_unknown_option "$1" "test" + ;; *) raw_args[raw_args_count]="$1" raw_args_count=$((raw_args_count + 1)) @@ -529,6 +546,9 @@ function bashunit::main::cmd_bench() { bashunit::console_header::print_bench_help exit 0 ;; + -*) + bashunit::main::abort_unknown_option "$1" "bench" + ;; *) raw_args[raw_args_count]="$1" raw_args_count=$((raw_args_count + 1)) diff --git a/tests/acceptance/bashunit_unknown_option_test.sh b/tests/acceptance/bashunit_unknown_option_test.sh new file mode 100644 index 000000000..17ccf2d26 --- /dev/null +++ b/tests/acceptance/bashunit_unknown_option_test.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +set -euo pipefail + +function set_up_before_script() { + TEST_ENV_FILE="tests/acceptance/fixtures/.env.default" + TEST_FILE="tests/acceptance/fixtures/tests_path/a_test.sh" +} + +# An unmatched option used to fall through to the test-path list, so the run +# continued with the flag silently ignored and still exited 0 (#871). +function test_bashunit_fails_when_given_an_unknown_option() { + local ec=0 + local output + output=$(./bashunit --env "$TEST_ENV_FILE" --parralel "$TEST_FILE" 2>&1) || ec=$? + + assert_general_error "" "" "$ec" + assert_contains "--parralel" "$output" +} + +function test_bashunit_reports_an_unknown_option_before_running_any_test() { + local output + output=$(./bashunit --env "$TEST_ENV_FILE" --parralel "$TEST_FILE" 2>&1 || true) + + assert_not_contains "All tests passed" "$output" +} + +# The value of a mistyped value-taking flag must not be mistaken for a test path. +function test_bashunit_fails_when_given_an_unknown_option_that_takes_a_value() { + local ec=0 + local output + output=$(./bashunit --env "$TEST_ENV_FILE" --filterr nope "$TEST_FILE" 2>&1) || ec=$? + + assert_general_error "" "" "$ec" + assert_contains "--filterr" "$output" +} + +function test_bashunit_still_accepts_a_known_option_with_a_path() { + local ec=0 + local output + output=$(./bashunit --env "$TEST_ENV_FILE" --no-parallel "$TEST_FILE" 2>&1) || ec=$? + + assert_successful_code "" "" "$ec" + assert_contains "All tests passed" "$output" +} + +function test_bashunit_bench_fails_when_given_an_unknown_option() { + local bench_dir + bench_dir="$(bashunit::temp_dir)" + printf '#!/usr/bin/env bash\nfunction bench_sample() { :; }\n' >"$bench_dir/sample_bench.sh" + + local ec=0 + local output + output=$(./bashunit bench --nonsense-flag "$bench_dir" 2>&1) || ec=$? + + assert_general_error "" "" "$ec" + assert_contains "--nonsense-flag" "$output" +} + +function test_bashunit_still_accepts_a_bare_path() { + local ec=0 + local output + output=$(./bashunit --env "$TEST_ENV_FILE" "$TEST_FILE" 2>&1) || ec=$? + + assert_successful_code "" "" "$ec" + assert_contains "All tests passed" "$output" +}