Problem
The plain documented command produces XML that no parser accepts:
$ bashunit --output junit tests/ > report.xml
$ xmllint --noout report.xml
report.xml:2: parser error : XML declaration allowed only at the start of the document
The first byte of the document is a newline. Parallel output is fine, and parallel is not the default (_BASHUNIT_DEFAULT_PARALLEL_RUN="false"), so the default invocation is the broken one.
Cause
bashunit::runner::run_tear_down_after_script prints a blank line between a file's tests and the next when the file has no tear_down_after_script. It is guarded against --simple, --failures-only, --no-progress and --parallel — but not against a machine --output. The document is written at the end of the run, so the blank line lands in front of it.
Reproduced by mode:
| run |
first bytes |
xmllint |
--no-parallel --output junit |
\n<?xml |
parser error |
--parallel --output junit |
<?xml |
OK |
--output json gains the same leading blank line. It went unnoticed because JSON tolerates leading whitespace, so jq accepts it and the existing test — which only checks that it parses — stayed green.
Why the existing guard missed it
test_output_junit_starts_with_the_xml_declaration_under_parallel exists for exactly this hazard but pins only the parallel path, which is the one that was already correct.
Fix
Suppress the blank line for json and junit. Name those two formats rather than using is_machine_output_enabled, which also covers tap: TAP streams line by line, a blank line between files is valid there, and its snapshots record it — using the broader predicate changes two TAP snapshots for no reason.
Problem
The plain documented command produces XML that no parser accepts:
The first byte of the document is a newline. Parallel output is fine, and parallel is not the default (
_BASHUNIT_DEFAULT_PARALLEL_RUN="false"), so the default invocation is the broken one.Cause
bashunit::runner::run_tear_down_after_scriptprints a blank line between a file's tests and the next when the file has notear_down_after_script. It is guarded against--simple,--failures-only,--no-progressand--parallel— but not against a machine--output. The document is written at the end of the run, so the blank line lands in front of it.Reproduced by mode:
xmllint--no-parallel --output junit\n<?xml--parallel --output junit<?xml--output jsongains the same leading blank line. It went unnoticed because JSON tolerates leading whitespace, sojqaccepts it and the existing test — which only checks that it parses — stayed green.Why the existing guard missed it
test_output_junit_starts_with_the_xml_declaration_under_parallelexists for exactly this hazard but pins only the parallel path, which is the one that was already correct.Fix
Suppress the blank line for
jsonandjunit. Name those two formats rather than usingis_machine_output_enabled, which also coverstap: TAP streams line by line, a blank line between files is valid there, and its snapshots record it — using the broader predicate changes two TAP snapshots for no reason.