Share a GoogleTest report
C++ test output has an unusual sharing problem: there is no HTML report to share. GoogleTest writes to the console and to XML, and that is it.
./my_tests --gtest_output=xml:results.xml
The XML is the JUnit schema, which is the good news — it means the whole
JUnit-consuming ecosystem already reads GoogleTest output. But results.xml
is not something you send a colleague, so what actually gets shared is a
paste of the console tail:
[ FAILED ] BufferTest.HandlesUnalignedRead
/src/buffer_test.cc:214: Failure
Expected equality of these values:
buf.read_u32(1)
Which is: 3735928559
expected
Which is: 3405691582
Which is fine for one failure and useless for nineteen, and which arrives with no way to see what else failed, what passed, or how long the run took.
Convert the XML
./my_tests --gtest_output=xml:results.xml
# either of these
npx xunit-viewer --results=results.xml --output=report.html
pip install junit2html && junit2html results.xml report.html
Both produce a single self-contained page: the suite tree, pass/fail/skip counts, per-test durations, and the assertion message expanded under each failure.
For a sharded run, merge first:
for i in 0 1 2 3; do
GTEST_TOTAL_SHARDS=4 GTEST_SHARD_INDEX=$i \
./my_tests --gtest_output="xml:results-$i.xml" &
done
wait
npx xunit-viewer --results=. --output=report.html
Publish it
Drag report.html into the app, or POST it:
curl -fsS -X POST "https://commareports.com/api/v1/reports" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html report.html \
--arg t "Unit tests — libcore @ $(git rev-parse --short HEAD)" \
'{title: $t, html: $html}')"
Report HTML renders with scripts enabled inside a sandboxed iframe
(allow-scripts, no allow-same-origin), so the filter controls and the
expandable failure details in the generated report keep working.
From CI
ctest --output-on-failure || TESTS_FAILED=1
npx xunit-viewer --results=build/test-results --output=report.html
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html report.html \
--arg t "Unit tests — libcore @ $GITHUB_SHA" '{title: $t, html: $html}')"
exit "${TESTS_FAILED:-0}"
Publish before failing the job, with if: always() (GitHub Actions),
when: always (GitLab, CircleCI) or post { always { … } } (Jenkins). A green
run's report is a formality; a red run's report is the whole point, and it is
the one a build that exits early never produces.
PATCHing one report id keeps a single URL per suite and appends a revision per run — so "is this the flake from Tuesday?" is a diff instead of a search through expired artifacts.
Platform matrices
C++ suites usually run across a matrix — GCC and Clang, Linux and macOS, x86-64 and ARM — and the interesting failures are the ones that only appear on one cell. Use one report id per cell:
REPORT_ID="${REPORT_IDS[$MATRIX_KEY]}"
so each configuration has a stable URL and its own revision history. "Fails on ARM only" then has a link rather than a claim.
What review adds
- Anchored threads on the failing assertion — see commenting on HTML.
- Revisions, so a flake's history is visible rather than remembered.
- Access per report — private, team, domain-gated or named reviewers. See the sharing model.
Limits
- Entry HTML: 5 MB. A suite with tens of thousands of test cases can approach it — publish the failures-and-summary view rather than every passing case.
- 60 requests/minute per token.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.