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 @@ -3,6 +3,7 @@
## Unreleased

### Changed
- Spies are substantially cheaper. `assert_have_been_called` and the spy call counter dropped their `cat` and command-substitution forks in favour of the `read` builtin and existing return-slot helpers: a 200-call spy test went from 1111ms to 170ms, against a 111ms fork-free floor
- `assert_contains_ignore_case` folds case with `shopt -s nocasematch` on Bash 3.1+ instead of two `tr` subprocesses, and falls back to `tr` only on Bash 3.0. Roughly 10x faster in a run dominated by that assertion (300 calls: 1419ms -> 131ms), with identical results including non-ASCII folding
- Internal: `src/runner.sh` and `src/coverage.sh` are split into `src/runner/` and `src/coverage/` modules of single-responsibility files, each behind a `source`-only `index.sh` aggregator. A pure relocation, no behavior change; see [ADR-010](adrs/adr-010-src-module-directories.md) (#924, #925)

Expand Down
42 changes: 31 additions & 11 deletions src/doubles/assertions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ function assert_have_been_called() {
local command=$1
bashunit::spy::times_to_slot "$command"
local times=$_BASHUNIT_SPY_TIMES_OUT
local label="${2:-$(bashunit::helper::normalize_test_function_name "${FUNCNAME[1]}")}"
local label="${2:-}"
if [ -z "$label" ]; then
bashunit::helper::normalize_test_function_name_to_slot "${FUNCNAME[1]}"
label=$_BASHUNIT_HELPER_NORMALIZED_OUT
fi

if [ "$_BASHUNIT_SPY_REGISTERED_OUT" = false ]; then
bashunit::spy::fail_unregistered "$command" "$label"
Expand Down Expand Up @@ -43,10 +47,12 @@ function assert_have_been_called_with() {
local expected="$*"

local variable
variable="$(bashunit::helper::normalize_variable_name "$command")"
bashunit::helper::normalize_variable_name_to_slot "$command"
variable=$_BASHUNIT_HELPER_VARNAME_OUT
local file_var="_BASHUNIT_SPY_${variable}_PARAMS_FILE"
local label
label="$(bashunit::helper::normalize_test_function_name "${FUNCNAME[1]}")"
bashunit::helper::normalize_test_function_name_to_slot "${FUNCNAME[1]}"
label=$_BASHUNIT_HELPER_NORMALIZED_OUT

if [ -z "${!file_var-}" ]; then
bashunit::spy::fail_unregistered "$command" "$label"
Expand Down Expand Up @@ -80,10 +86,12 @@ function assert_have_been_called_with_args() {
local expected=$_BASHUNIT_SPY_SERIALIZED_OUT

local variable
variable="$(bashunit::helper::normalize_variable_name "$command")"
bashunit::helper::normalize_variable_name_to_slot "$command"
variable=$_BASHUNIT_HELPER_VARNAME_OUT
local file_var="_BASHUNIT_SPY_${variable}_PARAMS_FILE"
local label
label="$(bashunit::helper::normalize_test_function_name "${FUNCNAME[1]}")"
bashunit::helper::normalize_test_function_name_to_slot "${FUNCNAME[1]}"
label=$_BASHUNIT_HELPER_NORMALIZED_OUT

if [ -z "${!file_var-}" ]; then
bashunit::spy::fail_unregistered "$command" "$label"
Expand Down Expand Up @@ -116,10 +124,12 @@ function assert_have_been_called_with_any() {
local expected="$*"

local variable
variable="$(bashunit::helper::normalize_variable_name "$command")"
bashunit::helper::normalize_variable_name_to_slot "$command"
variable=$_BASHUNIT_HELPER_VARNAME_OUT
local file_var="_BASHUNIT_SPY_${variable}_PARAMS_FILE"
local label
label="$(bashunit::helper::normalize_test_function_name "${FUNCNAME[1]}")"
bashunit::helper::normalize_test_function_name_to_slot "${FUNCNAME[1]}"
label=$_BASHUNIT_HELPER_NORMALIZED_OUT

if [ -z "${!file_var-}" ]; then
bashunit::spy::fail_unregistered "$command" "$label"
Expand Down Expand Up @@ -156,7 +166,11 @@ function assert_have_been_called_times() {
local command=$2
bashunit::spy::times_to_slot "$command"
local times=$_BASHUNIT_SPY_TIMES_OUT
local label="${3:-$(bashunit::helper::normalize_test_function_name "${FUNCNAME[1]}")}"
local label="${3:-}"
if [ -z "$label" ]; then
bashunit::helper::normalize_test_function_name_to_slot "${FUNCNAME[1]}"
label=$_BASHUNIT_HELPER_NORMALIZED_OUT
fi

if [ "$_BASHUNIT_SPY_REGISTERED_OUT" = false ]; then
bashunit::spy::fail_unregistered "$command" "$label"
Expand All @@ -183,10 +197,12 @@ function assert_have_been_called_nth_with() {
local expected="$*"

local variable
variable="$(bashunit::helper::normalize_variable_name "$command")"
bashunit::helper::normalize_variable_name_to_slot "$command"
variable=$_BASHUNIT_HELPER_VARNAME_OUT
local file_var="_BASHUNIT_SPY_${variable}_PARAMS_FILE"
local label
label="$(bashunit::helper::normalize_test_function_name "${FUNCNAME[1]}")"
bashunit::helper::normalize_test_function_name_to_slot "${FUNCNAME[1]}"
label=$_BASHUNIT_HELPER_NORMALIZED_OUT

bashunit::spy::times_to_slot "$command"
local times=$_BASHUNIT_SPY_TIMES_OUT
Expand Down Expand Up @@ -227,6 +243,10 @@ function assert_have_been_called_nth_with() {

function assert_not_called() {
local command=$1
local label="${2:-$(bashunit::helper::normalize_test_function_name "${FUNCNAME[1]}")}"
local label="${2:-}"
if [ -z "$label" ]; then
bashunit::helper::normalize_test_function_name_to_slot "${FUNCNAME[1]}"
label=$_BASHUNIT_HELPER_NORMALIZED_OUT
fi
assert_have_been_called_times 0 "$command" "$label"
}
23 changes: 17 additions & 6 deletions src/doubles/spy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,23 @@ _BASHUNIT_SPY_REGISTERED_OUT=false
function bashunit::spy::times_to_slot() {
local command="$1"
local variable
variable="$(bashunit::helper::normalize_variable_name "$command")"
bashunit::helper::normalize_variable_name_to_slot "$command"
variable=$_BASHUNIT_HELPER_VARNAME_OUT
local file_var="_BASHUNIT_SPY_${variable}_TIMES_FILE"
_BASHUNIT_SPY_TIMES_OUT=0
_BASHUNIT_SPY_REGISTERED_OUT=false
if [ -n "${!file_var-}" ]; then
_BASHUNIT_SPY_REGISTERED_OUT=true
fi
if [ -f "${!file_var-}" ]; then
_BASHUNIT_SPY_TIMES_OUT=$(cat "${!file_var}" 2>/dev/null || builtin echo 0)
# `read` is a builtin: the count is a single short line, so this avoids a
# `cat` fork on a path a spy-heavy test hits once per assertion.
local times_line=""
read -r times_line <"${!file_var}" 2>/dev/null || times_line=""
case "$times_line" in
'' | *[!0-9]*) _BASHUNIT_SPY_TIMES_OUT=0 ;;
*) _BASHUNIT_SPY_TIMES_OUT=$times_line ;;
esac
fi
}

Expand Down Expand Up @@ -87,7 +95,8 @@ function bashunit::spy::call_log_to_slot() {
_BASHUNIT_SPY_CALL_LOG_OUT=""

local variable
variable="$(bashunit::helper::normalize_variable_name "$command")"
bashunit::helper::normalize_variable_name_to_slot "$command"
variable=$_BASHUNIT_HELPER_VARNAME_OUT
local file_var="_BASHUNIT_SPY_${variable}_PARAMS_FILE"
if [ -z "${!file_var-}" ] || [ ! -f "${!file_var}" ]; then
return
Expand Down Expand Up @@ -159,7 +168,8 @@ function bashunit::spy() {
local command=$1
local exit_code_or_impl="${2:-}"
local variable
variable="$(bashunit::helper::normalize_variable_name "$command")"
bashunit::helper::normalize_variable_name_to_slot "$command"
variable=$_BASHUNIT_HELPER_VARNAME_OUT

local times_file params_file
local test_id="${BASHUNIT_CURRENT_TEST_ID:-global}"
Expand Down Expand Up @@ -188,8 +198,9 @@ function bashunit::spy() {
done
serialized=\${serialized%$'\\x1f'}
builtin printf '%s\x1e%s\\n' \"\$raw\" \"\$serialized\" >> '$params_file'
local _c
_c=\$(cat '$times_file' 2>/dev/null || builtin echo 0)
local _c=\"\"
read -r _c < '$times_file' 2>/dev/null || _c=\"\"
case \"\$_c\" in '' | *[!0-9]*) _c=0 ;; esac
_c=\$((_c+1))
builtin echo \"\$_c\" > '$times_file'
$body_suffix
Expand Down
Loading