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
4 changes: 4 additions & 0 deletions .claude/rules/perf-fork-budget.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ only** β€” toggling it in the caller's shell clobbers caller state (#808).
because 3.2 changed whether a quoted right-hand side is a regex or a literal,
so at the 3.0 floor the same pattern would match differently across supported
versions. Removing the fork means picking one semantic and breaking the other.
Measured cost of keeping it, so the trade is at least quantified: 2000
`assert_same` run in ~130 ms (0.065 ms each), 500 `assert_matches` in ~1.25 s
(**2.5 ms each, ~38x**). That is the fork, and it is the price of the 3.0
floor -- not a regression to chase.
- **Single-file build artifact**: sourcing `bin/bashunit` is *not* faster than
sourcing `src/*.sh` (parse time dominates, file opens don't).
- **`tput cols` at startup**: returns 80 on non-tty; snapshots depend on that
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- `--verbose` warns on Bash 3.x that coverage does not count lines run inside a subshell, so a percentage that reads lower there than on Bash 4+ explains itself (#1112)

### Changed
- Docs: `assert_matches` notes that it runs `grep -E` in a subprocess per call β€” measured at ~2.5ms against ~0.065ms for `assert_same`, about 38x β€” and points at `assert_contains` for hot loops where the pattern is a fixed substring. The subprocess is deliberate: Bash 3.2 changed whether a quoted right-hand side of `[[ =~ ]]` is a regex or a literal, so at the Bash 3.0 floor the same pattern would match differently across supported versions (#1187)
- Docs: `assert_match_snapshot` warns that a `@data_provider` test shares one snapshot across all its values β€” the filename comes from the test function, so the first value creates it and the rest fail against its content. `assert_match_named_snapshot "$1"` gives each value its own (#1185)
- A test file that fails to source without writing to stderr now reports its size and says there was no stderr, so a truncated file can be told apart from one whose last command returned non-zero (#1137)
- Performance: cold start makes two fewer forks β€” `check_os::init` ran twice, once at source time and again from the entrypoint, and the root directory came from `$(dirname …)` β€” worth about 4ms of a 65ms startup, on every invocation (#1124)
Expand Down
14 changes: 14 additions & 0 deletions docs/assertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,20 @@ function test_failure() {
```
:::

::: tip Cost in a hot loop
This assertion runs `grep -E` in a subprocess for every call, so it is far more
expensive than a string comparison: measured here, 500 `assert_matches` take
~1.25 s against ~130 ms for 2000 `assert_same` β€” about **38x per call**.

That subprocess is deliberate. Bash 3.2 changed whether a quoted right-hand side
of `[[ =~ ]]` is a regex or a literal, so at bashunit's Bash 3.0 floor the same
pattern would match differently across supported versions.

It is irrelevant for ordinary suites. If you are asserting inside a large loop
and the pattern is a fixed substring or a glob, prefer
[assert_contains](#assert-contains).
:::

## assert_string_starts_with
> `assert_string_starts_with "needle" "haystack"...`

Expand Down
Loading