# Share a GoogleTest Report — C++ Results as a Link, Not a Log Tail

Canonical: https://commareports.com/share-gtest-report
Published: 2026-09-08

> GoogleTest writes XML, not HTML. Convert it and publish to Comma so a C++ failure arrives as a readable report with a comment thread, instead of 4,000 lines of CI log.

# 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.

```bash
./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

```bash
./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:

```bash
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](https://commareports.com/), or POST it:

```bash
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

```bash
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](/docs/ci) 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:

```bash
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](/comment-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](/docs/sharing).

## 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.

**[Publish a test report →](https://commareports.com/)**

### Related

- [Share a CTest report](/share-ctest-report) · [Share a Catch2 report](/share-catch2-report)
- [Share a JUnit report](/share-junit-report) · [Share a coverage report](/share-coverage-report)
- [Share a Doxygen doc](/share-doxygen-docs) · [Publish from CI](/docs/ci)
