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 @@ -9,6 +9,7 @@
- JSON reports write ordinary filenames, test names and empty messages without per-field processes, preserving existing escaping (#1369)

### Fixed
- Concurrent provider rows have separate temporary-file owners, including rows with identical arguments (#1379)
- Skipped macOS and Windows acceptance CI jobs have readable names, with shard labels in their test steps
- Cancellation regression checks distinguish exited bodies awaiting reaping from running cleanup (#1385)
- Synchronous provider rows retain separate results in parallel runs, so later passing rows cannot hide failures (#1377)
Expand Down
4 changes: 3 additions & 1 deletion src/runner/context.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ function bashunit::runner::export_test_identity() {
local test_file=$1
local fn_name=$2
bashunit::helper::generate_id "$fn_name"
export BASHUNIT_CURRENT_TEST_ID="$_BASHUNIT_HELPER_ID_OUT"
local file_ordinal=${_BASHUNIT_REPORTS_FILE_ORDINAL:-0}
local test_ordinal=${_BASHUNIT_RUNNER_RESULT_ORDINAL:-0}
export BASHUNIT_CURRENT_TEST_ID="${_BASHUNIT_HELPER_ID_OUT}_${file_ordinal}_${test_ordinal}"
# Carry the inputs; do not resolve. Reading the definition line costs a
# subshell, and only a failure message and a report row ever ask for it
# (#1346). Cleared per test so a previous test's line cannot answer for this
Expand Down
114 changes: 114 additions & 0 deletions tests/acceptance/bashunit_run_forks_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,120 @@ function test_run_removes_its_run_output_dir() {
assert_equals 0 "$leftover"
}

function test_provider_temp_owners_survive_other_rows_cleanup() {
local dir
dir="$(unset BASHUNIT_CURRENT_TEST_ID; bashunit::temp_dir)"
local fixture="$dir/first_test.sh"
cat >"$fixture" <<'FIXTURE'
OWNER_COORD_DIR="$OWNER_PROBE_ROOT/${BASH_SOURCE[0]##*/}"

function set_up_before_script() {
mkdir "$OWNER_COORD_DIR"
OWNER_SCRIPT_FILE=$(bashunit::temp_file)
OWNER_SCRIPT_DIR=$(bashunit::temp_dir)
printf '%s\n' "$OWNER_SCRIPT_FILE" "$OWNER_SCRIPT_DIR" >"$OWNER_COORD_DIR/script_paths"
}

function _wait_for_owner_path() {
local path=$1 state=$2 deadline=$((SECONDS + 10))
while [ "$SECONDS" -lt "$deadline" ]; do
case "$state" in
present) [ -f "$path" ] && return 0 ;;
absent) [ ! -e "$path" ] && return 0 ;;
esac
sleep 0.01
done
assert_same "$state: $path" "timed out waiting for cleanup coordination"
return 1
}

function owner_probe_rows() { printf '%s\n' same same; }

# @data_provider owner_probe_rows
function test_overlapping_temp_owner_rows() {
assert_same same "$1"
local row=$_BASHUNIT_RUNNER_RESULT_ORDINAL temp_dir temp_file
temp_dir=$(bashunit::temp_dir)
temp_file=$(bashunit::temp_file)
printf 'still alive\n' >"$temp_file"
printf '%s\n' "$BASHUNIT_CURRENT_TEST_ID" "$temp_dir" "$temp_file" >"$OWNER_COORD_DIR/row_$row"
: >"$OWNER_COORD_DIR/ready_$row"
if [ "$row" -eq 1 ]; then
if [ "$OWNER_PROBE_PARALLEL" = true ]; then
_wait_for_owner_path "$OWNER_COORD_DIR/ready_2" present || return
fi
return
fi

_wait_for_owner_path "$OWNER_COORD_DIR/ready_1" present || return
local quick_owner quick_dir quick_file
{
IFS= read -r quick_owner
IFS= read -r quick_dir
IFS= read -r quick_file
} <"$OWNER_COORD_DIR/row_1"
_wait_for_owner_path "$quick_dir" absent || return
_wait_for_owner_path "$quick_file" absent || return
assert_file_not_exists "$quick_file"
assert_directory_exists "$temp_dir"
assert_file_contains "$temp_file" 'still alive'
assert_not_equals "$quick_owner" "$BASHUNIT_CURRENT_TEST_ID"
: >"$OWNER_COORD_DIR/late_row_checked"
}

function tear_down_after_script() {
assert_file_exists "$OWNER_SCRIPT_FILE"
assert_directory_exists "$OWNER_SCRIPT_DIR"
: >"$OWNER_COORD_DIR/script_teardown_checked"
}
FIXTURE
cp "$fixture" "$dir/second_test.sh"

local mode parallel strict root output status file path owner first_owner
local -a flags
for mode in parallel parallel_strict sequential sequential_strict; do
root="$dir/$mode"
mkdir "$root"
parallel=false
strict=false
flags=(--no-parallel)
case "$mode" in
parallel*) parallel=true; flags=(--parallel --jobs 2) ;;
esac
case "$mode" in
*_strict) strict=true ;;
esac
status=0
output=$(TMPDIR="$root" OWNER_PROBE_ROOT="$root" OWNER_PROBE_PARALLEL="$parallel" \
BASHUNIT_STRICT_MODE="$strict" ./bashunit --skip-env-file --simple "${flags[@]}" \
"$fixture" "$dir/second_test.sh" 2>&1) || status=$?

assert_contains '4 passed' "$output"
assert_same 0 "$status"
first_owner=""
for file in first_test.sh second_test.sh; do
assert_file_exists "$root/$file/late_row_checked"
assert_file_exists "$root/$file/script_teardown_checked"
IFS= read -r owner <"$root/$file/row_1"
if [ -n "$first_owner" ]; then
assert_not_equals "$first_owner" "$owner"
fi
first_owner=$owner
while IFS= read -r path; do
assert_file_not_exists "$path"
done <"$root/$file/script_paths"
for owner in 1 2; do
{
IFS= read -r path
while IFS= read -r path; do
assert_file_not_exists "$path"
done
} <"$root/$file/row_$owner"
done
done
done
}

# Regression guard for the parallel per-test result path. Publishing each
# test's result file used to fork `basename` (suite dir name), `mkdir -p`
# (suite dir, per test), an `echo | tr | sed` pipeline (arg sanitizing, even
Expand Down
Loading