Skip to content

Add progress bar #429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- Improve clock performance
- Make install.sh args more flexible
- Improve Windows detection allowing parallel tests on Git Bash, MSYS and Cygwin
- Add progress bar to non-parallel runner

## [0.20.0](https://github.com/TypedDevs/bashunit/compare/0.19.1...0.20.0) - 2025-06-01

Expand Down
4 changes: 4 additions & 0 deletions bashunit
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ source "$BASHUNIT_ROOT_DIR/src/parallel.sh"
source "$BASHUNIT_ROOT_DIR/src/env.sh"
source "$BASHUNIT_ROOT_DIR/src/clock.sh"
source "$BASHUNIT_ROOT_DIR/src/state.sh"
source "$BASHUNIT_ROOT_DIR/src/progress.sh"
source "$BASHUNIT_ROOT_DIR/src/colors.sh"
source "$BASHUNIT_ROOT_DIR/src/console_header.sh"
source "$BASHUNIT_ROOT_DIR/src/console_results.sh"
Expand Down Expand Up @@ -63,6 +64,9 @@ while [[ $# -gt 0 ]]; do
fi
set -x
;;
-pb|--progress-bar)
export BASHUNIT_PROGRESS_BAR=true
;;
-b|--bench)
_BENCH_MODE=true
export BASHUNIT_BENCH_MODE=true
Expand Down
1 change: 1 addition & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ function build::dependencies() {
"src/env.sh"
"src/clock.sh"
"src/state.sh"
"src/progress.sh"
"src/colors.sh"
"src/console_header.sh"
"src/console_results.sh"
Expand Down
16 changes: 16 additions & 0 deletions docs/command-line.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,22 @@ Duration: 48 ms
```
:::

## Progress bar

> `bashunit -pb|--progress-bar`

Enable the progress bar during test execution. By default, the progress bar is disabled.

::: warning
The progress bar cannot be used when running tests in parallel. If `--progress-bar` is combined with `--parallel`, a warning is printed and the bar is disabled.
:::

::: code-group
```bash [Example]
./bashunit --progress-bar
```
:::

## Version

> `bashunit --version`
Expand Down
16 changes: 16 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,22 @@ BASHUNIT_VERBOSE=true
```
:::

## Progress bar

> `BASHUNIT_PROGRESS_BAR=true|false`

Controls whether the progress bar is rendered. `false` by default.

::: warning
The progress bar is disabled when tests run in parallel. Enabling `BASHUNIT_PROGRESS_BAR` while `BASHUNIT_PARALLEL_RUN` is `true` will print a warning and no bar will be shown.
:::

::: code-group
```bash [Example]
BASHUNIT_PROGRESS_BAR=true
```
:::

<script setup>
import pkg from '../package.json'
</script>
9 changes: 9 additions & 0 deletions src/assert_snapshot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,17 @@ function snapshot::match_with_placeholder() {
fi
}

# Remove progress bar output from a given string. Progress bar sequences are
# wrapped between ESC7 and ESC8 control codes when a TTY is present.
function snapshot::strip_progress_line() {
local input="$1"
echo -n "$input" | sed $'s/\x1b7.*\x1b8//'
}

function assert_match_snapshot() {
local actual
actual=$(echo -n "$1" | tr -d '\r')
actual=$(snapshot::strip_progress_line "$actual")
local directory
directory="./$(dirname "${BASH_SOURCE[1]}")/snapshots"
local test_file
Expand Down Expand Up @@ -73,6 +81,7 @@ function assert_match_snapshot() {
function assert_match_snapshot_ignore_colors() {
local actual
actual=$(echo -n "$1" | sed -r 's/\x1B\[[0-9;]*[mK]//g' | tr -d '\r')
actual=$(snapshot::strip_progress_line "$actual")

local directory
directory="./$(dirname "${BASH_SOURCE[1]}")/snapshots"
Expand Down
10 changes: 7 additions & 3 deletions src/benchmark.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,16 @@ function benchmark::print_results() {
fi

if env::is_simple_output_enabled; then
printf "\n"
progress::blank_line
fi

printf "\nBenchmark Results (avg ms)\n"
progress::blank_line
printf "Benchmark Results (avg ms)\n"
print_line 80 "="
printf "\n"
progress::blank_line
if env::is_simple_output_enabled; then
progress::refresh
fi

local has_threshold=false
for val in "${_BENCH_MAX_MILLIS[@]}"; do
Expand Down
3 changes: 3 additions & 0 deletions src/console_header.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ Options:
-p, --parallel || --no-parallel [default]
Run each test in child process, randomizing the tests execution order.

-pb, --progress-bar
Display a real-time progress bar during test execution. Requires a TTY and disables parallel mode.

-r, --report-html <out.html>
Create a report HTML file that contains information about the test results.

Expand Down
96 changes: 80 additions & 16 deletions src/console_results.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function console_results::render_result() {
fi

if env::is_simple_output_enabled; then
printf "\n\n"
progress::blank_line
fi

local total_tests=0
Expand Down Expand Up @@ -67,36 +67,42 @@ function console_results::render_result() {
printf " %s total\n" "$total_assertions"

if [[ "$(state::get_tests_failed)" -gt 0 ]]; then
printf "\n%s%s%s\n" "$_COLOR_RETURN_ERROR" " Some tests failed " "$_COLOR_DEFAULT"
progress::blank_line
printf "%s%s%s\n" "$_COLOR_RETURN_ERROR" " Some tests failed " "$_COLOR_DEFAULT"
console_results::print_execution_time
return 1
fi

if [[ "$(state::get_tests_incomplete)" -gt 0 ]]; then
printf "\n%s%s%s\n" "$_COLOR_RETURN_INCOMPLETE" " Some tests incomplete " "$_COLOR_DEFAULT"
progress::blank_line
printf "%s%s%s\n" "$_COLOR_RETURN_INCOMPLETE" " Some tests incomplete " "$_COLOR_DEFAULT"
console_results::print_execution_time
return 0
fi

if [[ "$(state::get_tests_skipped)" -gt 0 ]]; then
printf "\n%s%s%s\n" "$_COLOR_RETURN_SKIPPED" " Some tests skipped " "$_COLOR_DEFAULT"
progress::blank_line
printf "%s%s%s\n" "$_COLOR_RETURN_SKIPPED" " Some tests skipped " "$_COLOR_DEFAULT"
console_results::print_execution_time
return 0
fi

if [[ "$(state::get_tests_snapshot)" -gt 0 ]]; then
printf "\n%s%s%s\n" "$_COLOR_RETURN_SNAPSHOT" " Some snapshots created " "$_COLOR_DEFAULT"
progress::blank_line
printf "%s%s%s\n" "$_COLOR_RETURN_SNAPSHOT" " Some snapshots created " "$_COLOR_DEFAULT"
console_results::print_execution_time
return 0
fi

if [[ $total_tests -eq 0 ]]; then
printf "\n%s%s%s\n" "$_COLOR_RETURN_ERROR" " No tests found " "$_COLOR_DEFAULT"
progress::blank_line
printf "%s%s%s\n" "$_COLOR_RETURN_ERROR" " No tests found " "$_COLOR_DEFAULT"
console_results::print_execution_time
return 1
fi

printf "\n%s%s%s\n" "$_COLOR_RETURN_SUCCESS" " All tests passed " "$_COLOR_DEFAULT"
progress::blank_line
printf "%s%s%s\n" "$_COLOR_RETURN_SUCCESS" " All tests passed " "$_COLOR_DEFAULT"
console_results::print_execution_time
return 0
}
Expand Down Expand Up @@ -143,17 +149,38 @@ function console_results::print_successful_test() {
state::print_line "successful" "$full_line"
}

function console_results::print_failed_test_summary() {
local test_name=$1
local duration=${2:-"0"}

local line
line=$(printf "%s✗ Failed%s: %s" "$_COLOR_FAILED" "$_COLOR_DEFAULT" "$test_name")

local full_line=$line
if env::is_show_execution_time_enabled; then
full_line="$(printf "%s\n" "$(str::rpad "$line" "$duration ms")")"
fi

state::print_line "failed" "$full_line"
}

function console_results::print_failure_message() {
local test_name=$1
local failure_message=$2
local duration=${3-}

local line
line="$(printf "\
${_COLOR_FAILED}✗ Failed${_COLOR_DEFAULT}: %s
${_COLOR_FAINT}Message:${_COLOR_DEFAULT} ${_COLOR_BOLD}'%s'${_COLOR_DEFAULT}\n"\
"${test_name}" "${failure_message}")"

state::print_line "failure" "$line"
local full_line=$line
if [[ -n "$duration" ]] && env::is_show_execution_time_enabled; then
full_line="$(printf "%s\n" "$(str::rpad "$line" "$duration ms")")"
fi

state::print_line "failure" "$full_line"
}

function console_results::print_failed_test() {
Expand All @@ -163,6 +190,7 @@ function console_results::print_failed_test() {
local actual=$4
local extra_key=${5-}
local extra_value=${6-}
local duration=${7-}

local line
line="$(printf "\
Expand All @@ -178,13 +206,19 @@ ${_COLOR_FAILED}✗ Failed${_COLOR_DEFAULT}: %s
"${extra_key}" "${extra_value}")"
fi

state::print_line "failed" "$line"
local full_line=$line
if [[ -n "$duration" ]] && env::is_show_execution_time_enabled; then
full_line="$(printf "%s\n" "$(str::rpad "$line" "$duration ms")")"
fi

state::print_line "failed" "$full_line"
}


function console_results::print_failed_snapshot_test() {
local function_name=$1
local snapshot_file=$2
local duration=${3-}

local line
line="$(printf "${_COLOR_FAILED}✗ Failed${_COLOR_DEFAULT}: %s
Expand All @@ -203,12 +237,18 @@ function console_results::print_failed_snapshot_test() {
rm "$actual_file"
fi

state::print_line "failed_snapshot" "$line"
local full_line=$line
if [[ -n "$duration" ]] && env::is_show_execution_time_enabled; then
full_line="$(printf "%s\n" "$(str::rpad "$line" "$duration ms")")"
fi

state::print_line "failed_snapshot" "$full_line"
}

function console_results::print_skipped_test() {
local function_name=$1
local reason=${2-}
local duration=${3-}

local line
line="$(printf "${_COLOR_SKIPPED}↷ Skipped${_COLOR_DEFAULT}: %s\n" "${function_name}")"
Expand All @@ -217,12 +257,18 @@ function console_results::print_skipped_test() {
line+="$(printf "${_COLOR_FAINT} %s${_COLOR_DEFAULT}\n" "${reason}")"
fi

state::print_line "skipped" "$line"
local full_line=$line
if [[ -n "$duration" ]] && env::is_show_execution_time_enabled; then
full_line="$(printf "%s\n" "$(str::rpad "$line" "$duration ms")")"
fi

state::print_line "skipped" "$full_line"
}

function console_results::print_incomplete_test() {
local function_name=$1
local pending=${2-}
local duration=${3-}

local line
line="$(printf "${_COLOR_INCOMPLETE}✒ Incomplete${_COLOR_DEFAULT}: %s\n" "${function_name}")"
Expand All @@ -231,23 +277,35 @@ function console_results::print_incomplete_test() {
line+="$(printf "${_COLOR_FAINT} %s${_COLOR_DEFAULT}\n" "${pending}")"
fi

state::print_line "incomplete" "$line"
local full_line=$line
if [[ -n "$duration" ]] && env::is_show_execution_time_enabled; then
full_line="$(printf "%s\n" "$(str::rpad "$line" "$duration ms")")"
fi

state::print_line "incomplete" "$full_line"
}

function console_results::print_snapshot_test() {
local function_name=$1
local duration=${2-}
local test_name
test_name=$(helper::normalize_test_function_name "$function_name")

local line
line="$(printf "${_COLOR_SNAPSHOT}✎ Snapshot${_COLOR_DEFAULT}: %s\n" "${test_name}")"

state::print_line "snapshot" "$line"
local full_line=$line
if [[ -n "$duration" ]] && env::is_show_execution_time_enabled; then
full_line="$(printf "%s\n" "$(str::rpad "$line" "$duration ms")")"
fi

state::print_line "snapshot" "$full_line"
}

function console_results::print_error_test() {
local function_name=$1
local error="$2"
local duration=${3-}

local test_name
test_name=$(helper::normalize_test_function_name "$function_name")
Expand All @@ -256,7 +314,12 @@ function console_results::print_error_test() {
line="$(printf "${_COLOR_FAILED}✗ Error${_COLOR_DEFAULT}: %s
${_COLOR_FAINT}%s${_COLOR_DEFAULT}\n" "${test_name}" "${error}")"

state::print_line "error" "$line"
local full_line=$line
if [[ -n "$duration" ]] && env::is_show_execution_time_enabled; then
full_line="$(printf "%s\n" "$(str::rpad "$line" "$duration ms")")"
fi

state::print_line "error" "$full_line"
}

function console_results::print_failing_tests_and_reset() {
Expand All @@ -265,7 +328,8 @@ function console_results::print_failing_tests_and_reset() {
total_failed=$(state::get_tests_failed)

if env::is_simple_output_enabled; then
printf "\n\n"
progress::blank_line
progress::blank_line
fi

if [[ "$total_failed" -eq 1 ]]; then
Expand All @@ -277,6 +341,6 @@ function console_results::print_failing_tests_and_reset() {
sed '${/^$/d;}' "$FAILURES_OUTPUT_PATH" | sed 's/^/|/'
rm "$FAILURES_OUTPUT_PATH"

echo ""
progress::blank_line
fi
}
6 changes: 6 additions & 0 deletions src/env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ _DEFAULT_STOP_ON_FAILURE="false"
_DEFAULT_SHOW_EXECUTION_TIME="true"
_DEFAULT_VERBOSE="false"
_DEFAULT_BENCH_MODE="false"
_DEFAULT_PROGRESS_BAR="false"

: "${BASHUNIT_PARALLEL_RUN:=${PARALLEL_RUN:=$_DEFAULT_PARALLEL_RUN}}"
: "${BASHUNIT_SHOW_HEADER:=${SHOW_HEADER:=$_DEFAULT_SHOW_HEADER}}"
Expand All @@ -37,6 +38,7 @@ _DEFAULT_BENCH_MODE="false"
: "${BASHUNIT_SHOW_EXECUTION_TIME:=${SHOW_EXECUTION_TIME:=$_DEFAULT_SHOW_EXECUTION_TIME}}"
: "${BASHUNIT_VERBOSE:=${VERBOSE:=$_DEFAULT_VERBOSE}}"
: "${BASHUNIT_BENCH_MODE:=${BENCH_MODE:=$_DEFAULT_BENCH_MODE}}"
: "${BASHUNIT_PROGRESS_BAR:=${PROGRESS_BAR:=$_DEFAULT_PROGRESS_BAR}}"

function env::is_parallel_run_enabled() {
[[ "$BASHUNIT_PARALLEL_RUN" == "true" ]]
Expand Down Expand Up @@ -74,6 +76,10 @@ function env::is_bench_mode_enabled() {
[[ "$BASHUNIT_BENCH_MODE" == "true" ]]
}

function env::is_progress_bar_enabled() {
[[ "$BASHUNIT_PROGRESS_BAR" == "true" ]]
}

function env::active_internet_connection() {
if ping -c 1 -W 3 google.com &> /dev/null; then
return 0
Expand Down
Loading
Loading