diff --git a/CHANGELOG.md b/CHANGELOG.md index bcfd6a48..8490f00e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Add inline filter syntax to run specific tests from a file - `path::function_name` - filter tests by function name - `path:line_number` - run the test containing the specified line +- Add `--show-skipped` and `--show-incomplete` options to display skipped/incomplete tests at the end ### Changed - **BREAKING:** Introduce subcommand-based CLI architecture @@ -66,8 +67,8 @@ - Improve `assert_have_been_called_with` with strict argument matching - Make Windows install clearer in the docs by adding an option for Linux/Mac and another one for Windows. -- Add support for empty values and values containing spaces, tabs or newlines in data providers via new `data_set` function -- Document workaround for global function name collisions when sourcing scripts in tests by copying the original function +- Add data_set function for empty values and values with spaces/tabs/newlines +- Document workaround for function name collisions when sourcing scripts - Fix `temp_dir` and `temp_file` data not being cleaned up when created in `set_up_before_script` - Fix `/tmp/bashunit/parallel` not being cleaned after test run diff --git a/docs/command-line.md b/docs/command-line.md index f7b3e6d0..ddf2f2a8 100644 --- a/docs/command-line.md +++ b/docs/command-line.md @@ -55,6 +55,8 @@ bashunit test tests/ --parallel --simple | `-s, --simple` | Simple output (dots) | | `--detailed` | Detailed output (default) | | `-S, --stop-on-failure` | Stop on first failure | +| `--show-skipped` | Show skipped tests summary at end | +| `--show-incomplete` | Show incomplete tests summary at end | | `-vvv, --verbose` | Show execution details | | `--debug [file]` | Enable shell debug mode | | `--no-output` | Suppress all output | diff --git a/src/console_results.sh b/src/console_results.sh index e1b8550e..b012e1fc 100644 --- a/src/console_results.sh +++ b/src/console_results.sh @@ -301,3 +301,47 @@ function console_results::print_failing_tests_and_reset() { echo "" fi } + +function console_results::print_skipped_tests_and_reset() { + if [[ -s "$SKIPPED_OUTPUT_PATH" ]] && env::is_show_skipped_enabled; then + local total_skipped + total_skipped=$(state::get_tests_skipped) + + if env::is_simple_output_enabled; then + printf "\n" + fi + + if [[ "$total_skipped" -eq 1 ]]; then + echo -e "${_COLOR_BOLD}There was 1 skipped test:${_COLOR_DEFAULT}\n" + else + echo -e "${_COLOR_BOLD}There were $total_skipped skipped tests:${_COLOR_DEFAULT}\n" + fi + + tr -d '\r' < "$SKIPPED_OUTPUT_PATH" | sed '/^[[:space:]]*$/d' | sed 's/^/|/' + rm "$SKIPPED_OUTPUT_PATH" + + echo "" + fi +} + +function console_results::print_incomplete_tests_and_reset() { + if [[ -s "$INCOMPLETE_OUTPUT_PATH" ]] && env::is_show_incomplete_enabled; then + local total_incomplete + total_incomplete=$(state::get_tests_incomplete) + + if env::is_simple_output_enabled; then + printf "\n" + fi + + if [[ "$total_incomplete" -eq 1 ]]; then + echo -e "${_COLOR_BOLD}There was 1 incomplete test:${_COLOR_DEFAULT}\n" + else + echo -e "${_COLOR_BOLD}There were $total_incomplete incomplete tests:${_COLOR_DEFAULT}\n" + fi + + tr -d '\r' < "$INCOMPLETE_OUTPUT_PATH" | sed '/^[[:space:]]*$/d' | sed 's/^/|/' + rm "$INCOMPLETE_OUTPUT_PATH" + + echo "" + fi +} diff --git a/src/env.sh b/src/env.sh index c7de9500..b0af723e 100644 --- a/src/env.sh +++ b/src/env.sh @@ -30,6 +30,8 @@ _DEFAULT_VERBOSE="false" _DEFAULT_BENCH_MODE="false" _DEFAULT_NO_OUTPUT="false" _DEFAULT_INTERNAL_LOG="false" +_DEFAULT_SHOW_SKIPPED="false" +_DEFAULT_SHOW_INCOMPLETE="false" : "${BASHUNIT_PARALLEL_RUN:=${PARALLEL_RUN:=$_DEFAULT_PARALLEL_RUN}}" : "${BASHUNIT_SHOW_HEADER:=${SHOW_HEADER:=$_DEFAULT_SHOW_HEADER}}" @@ -41,6 +43,8 @@ _DEFAULT_INTERNAL_LOG="false" : "${BASHUNIT_BENCH_MODE:=${BENCH_MODE:=$_DEFAULT_BENCH_MODE}}" : "${BASHUNIT_NO_OUTPUT:=${NO_OUTPUT:=$_DEFAULT_NO_OUTPUT}}" : "${BASHUNIT_INTERNAL_LOG:=${INTERNAL_LOG:=$_DEFAULT_INTERNAL_LOG}}" +: "${BASHUNIT_SHOW_SKIPPED:=${SHOW_SKIPPED:=$_DEFAULT_SHOW_SKIPPED}}" +: "${BASHUNIT_SHOW_INCOMPLETE:=${SHOW_INCOMPLETE:=$_DEFAULT_SHOW_INCOMPLETE}}" function env::is_parallel_run_enabled() { [[ "$BASHUNIT_PARALLEL_RUN" == "true" ]] @@ -86,6 +90,14 @@ function env::is_no_output_enabled() { [[ "$BASHUNIT_NO_OUTPUT" == "true" ]] } +function env::is_show_skipped_enabled() { + [[ "$BASHUNIT_SHOW_SKIPPED" == "true" ]] +} + +function env::is_show_incomplete_enabled() { + [[ "$BASHUNIT_SHOW_INCOMPLETE" == "true" ]] +} + function env::active_internet_connection() { if [[ "${BASHUNIT_NO_NETWORK:-}" == "true" ]]; then return 1 @@ -157,6 +169,8 @@ TEMP_DIR_PARALLEL_TEST_SUITE="${TMPDIR:-/tmp}/bashunit/parallel/${_OS:-Unknown}/ TEMP_FILE_PARALLEL_STOP_ON_FAILURE="$TEMP_DIR_PARALLEL_TEST_SUITE/.stop-on-failure" TERMINAL_WIDTH="$(env::find_terminal_width)" FAILURES_OUTPUT_PATH=$(mktemp) +SKIPPED_OUTPUT_PATH=$(mktemp) +INCOMPLETE_OUTPUT_PATH=$(mktemp) CAT="$(command -v cat)" # Initialize temp directory once at startup for performance diff --git a/src/main.sh b/src/main.sh index e1f730d4..de979f6a 100644 --- a/src/main.sh +++ b/src/main.sh @@ -66,6 +66,12 @@ function main::cmd_test() { console_header::print_test_help exit 0 ;; + --show-skipped) + export BASHUNIT_SHOW_SKIPPED=true + ;; + --show-incomplete) + export BASHUNIT_SHOW_INCOMPLETE=true + ;; *) raw_args+=("$1") ;; @@ -319,6 +325,8 @@ function main::exec_tests() { fi console_results::print_failing_tests_and_reset + console_results::print_incomplete_tests_and_reset + console_results::print_skipped_tests_and_reset console_results::render_result exit_code=$? @@ -378,6 +386,8 @@ function main::cleanup() { function main::handle_stop_on_failure_sync() { printf "\n%sStop on failure enabled...%s\n" "${_COLOR_SKIPPED}" "${_COLOR_DEFAULT}" console_results::print_failing_tests_and_reset + console_results::print_incomplete_tests_and_reset + console_results::print_skipped_tests_and_reset console_results::render_result cleanup_script_temp_files if parallel::is_enabled; then diff --git a/src/runner.sh b/src/runner.sh index 9c066606..07a3ffdb 100755 --- a/src/runner.sh +++ b/src/runner.sh @@ -65,7 +65,7 @@ function runner::load_test_files() { parallel::aggregate_test_results "$TEMP_DIR_PARALLEL_TEST_SUITE" # Kill the spinner once the aggregation finishes disown "$spinner_pid" && kill "$spinner_pid" &>/dev/null - printf "\r " # Clear the spinner output + printf "\r \r" # Clear the spinner output for script_id in "${scripts_ids[@]}"; do export BASHUNIT_CURRENT_SCRIPT_ID="${script_id}" cleanup_script_temp_files @@ -111,6 +111,13 @@ function runner::load_bench_files() { } function runner::spinner() { + # Only show spinner when output is to a terminal + if [[ ! -t 1 ]]; then + # Not a terminal, just wait silently + while true; do sleep 1; done + return + fi + if env::is_simple_output_enabled; then printf "\n" fi @@ -524,6 +531,7 @@ function runner::run_test() { if [[ "$current_assertions_incomplete" != "$(state::get_assertions_incomplete)" ]]; then state::add_tests_incomplete reports::add_test_incomplete "$test_file" "$label" "$duration" "$total_assertions" + runner::write_incomplete_result_output "$test_file" "$fn_name" "$subshell_output" internal_log "Test incomplete" "$label" return fi @@ -531,6 +539,7 @@ function runner::run_test() { if [[ "$current_assertions_skipped" != "$(state::get_assertions_skipped)" ]]; then state::add_tests_skipped reports::add_test_skipped "$test_file" "$label" "$duration" "$total_assertions" + runner::write_skipped_result_output "$test_file" "$fn_name" "$subshell_output" internal_log "Test skipped" "$label" return fi @@ -681,6 +690,38 @@ function runner::write_failure_result_output() { echo -e "$test_nr) $test_file:$line_number\n$error_msg" >> "$FAILURES_OUTPUT_PATH" } +function runner::write_skipped_result_output() { + local test_file=$1 + local fn_name=$2 + local output_msg=$3 + + local line_number + line_number=$(helper::get_function_line_number "$fn_name") + + local test_nr="*" + if ! parallel::is_enabled; then + test_nr=$(state::get_tests_skipped) + fi + + echo -e "$test_nr) $test_file:$line_number\n$output_msg" >> "$SKIPPED_OUTPUT_PATH" +} + +function runner::write_incomplete_result_output() { + local test_file=$1 + local fn_name=$2 + local output_msg=$3 + + local line_number + line_number=$(helper::get_function_line_number "$fn_name") + + local test_nr="*" + if ! parallel::is_enabled; then + test_nr=$(state::get_tests_incomplete) + fi + + echo -e "$test_nr) $test_file:$line_number\n$output_msg" >> "$INCOMPLETE_OUTPUT_PATH" +} + function runner::record_file_hook_failure() { local hook_name="$1" local test_file="$2" diff --git a/tests/acceptance/bashunit_summary_output_test.sh b/tests/acceptance/bashunit_summary_output_test.sh new file mode 100644 index 00000000..2fafa086 --- /dev/null +++ b/tests/acceptance/bashunit_summary_output_test.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +function set_up_before_script() { + TEST_ENV_FILE="tests/acceptance/fixtures/.env.default" +} + +function test_skipped_tests_not_displayed_without_flag() { + local output + output=$(./bashunit --no-parallel --simple --env "$TEST_ENV_FILE" \ + "tests/acceptance/bashunit_init_test.sh") + + assert_not_contains "There was 1 skipped test:" "$output" +} + +function test_incomplete_tests_not_displayed_without_flag() { + local output + output=$(./bashunit --no-parallel --simple --env "$TEST_ENV_FILE" \ + "tests/acceptance/bashunit_execution_error_test.sh") + + assert_not_contains "There was 1 incomplete test:" "$output" +} + +function test_skipped_tests_displayed_with_show_skipped_flag() { + local output + output=$(./bashunit --no-parallel --simple --show-skipped --env "$TEST_ENV_FILE" \ + "tests/acceptance/bashunit_init_test.sh") + + assert_contains "There was 1 skipped test:" "$output" + assert_contains "Bashunit init updates env" "$output" +} + +function test_incomplete_tests_displayed_with_show_incomplete_flag() { + local output + output=$(./bashunit --no-parallel --simple --show-incomplete --env "$TEST_ENV_FILE" \ + "tests/acceptance/bashunit_execution_error_test.sh") + + assert_contains "There was 1 incomplete test:" "$output" + assert_contains "Add snapshots with regex" "$output" +} + +function test_both_flags_can_be_used_together() { + local output + output=$(./bashunit --no-parallel --simple --show-skipped --show-incomplete --env "$TEST_ENV_FILE" \ + "tests/acceptance/bashunit_fail_test.sh" "tests/acceptance/bashunit_init_test.sh") + + assert_contains "incomplete test" "$output" + assert_contains "skipped test" "$output" +}