LCOV

LCOV is a plain-text code coverage format: a tracefile (usually lcov.info) that lists each source file followed by one record per line, branch and function with how many times it ran. It is named after lcov, the front-end to GCC's gcov, but today it is the format almost every coverage tool can write and almost every coverage service can read.

What is in a file

TN:
SF:src/cart/cart.ts
FN:15,applyCoupon
FNDA:9,applyCoupon
FNF:1
FNH:1
DA:15,9
DA:16,9
DA:20,7
DA:21,0
LF:4
LH:3
BRDA:20,0,0,0
BRDA:20,0,1,7
BRF:2
BRH:1
end_of_record
Record Meaning
TN:<name> Test name — often empty; several runs can be concatenated
SF:<path> Starts a source file (usually an absolute path)
FN:<line>,<name> A function declared at that line (LCOV 2 adds an end line)
FNDA:<hits>,<name> How many times that function was called
DA:<line>,<hits> An executable line and its hit count
BRDA:<line>,<block>,<branch>,<taken> One side of a branch; - means the condition never ran
LF / LH, BRF / BRH, FNF / FNH Lines, branches, functions found / hit
end_of_record Closes the file

Line coverage is LH / LF; branch coverage is BRH / BRF.

Who writes it

  • C and C++: lcov --capture over gcov data, or llvm-cov export -format=lcov
  • JavaScript and TypeScript: Istanbul/nyc, c8, Jest and Vitest with the lcov reporter, Bun (--coverage-reporter=lcov), Deno (deno coverage --lcov)
  • Python: coverage lcov (coverage.py 6.3+)
  • Rust: cargo llvm-cov --lcov, grcov
  • Dart and Flutter: flutter test --coverage writes coverage/lcov.info
  • Go: go test -coverprofile plus gcov2lcov

Codecov, Coveralls, SonarQube and the coverage gutters in VS Code and JetBrains all read it.

The parts that bite

  • Absolute paths. SF: usually holds the CI runner's path (/home/runner/work/app/app/src/…), so tools that match files against the repository need a path rewrite or --base-directory.
  • Summaries can disagree with records. LF/LH are written by the tool, not checked by the reader. When tracefiles are concatenated or edited, recount from the DA lines.
  • Merging is a sum. The same SF: in two runs should be added together (lcov --add-tracefile), so a line hit by either suite counts as covered — appending files without merging double-lists them.
  • No statements. LCOV has lines, branches and functions. Istanbul's "statements" percentage does not survive the conversion, which is why numbers drift between tools.

Reading one without genhtml

genhtml coverage.info -o out turns a tracefile into a browsable HTML tree, but it needs Perl and the lcov package. The free coverage viewer reads lcov.info (and Cobertura or Clover XML) in the browser: line, branch and function coverage, a roll-up per directory, files sorted least-covered first with their uncovered line ranges, and a Markdown summary for the pull request.

Try it

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

Open the coverage viewer →

Related