Skip to content
Justin SilverTECHNOLOGY, TRAVEL & EVERYTHING IN BETWEEN

Programming

Viewing coverage across languages with doublcov

April 25, 2026

The doublcov geometric owl mascot in green and dark blue.

I wanted a better way to visualize test coverage across projects written in different languages. The report needed to be more modern and easier to read, with search and navigation that made it straightforward to find the code that still needed tests.

I built doublcov to give those projects a common coverage view. It takes LCOV data and source files and builds a report you can browse locally or publish as a static site. Tracking coverage over time and fitting into existing CI workflows were part of what I wanted it to do.

Finding the gaps in coverage

A summary percentage is a useful starting point. When I am working on tests, I also want to get to the relevant source and see which lines or branches were missed.

Doublcov includes source browsing with syntax highlighting, search across files, functions and source text, and navigation between uncovered items. Line, function and branch totals stay visible while inspecting a file. You can filter the file list to concentrate on files with gaps, then move directly to the uncovered code. Themes and project configuration let the same report fit different projects.

One input format across languages

LCOV records coverage information against source files, including line hits and function and branch counts. Using it as the common input lets a project keep its own testing tools while using doublcov for the report.

The CLI also includes builders that run a project's coverage tool and pass its LCOV output into the report builder:

  • Solidity with Foundry or Hardhat.
  • JavaScript and TypeScript with Vitest, Jest or the Node test runner through c8.
  • Python with pytest-cov.
  • Rust with cargo-llvm-cov or cargo-tarpaulin.
  • C and C++ with gcov/lcov.

The corresponding test and coverage tools need to be installed in the project. An existing LCOV file can also go straight into the generic builder. Source detection and highlighting cover more languages, and unknown source extensions can be included as plain text.

The npm CLI requires Node.js 22 or newer. The commands and features here reflect version 0.4.4. For a project that already produces coverage/lcov.info:

npm install --save-dev @0xdoublesharp/doublcov
npx doublcov build --lcov coverage/lcov.info --sources src --out coverage/report --no-open

The local default is a standalone report. Open coverage/report/index.html to browse it. The HTML contains the report and assets, so it can be opened directly from disk.

Keeping coverage history

I also wanted to see how coverage changed over time. Doublcov stores run history in .doublcov/history.json and includes that history in the report, with totals and file coverage from previous runs.

For CI, restore that file before building and save it afterward. A CI cache or another persistent store can carry it between runs. Keeping the current report and keeping the history are separate steps in the workflow, so the history file needs to survive the runner.

Publishing reports from CI

There are two output formats. Standalone mode packages the report into one HTML file. Static mode writes the HTML, assets and report data separately, with source data loaded as needed. Static mode is useful for larger reports and publishing to a site such as GitHub Pages:

npx doublcov build --lcov coverage/lcov.info --sources src --out coverage/report --mode static --no-open

For GitHub Actions, the official action can download the CLI release and build the report. This is a step to add after the workflow has generated LCOV:

- uses: doublesharp/doublcov@v0
  with:
    version: v0.4.4
    command: build
    args: --lcov coverage/lcov.info --sources src --out coverage/report --mode static

Upload coverage/report as a workflow artifact to keep a downloadable copy, or add the Pages upload and deployment steps to publish it. The CI and hosting guide includes the complete publishing flow. Other CI systems can run the CLI and publish the same output directory to their static host.

The generated files can be served by a static web host. The CLI is available through npm, with standalone binaries and a Docker image for workflows that use those instead.

The library page has an overview, and the README links the per-framework guides and configuration options.