Share a CTest report

CTest's designed sharing story is CDash: run the tests with -T Test and -T Submit, and results appear on a dashboard server. That works, and it means standing up and maintaining a CDash instance — a reasonable trade for LLVM or VTK, and disproportionate for a team of six who want to send a colleague the list of what broke.

The alternative most teams end up with is pasting the tail of ctest output into a chat window:

The following tests FAILED:
	 14 - buffer_unaligned_read (Failed)
	 22 - net_timeout_retry (Timeout)
Errors while running CTest

Which names the failures and tells you nothing about them.

Emit JUnit XML

CMake 3.21 and later can write JUnit directly:

ctest --output-on-failure --output-junit results.xml

--output-on-failure is the important half — without it the XML records that a test failed and not what it printed, and a C++ test's stdout is usually the entire diagnosis.

On older CMake, use the CDash-format output and convert:

ctest -T Test --output-on-failure     # → Testing/<tag>/Test.xml

Convert and publish

npx xunit-viewer --results=results.xml --output=report.html
# or
pip install junit2html && junit2html results.xml report.html

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 "CTest — libcore, $(git rev-parse --short HEAD)" \
        '{title: $t, html: $html}')"

The generated page gives you the suite tree, durations per test, and the captured output expanded under each failure — which is the thing the console tail leaves out.

Timeouts deserve their own attention

CTest reports timeouts as failures, and they are the category most likely to be a flake and most likely to be misdiagnosed from a log tail. Set the timeout explicitly so the report distinguishes them:

set_tests_properties(net_timeout_retry PROPERTIES TIMEOUT 30)

Then a timeout in the published report is a test that exceeded a limit somebody chose, rather than one that hit CTest's default and might simply need longer on a slow runner.

From CI

ctest --output-on-failure --output-junit results.xml || TESTS_FAILED=1
npx xunit-viewer --results=results.xml --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 "CTest — $PRESET @ $GITHUB_SHA" '{title: $t, html: $html}')"

exit "${TESTS_FAILED:-0}"

Publish before you exit, and run the step with if: always() (GitHub Actions), when: always (GitLab, CircleCI) or post { always { … } } (Jenkins).

Use one report id per CMake preset or toolchain. A C++ project's interesting failures are usually configuration-specific — Debug-only, ASan-only, MSVC-only — and a per-configuration URL makes that visible instead of leaving someone to correlate build numbers.

What review adds

  • Anchored threads on the failing test — see commenting on HTML.
  • Revisions, so an intermittent timeout has a history.
  • Access per report — private, team, domain-gated or named reviewers. See the sharing model.

Limits

  • Entry HTML: 5 MB.
  • 60 requests/minute per token.

Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision history.

Publish a test report →

Related