🤔 Background
bashunit::reports::add_test base64-encodes each of the nine fields of a result row separately in every --parallel worker, and load_spooled decodes each one the same way in the parent (src/reports/collect.sh). On macOS the encode path is printf | base64 | tr -d '\n', so each field costs two forks plus the $() subshell.
PATH-shim census, four trivial tests with --parallel --log-junit:
base64 forks: 56 (28 encodes + 28 decodes)
Fourteen per test, plus a tr per encode. Measured on a 300-test file, two rounds each:
| wall | CPU |
|---|
--parallel | 1.22 s | 5.8 s |
--parallel --log-junit | 9.19 s | 27.4 s |
7.5x wall, 4.7x CPU — turning on the report costs far more than running the tests. This is the standard CI setup, so most CI users are paying it.
💡 Proposal
Only four of the nine fields can hold arbitrary text: the file path, the test name (which may end in a data provider's arguments), the failure message and the captured output. The other five are a status word and four numbers produced by bashunit's own callers.
Encode those four, join all nine with the ASCII unit separator (0x1F), write one line. Base64 output is [A-Za-z0-9+/=] and the raw fields are numeric, so no field can contain the separator and the row stays a single line. The separator is not IFS whitespace, so a run of them yields empty fields rather than collapsing — which is what an absent message has to produce.
A passing test carries no message and no output, so both of those short-circuit on the existing empty sentinel: the row costs two encodes and two decodes instead of fourteen.
The spool is written and read within one run, so there is no format compatibility concern.
🤔 Background
bashunit::reports::add_testbase64-encodes each of the nine fields of a result row separately in every--parallelworker, andload_spooleddecodes each one the same way in the parent (src/reports/collect.sh). On macOS the encode path isprintf | base64 | tr -d '\n', so each field costs two forks plus the$()subshell.PATH-shim census, four trivial tests with
--parallel --log-junit:Fourteen per test, plus a
trper encode. Measured on a 300-test file, two rounds each:--parallel--parallel --log-junit7.5x wall, 4.7x CPU — turning on the report costs far more than running the tests. This is the standard CI setup, so most CI users are paying it.
💡 Proposal
Only four of the nine fields can hold arbitrary text: the file path, the test name (which may end in a data provider's arguments), the failure message and the captured output. The other five are a status word and four numbers produced by bashunit's own callers.
Encode those four, join all nine with the ASCII unit separator (0x1F), write one line. Base64 output is
[A-Za-z0-9+/=]and the raw fields are numeric, so no field can contain the separator and the row stays a single line. The separator is not IFS whitespace, so a run of them yields empty fields rather than collapsing — which is what an absent message has to produce.A passing test carries no message and no output, so both of those short-circuit on the existing empty sentinel: the row costs two encodes and two decodes instead of fourteen.
The spool is written and read within one run, so there is no format compatibility concern.