From bcf5952bc18339f510e66f51db3c2ae2d05e2d2e Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Sat, 18 Apr 2026 00:04:19 +0200 Subject: [PATCH 1/3] perf(runner): replace grep/sed with parameter expansion in parse_result_sync Drop six echo|sed subprocesses and one grep per sync test result parse at src/runner.sh:971. Use bash parameter expansion (##*, %%##*) in a single case branch, matching the pattern already used by the async path at lines 714-727. Bash 3.0+ compatible. --- src/runner.sh | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/runner.sh b/src/runner.sh index a4fe37fc..5dac5084 100755 --- a/src/runner.sh +++ b/src/runner.sh @@ -966,16 +966,32 @@ function bashunit::runner::parse_result_sync() { local assertions_snapshot=0 local test_exit_code=0 - # Extract values using sed instead of BASH_REMATCH for Bash 3.0+ compatibility - # shellcheck disable=SC2001 - if [ "$(echo "$result_line" | "$GREP" -cE 'ASSERTIONS_FAILED=[0-9]*##ASSERTIONS_PASSED=[0-9]*' || true)" -gt 0 ]; then - assertions_failed=$(echo "$result_line" | sed 's/.*ASSERTIONS_FAILED=\([0-9]*\)##.*/\1/') - assertions_passed=$(echo "$result_line" | sed 's/.*ASSERTIONS_PASSED=\([0-9]*\)##.*/\1/') - assertions_skipped=$(echo "$result_line" | sed 's/.*ASSERTIONS_SKIPPED=\([0-9]*\)##.*/\1/') - assertions_incomplete=$(echo "$result_line" | sed 's/.*ASSERTIONS_INCOMPLETE=\([0-9]*\)##.*/\1/') - assertions_snapshot=$(echo "$result_line" | sed 's/.*ASSERTIONS_SNAPSHOT=\([0-9]*\)##.*/\1/') - test_exit_code=$(echo "$result_line" | sed 's/.*TEST_EXIT_CODE=\([0-9]*\).*/\1/') - fi + # Extract values using parameter expansion instead of spawning grep/sed subprocesses + case "$result_line" in + *"ASSERTIONS_FAILED="*"##ASSERTIONS_PASSED="*) + local _tail + _tail="${result_line##*ASSERTIONS_FAILED=}" + assertions_failed="${_tail%%##*}" + _tail="${result_line##*ASSERTIONS_PASSED=}" + assertions_passed="${_tail%%##*}" + _tail="${result_line##*ASSERTIONS_SKIPPED=}" + assertions_skipped="${_tail%%##*}" + _tail="${result_line##*ASSERTIONS_INCOMPLETE=}" + assertions_incomplete="${_tail%%##*}" + _tail="${result_line##*ASSERTIONS_SNAPSHOT=}" + assertions_snapshot="${_tail%%##*}" + _tail="${result_line##*TEST_EXIT_CODE=}" + test_exit_code="${_tail%%##*}" + # Strip any trailing non-digit suffix (end of line) from the final field + test_exit_code="${test_exit_code%%[!0-9]*}" + : "${assertions_failed:=0}" + : "${assertions_passed:=0}" + : "${assertions_skipped:=0}" + : "${assertions_incomplete:=0}" + : "${assertions_snapshot:=0}" + : "${test_exit_code:=0}" + ;; + esac bashunit::internal_log "[SYNC]" "fn_name:$fn_name" "execution_result:$execution_result" From 2582b3318cc29bbb6ac7a94311f2d610e85908c5 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Sat, 18 Apr 2026 00:05:17 +0200 Subject: [PATCH 2/3] perf(runner): adaptive backoff in wait_for_job_slot poll loop Replace fixed 50ms poll with stepped backoff (50ms -> 100ms after 4 iterations -> 200ms after 20) in src/runner.sh:15. Long-running parallel tests no longer spawn `jobs -r | wc -l` 20 times per second per wait; short tests keep the original responsiveness. Bash 3.0+ compatible. --- src/runner.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/runner.sh b/src/runner.sh index 5dac5084..a614c515 100755 --- a/src/runner.sh +++ b/src/runner.sh @@ -18,13 +18,23 @@ function bashunit::runner::wait_for_job_slot() { return 0 fi + # Adaptive backoff: start at 50ms, grow to 200ms to reduce `jobs -r` overhead + # on long-running tests while keeping short tests responsive. + local delay="0.05" + local iterations=0 while true; do local running_jobs running_jobs=$(jobs -r | wc -l) if [ "$running_jobs" -lt "$max_jobs" ]; then break fi - sleep 0.05 + sleep "$delay" + iterations=$((iterations + 1)) + if [ "$iterations" -eq 4 ]; then + delay="0.1" + elif [ "$iterations" -eq 20 ]; then + delay="0.2" + fi done } From 4af7d099e64e6f207f34311df0ab63a8aa051d10 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Sat, 18 Apr 2026 00:06:49 +0200 Subject: [PATCH 3/3] perf(runner): collapse metachar check to single regex in parse_data_provider_args Merge the leading-metachar and embedded-metachar grep calls at src/runner.sh:263 into one `(^|[^\])[|&;*]` pattern. Saves one grep subprocess per parametrized test invocation. Bash 3.0+ compatible. --- src/runner.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/runner.sh b/src/runner.sh index a614c515..833c6f35 100755 --- a/src/runner.sh +++ b/src/runner.sh @@ -269,12 +269,11 @@ function bashunit::runner::parse_data_provider_args() { local -a args=() local args_count=0 - # Check for shell metacharacters that would break eval or cause globbing + # Check for unescaped shell metacharacters that would break eval or cause + # globbing. Combines the leading-metachar case and the embedded-metachar + # case into a single regex to avoid a second grep subprocess per call. local has_metachar=false - local _re1='[^\\][\|\&\;\*]' - local _re2='^[\|\&\;\*]' - if [ "$(echo "$input" | "$GREP" -cE "$_re1" || true)" -gt 0 ] \ - || [ "$(echo "$input" | "$GREP" -cE "$_re2" || true)" -gt 0 ]; then + if [ "$(echo "$input" | "$GREP" -cE '(^|[^\])[|&;*]' || true)" -gt 0 ]; then has_metachar=true fi