Skip to content
Merged
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 @@ -6,6 +6,7 @@
- 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 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
- Fix `temp_dir` and `temp_file` data not being cleaned up when created in `set_up_before_script`

## [0.23.0](https://github.com/TypedDevs/bashunit/compare/0.22.3...0.23.0) - 2025-08-03

Expand Down
4 changes: 4 additions & 0 deletions docs/globals.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ Internal messages from bashunit include the `[INTERNAL]` prefix so you can easil

> `temp_file <?prefix>`: creates a temporal file

The file is automatically deleted when bashunit completes.

## temp_dir

> `temp_dir <?prefix>`: creates a temporal directory

The directory is automatically deleted when bashunit completes.

## is_command_available

> `is_command_available`: Checks if command is available
21 changes: 17 additions & 4 deletions src/globals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ function temp_file() {
mkdir -p "$base_dir" && chmod -R 777 "$base_dir"
local test_prefix=""
if [[ -n "${BASHUNIT_CURRENT_TEST_ID:-}" ]]; then
# We're inside a test function - use test ID
test_prefix="${BASHUNIT_CURRENT_TEST_ID}_"
elif [[ -n "${BASHUNIT_CURRENT_SCRIPT_ID:-}" ]]; then
# We're at script level (e.g., in set_up_before_script) - use script ID
test_prefix="${BASHUNIT_CURRENT_SCRIPT_ID}_"
fi
mktemp "$base_dir/${test_prefix}${prefix}.XXXXXXX"
}
Expand All @@ -54,17 +58,26 @@ function temp_dir() {
mkdir -p "$base_dir" && chmod -R 777 "$base_dir"
local test_prefix=""
if [[ -n "${BASHUNIT_CURRENT_TEST_ID:-}" ]]; then
# We're inside a test function - use test ID
test_prefix="${BASHUNIT_CURRENT_TEST_ID}_"
elif [[ -n "${BASHUNIT_CURRENT_SCRIPT_ID:-}" ]]; then
# We're at script level (e.g., in set_up_before_script) - use script ID
test_prefix="${BASHUNIT_CURRENT_SCRIPT_ID}_"
fi
mktemp -d "$base_dir/${test_prefix}${prefix}.XXXXXXX"
}

function cleanup_temp_files() {
internal_log "cleanup_temp_files"
function cleanup_testcase_temp_files() {
internal_log "cleanup_testcase_temp_files"
if [[ -n "${BASHUNIT_CURRENT_TEST_ID:-}" ]]; then
rm -rf "${TMPDIR:-/tmp}/bashunit/tmp/${BASHUNIT_CURRENT_TEST_ID}"_*
else
rm -rf "${TMPDIR:-/tmp}/bashunit/tmp"/*
fi
}

function cleanup_script_temp_files() {
internal_log "cleanup_script_temp_files"
if [[ -n "${BASHUNIT_CURRENT_SCRIPT_ID:-}" ]]; then
rm -rf "${TMPDIR:-/tmp}/bashunit/tmp/${BASHUNIT_CURRENT_SCRIPT_ID}"_*
fi
}

Expand Down
11 changes: 11 additions & 0 deletions src/helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,14 @@ function helper::get_function_line_number() {

echo "$line_number"
}

function helper::generate_id() {
local basename="$1"
local sanitized_basename
sanitized_basename="$(helper::normalize_variable_name "$basename")"
if env::is_parallel_run_enabled; then
echo "${sanitized_basename}_$$_$(random_str 6)"
else
echo "${sanitized_basename}_$$"
fi
}
6 changes: 2 additions & 4 deletions src/main.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ function main::exec_tests() {
reports::generate_report_html "$BASHUNIT_REPORT_HTML"
fi

cleanup_temp_files
internal_log "Finished tests" "exit_code:$exit_code"
exit $exit_code
}
Expand Down Expand Up @@ -98,23 +97,22 @@ function main::exec_benchmarks() {

benchmark::print_results

cleanup_temp_files
internal_log "Finished benchmarks"
}

function main::cleanup() {
printf "%sCaught Ctrl-C, killing all child processes...%s\n" "${_COLOR_SKIPPED}" "${_COLOR_DEFAULT}"
# Kill all child processes of this script
pkill -P $$
cleanup_temp_files
cleanup_script_temp_files
exit 1
}

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::render_result
cleanup_temp_files
cleanup_script_temp_files
exit 1
}

Expand Down
25 changes: 17 additions & 8 deletions src/runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ function runner::load_test_files() {
local filter=$1
shift
local files=("${@}")
local scripts_ids=()

for test_file in "${files[@]}"; do
if [[ ! -f $test_file ]]; then
continue
fi
unset BASHUNIT_CURRENT_TEST_ID
export BASHUNIT_CURRENT_SCRIPT_ID="$(helper::generate_id "${test_file}")"
scripts_ids+=("${BASHUNIT_CURRENT_SCRIPT_ID}")
internal_log "Loading file" "$test_file"
# shellcheck source=/dev/null
source "$test_file"
Expand All @@ -21,6 +25,9 @@ function runner::load_test_files() {
fi
runner::run_tear_down_after_script
runner::clean_set_up_and_tear_down_after_script
if ! parallel::is_enabled; then
cleanup_script_temp_files
fi
internal_log "Finished file" "$test_file"
done

Expand All @@ -32,6 +39,10 @@ function runner::load_test_files() {
# Kill the spinner once the aggregation finishes
disown "$spinner_pid" && kill "$spinner_pid" &>/dev/null
printf "\r " # Clear the spinner output
for script_id in "${scripts_ids[@]}"; do
export BASHUNIT_CURRENT_SCRIPT_ID="${script_id}"
cleanup_script_temp_files
done
fi
}

Expand All @@ -42,12 +53,15 @@ function runner::load_bench_files() {

for bench_file in "${files[@]}"; do
[[ -f $bench_file ]] || continue
unset BASHUNIT_CURRENT_TEST_ID
export BASHUNIT_CURRENT_SCRIPT_ID="$(helper::generate_id "${test_file}")"
# shellcheck source=/dev/null
source "$bench_file"
runner::run_set_up_before_script
runner::call_bench_functions "$bench_file" "$filter"
runner::run_tear_down_after_script
runner::clean_set_up_and_tear_down_after_script
cleanup_script_temp_files
done
}

Expand Down Expand Up @@ -232,17 +246,11 @@ function runner::run_test() {
local fn_name="$1"
shift

internal_log "Running test" "$fn_name" "$*"
# Export a unique test identifier so that test doubles can
# create temporary files scoped per test run. This prevents
# race conditions when running tests in parallel.
local sanitized_fn_name
sanitized_fn_name="$(helper::normalize_variable_name "$fn_name")"
internal_log "Running test" "$fn_name" "$*"
if env::is_parallel_run_enabled; then
export BASHUNIT_CURRENT_TEST_ID="${sanitized_fn_name}_$$_$(random_str 6)"
else
export BASHUNIT_CURRENT_TEST_ID="${sanitized_fn_name}_$$"
fi
export BASHUNIT_CURRENT_TEST_ID="$(helper::generate_id "$fn_name")"

state::reset_test_title

Expand All @@ -265,6 +273,7 @@ function runner::run_test() {
state::set_test_exit_code "$exit_code"
runner::run_tear_down
runner::clear_mocks
cleanup_testcase_temp_files
state::export_subshell_context
' EXIT
state::initialize_assertions_count
Expand Down
36 changes: 36 additions & 0 deletions tests/acceptance/bashunit_script_temp_file_cleanup_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash

# @data_provider execution_modes
function test_script_temp_files_are_cleaned_up_after_test_run() {
local mode="$1"
local fixture_file="tests/acceptance/fixtures/script_with_setup_temp_file.sh"
local temp_base_dir="${TMPDIR:-/tmp}/bashunit/tmp"
local output

if [[ "$mode" == "parallel" ]]; then
output=$(./bashunit --parallel "$fixture_file" 2>&1)
else
output=$(./bashunit "$fixture_file" 2>&1)
fi

# Check that the test run was successful
assert_contains "All tests passed" "$output"

# Check that no script-setup temp files remain in the temp directory
local remaining_files
if [[ -d "$temp_base_dir" ]]; then
remaining_files=$(find "$temp_base_dir" -name "*script-setup*" 2>/dev/null || true)

assert_empty "$remaining_files"

# Manually clean up remaining files
if [[ -n "$remaining_files" ]]; then
echo "$remaining_files" | xargs rm -rf 2>/dev/null || true
fi
fi
}

function execution_modes() {
echo "sequential"
echo "parallel"
}
17 changes: 17 additions & 0 deletions tests/acceptance/fixtures/script_with_setup_temp_file.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash

function set_up_before_script() {
SCRIPT_TEMP_FILE=$(temp_file "script-setup")
SCRIPT_TEMP_DIR=$(temp_dir "script-setup")
echo "Script temp file created: $SCRIPT_TEMP_FILE" > "$SCRIPT_TEMP_FILE"
echo "Script temp dir created: $SCRIPT_TEMP_DIR" > "$SCRIPT_TEMP_DIR/marker.txt"
}

function test_simple_passing_test() {
assert_equals "1" "1"
}

function test_another_simple_test() {
assert_file_exists "$SCRIPT_TEMP_FILE"
assert_directory_exists "$SCRIPT_TEMP_DIR"
}
21 changes: 17 additions & 4 deletions tests/unit/globals_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

set -euo pipefail

function set_up_before_script() {
SCRIPT_TEMP_FILE=$(temp_file "custom-prefix")
SCRIPT_TEMP_DIR=$(temp_dir "custom-prefix")
}

function tear_down_after_script() {
export BASHUNIT_DEV_LOG=""
}
Expand Down Expand Up @@ -58,22 +63,30 @@ function test_globals_random_str_custom_len() {
assert_matches "^[A-Za-z0-9]{3}$" "$(random_str 3)"
}

function test_globals_temp_file() {
function test_globals_temp_file_in_test_function() {
# shellcheck disable=SC2155
local temp_file=$(temp_file "custom-prefix")
assert_file_exists "$temp_file"
cleanup_temp_files
cleanup_testcase_temp_files
assert_file_not_exists "$temp_file"
}

function test_globals_temp_dir() {
function test_globals_temp_dir_in_test_function() {
# shellcheck disable=SC2155
local temp_dir=$(temp_dir "custom-prefix")
assert_directory_exists "$temp_dir"
cleanup_temp_files
cleanup_testcase_temp_files
assert_directory_not_exists "$temp_dir"
}

function test_globals_temp_dir_and_file_in_script() {
assert_directory_exists "$SCRIPT_TEMP_DIR"
assert_file_exists "$SCRIPT_TEMP_FILE"
cleanup_script_temp_files
assert_directory_not_exists "$SCRIPT_TEMP_DIR"
assert_file_not_exists "$SCRIPT_TEMP_FILE"
}

function test_globals_log_level_error() {
log "error" "hello," "error"

Expand Down
Loading