coverage.correlator

Path correlator: normalise source paths across build host, remote hosts, and gcda metadata.

gcda files embed the absolute paths used at compile time. When run on a remote host those paths rarely match the local source tree — especially in cross-compilation scenarios. This module handles the remapping.

class otto.coverage.correlator.paths.PathMapping(from_prefix, to_prefix)

Bases: object

A single path-prefix substitution rule.

from_prefix is matched against embedded .info paths. to_prefix is the replacement on the local build host.

from_prefix : --is-rst--:py:class:`str`
to_prefix : --is-rst--:py:class:`str`
apply(path)

Return the remapped path if this rule matches, else None.

Return type:

str | None

class otto.coverage.correlator.paths.PathCorrelator(mappings)

Bases: object

Apply a sequence of PathMapping rules to normalise file paths.

Mappings are tried in order; first match wins. This lets you layer rules for different build environments (local dev, CI, release).

resolve(raw_path)
Return type:

Path | None

resolve_strict(raw_path)
Return type:

Path

async otto.coverage.correlator.paths.discover_path_mappings(info_path, source_root, localhost)

Auto-discover path mappings from a sample .info file.

Parses SF: lines from the .info file, extracts the common prefix of the embedded paths, and builds a mapping to source_root.

This eliminates the need for users to manually reverse-engineer compiler -fdebug-prefix-map settings.

Parameters:
  • info_path – Path to a captured .info file (from lcov --capture).

  • source_root – Local source tree root to map into.

  • localhost – LocalHost instance for running commands.

Return type:

list[PathMapping]

Returns:

A list containing a single PathMapping from the discovered common prefix to source_root, or an empty list if no SF: lines were found.

async otto.coverage.correlator.paths.discover_from_gcno(gcno_dir, source_root, localhost)

Auto-discover path mappings by inspecting .gcno file contents.

Uses strings to extract embedded paths from .gcno files, then finds the common prefix and maps it to source_root.

This is useful when no .info file is available yet (before the first lcov --capture).

Parameters:
  • gcno_dir – Directory containing .gcno files from the build.

  • source_root – Local source tree root to map into.

  • localhost – LocalHost instance for running commands.

Return type:

list[PathMapping]

Returns:

A list with a single PathMapping, or empty if discovery fails.

Parse lcov .info files and load them into a CoverageStore.

The lcov .info format is stable and well-documented: https://manpages.ubuntu.com/manpages/focal/man1/geninfo.1.html

Format summary:

TN:<test name>
SF:<source file path>
FN:<line>,<function name>
FNDA:<count>,<function name>
DA:<line>,<count>[,<checksum>]
BRDA:<line>,<block>,<branch>,<taken>   taken='-' means never reached
BRH:<hit>,<found>
end_of_record
class otto.coverage.correlator.lcov_loader.LCOVLoader(store, correlator)

Bases: object

Parse .info files and load them into a CoverageStore under a named tier.

Tier names are free-form strings. The loader registers each tier with the store on first use so the store’s tier_order reflects the data without callers having to pre-declare every tier.

Example:

store = CoverageStore(tier_order=["unit", "system", "manual"])
correlator = PathCorrelator([...])
loader = LCOVLoader(store, correlator)

loader.load("system_merged.info", "system")
loader.load("unit_tests.info",    "unit")
load(info_path, tier)

Load an .info file into the store under tier.

Returns the number of source files loaded.

Return type:

int

Merge gcda files from multiple hosts using lcov.

Wraps lcov --capture and lcov --add-tracefile invocations, executing them through LocalHost so they are fully async with proper logging and timeout handling.

class otto.coverage.correlator.merger.LcovMerger(localhost, lcov='lcov', gcov='gcov')

Bases: object

Merge coverage using lcov --capture + lcov --add-tracefile.

Works with any GCC version. Each host’s gcda directory is captured into a .info file, then all .info files are merged.

The lcov and gcov constructor arguments serve as defaults. Individual capture() calls can override them via the toolchain parameter to support per-host toolchains.

async capture(gcda_dir, gcno_dir, output, toolchain=None)

Run lcov --capture on a single host’s gcda directory.

Parameters:
  • gcda_dir – Directory containing .gcda files (fetched from remote).

  • gcno_dir – Directory containing .gcno files (from the build).

  • output – Path for the output .info file.

  • toolchain – Per-host toolchain override. When provided, its lcov_bin and gcov_bin are used instead of the instance defaults.

Return type:

Path

Returns:

The output path on success.

Raises:

RuntimeError – If lcov --capture fails.

async merge_info_files(info_files, output, toolchain=None)

Merge pre-captured .info files using lcov --add-tracefile.

Parameters:
  • info_files – List of .info files to merge.

  • output – Path for the merged output .info file.

  • toolchain – Optional toolchain override for the lcov binary.

Return type:

Path

Returns:

The output path on success.

Raises:

RuntimeError – If merging fails.

async capture_and_merge(host_gcda_dirs, gcno_dir, work_dir, toolchains=None, gcno_dirs=None)

Capture each host dir to .info, then merge all.

Parameters:
  • host_gcda_dirs – Per-host directories containing .gcda files.

  • gcno_dir – Directory containing .gcno files (from the build). Used as the fallback for all hosts when gcno_dirs is not provided.

  • work_dir – Scratch directory for intermediate .info files.

  • toolchains – Per-host toolchains, parallel to host_gcda_dirs. Each entry can be None to use instance defaults. If the entire list is None, defaults are used for all.

  • gcno_dirs – Per-host .gcno directories, parallel to host_gcda_dirs. When provided, host i is captured against gcno_dirs[i] instead of the shared gcno_dir fallback. Must have the same length as host_gcda_dirs.

Return type:

Path

Returns:

Path to the merged .info file.

Raises:

ValueError – If gcno_dirs is provided but its length differs from host_gcda_dirs.