Skip to content

perf(assert): remove per-assertion array-join fork in assert.sh #844

Description

@Chemaclass

Problem

Most core assertions join their haystack args with a command-substitution fork just to build the compared string:

actual=$(printf '%s\n' "${actual_arr[@]}")

This forks a subshell on every assertion call. Micro-benchmark (20 000 iterations, Bash 3.2):

$(printf '%s\n' ...)      : 10.44s
IFS join (fork-free)      :  0.09s   # ~100x faster

On assertion-heavy suites this is the dominant per-assertion cost. assert_contains already matches with a native case; only the join in front of it still forks.

Fix

Replace the fork with a Bash-3.0-safe, fork-free IFS join:

local IFS=$'\n'
actual="${actual_arr[*]}"

Affected sites (src/assert.sh)

All actual=$(printf '%s\n' "${actual_arr[@]}") occurrences — currently ~8, including assert_contains, assert_not_contains, assert_matches, assert_not_matches, and the array-shaped assertions around lines 277, 330, 353, 379, 656, 701, 726. grep -nE '=\$\(printf .%s.n. "\$\{actual_arr' src/assert.sh lists them.

IMPORTANT — per-site semantic check (do NOT blanket-sed)

The IFS join differs from printf '%s\n' in two ways:

  1. No trailing newline after the last element.
  2. Elements are joined between only.
  • Safe where actual feeds a substring case, a grep/regex match, or is only shown on failure (trailing-newline irrelevant): assert_contains, assert_not_contains, assert_matches, assert_not_matches.
  • Verify carefully any site where actual is exact-compared or its exact byte form is asserted in a snapshot — if a test pins the trailing newline, either keep that site or adjust deliberately.

Convert only the sites you can prove are behavior-identical. Leave the rest and note them.

Bash 3.0+ constraint

Hard floor is Bash 3.0 (CI runs a 3.0 job). printf -v is 3.1+ — do NOT use it. The IFS "${arr[*]}" join is the only 3.0-safe fork-free approach. No [[ ]], ${var,,}, declare -A, ${arr[-1]}, &>>.

TDD

  1. RED: existing assertion tests already cover behavior; add a targeted test if any converted assertion lacks trailing-newline / multi-arg coverage.
  2. GREEN: convert one site, keep suite green.
  3. Repeat per site.

Acceptance criteria

  • Every converted site is behavior-identical (proven, per-site).
  • ./bashunit tests/ and ./bashunit --parallel --simple --strict tests/ green.
  • make sa, make lint, ./build.sh --verify green.
  • Before/after benchmark included in the PR (e.g. a suite of N assert_contains calls, wall-time delta).
  • No CHANGELOG entry needed (internal, no user-facing change) unless behavior anywhere is intentionally touched.

Metadata

Metadata

Assignees

Labels

refactoringRefactoring or cleaning related

Type

No type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions