coverage.store.model¶
Coverage data model.
This is the core of the coverage module — everything else feeds into or reads from these types. The model is deliberately independent of gcov, lcov, or coverage.py internals.
Coverage tiers are names — free-form strings like "system",
"unit", "manual", or anything a user wires up on the command
line. LineHits and BranchHits store per-tier counts in dicts
keyed by tier name, so adding a new tier requires no changes to the
model. A CoverageStore also carries a tier_order list that
defines the precedence of tiers for presentation purposes (first =
highest precedence).
-
otto.coverage.store.model.TIER_SYSTEM =
'system'¶ Conventional tier name used by the merged .gcda pipeline.
Any string is a valid tier name; this constant spares callers a string literal when they mean the canonical system-coverage tier.
-
otto.coverage.store.model.STORE_FORMAT_VERSION =
3¶ store.jsonschema version, bumped on breaking on-disk changes.Version 2 is the first version to carry an explicit
"format"key — introduced alongside a run-table field/key rename that shortened every per-record and per-line name (JSON keys and the record class/attribute names alike) by dropping a now-redundant qualifying word. Version 3 renames the run table’s git-anchor field frompintobase_commit, matching the same rename on the capture artifact it is sourced from. There is no migration shim: a file that does not declare this exact version fails loud inCoverageStore.load()with a message telling the caller to regenerate it, rather than silently mis-reading renamed/reshaped keys under old names.
- class otto.coverage.store.model.LineHits(counts: dict[str, int] = <factory>)¶
Bases:
objectPer-tier hit counts for a single line.
Counts are a dict keyed by tier name. Absent keys mean zero hits for that tier.
- class otto.coverage.store.model.BranchHits(block: int, branch: int, hits: ~otto.coverage.store.model.LineHits = <factory>, reachable: dict[str, bool] = <factory>)¶
Bases:
objectPer-tier hit counts and reachability for one branch.
Reachability is tri-state per tier:
key present, value
True→ observed as reachable at least oncekey present, value
False→ observed only as unreachable (lcov-)key absent → no data yet for that tier
Once a tier sees the branch as reachable it stays reachable for that tier — merges only flip
False→True, never the other way.- property branch_id : tuple[int, int]¶
(block, branch)tuple that uniquely identifies this branch within a line.
- set_reachable(tier: str, reachable: bool) None¶
Record a reachability observation for tier.
If the tier was already marked reachable, keep it reachable. Otherwise adopt the new value.
-
is_reachable(tier: str | None =
None) bool | None¶ Reachability for tier, or combined across all tiers when None.
Returns
Noneif no data has been recorded for the requested tier (or for any tier whentieris None).
-
is_hit_for(tier: str | None =
None) bool¶ Return True if this branch was taken at least once in tier.
- merge(other: BranchHits) None¶
Merge other into this branch, accumulating hits and updating reachability.
- class otto.coverage.store.model.RunRecord(id: int, tier: str, label: str, board: str = '', labs: list[str] = <factory>, captured_at: str = '', tester: dict[str, str] | None = None, ticket: str | None = None, note: str | None = None, base_commit: str = '', dirty_remap: bool = False, aging: bool = False)¶
Bases:
objectOne coverage run in the report — a capture or a synthetic per-tier load.
The run table (
CoverageStore.runs) is derived fresh at report time from the capture inputs;idis the record’s index into that list.labelis what the drilldown chip shows: the host display name when the capture carries one, else the board (host id), else the tier name (synthetic runs pass neither).
- class otto.coverage.store.model.LineRecord(line_number: int, hits: ~otto.coverage.store.model.LineHits = <factory>, branches: list[~otto.coverage.store.model.BranchHits] = <factory>, state: str | None = None, run_hits: dict[int, int] = <factory>, stale_runs: list[int] = <factory>)¶
Bases:
objectCoverage data for a single source line.
- branches : list[BranchHits]¶
- merge(other: LineRecord) None¶
Merge other into this line record, accumulating hits and branch data.
- class otto.coverage.store.model.FileRecord(path: ~pathlib.Path, lines: dict[int, ~otto.coverage.store.model.LineRecord] = <factory>, excluded_lines: set[int] = <factory>)¶
Bases:
objectCoverage data for a single source file.
- lines : dict[int, LineRecord]¶
- get_or_create_line(line_number: int) LineRecord¶
Return the
LineRecordfor line_number, creating it if absent.
- merge(other: FileRecord) None¶
Merge other into this file record, combining per-line hits and branches.
-
line_coverage_pct(tier: str | None =
None) float¶ Line coverage percentage, optionally filtered by tier.
-
branch_coverage_pct(tier: str | None =
None, conservative: bool =True) float¶ Branch coverage percentage.
conservative=True (default): denominator is only branches where
is_reachable(tier)isTrue. Matches genhtml behaviour.conservative=False: denominator is all branches seen in any
.infofile.
- sorted_lines() Iterator[LineRecord]¶
Yield
LineRecordobjects in ascending line-number order.
-
class otto.coverage.store.model.CoverageStore(tier_order: list[str] | None =
None)¶ Bases:
objectCentral store for all coverage data across all files, hosts, and tiers.
tier_orderholds the user-defined precedence of tiers — first entry is highest precedence. Downstream renderers iterate this list to drive column ordering and the winner-take-all row coloring used by the annotated source view.runscaptures the run table — each run record corresponds to one coverage run or synthetic per-tier aggregate loaded into the store.- register_tier(tier: str) None¶
Append tier to the tier order if not already present.
Loaders call this when they see a new tier so the store’s
tier_orderlist stays in sync with the data without requiring every call site to pre-declare every tier.
-
add_run(*, tier: str, label: str | None =
None, board: str ='', labs: list[str] | None =None, captured_at: str ='', tester: dict[str, str] | None =None, ticket: str | None =None, note: str | None =None, base_commit: str ='', dirty_remap: bool =False) int¶ Register one run and return its run id (index into
runs).labelfalls back toboard, then totier— the synthetic per-tier runs pass neither. Also registers tier so the run table can never reference an unknown tier.
- get_or_create_file(path: Path) FileRecord¶
Return the
FileRecordfor path, creating it if absent.Resolves path to its canonical form when the file exists on disk, so duplicate entries from different relative or symlinked references to the same file are collapsed to one record.
- merge_file(record: FileRecord) None¶
Merge record into the store, accumulating data for an existing path or inserting it.
- files() Iterator[FileRecord]¶
Yield all
FileRecordobjects in sorted path order.
-
overall_branch_pct(tier: str | None =
None, conservative: bool =True) float¶ Overall branch coverage percentage across all files.
- classmethod load(path: Path) CoverageStore¶
Deserialise a store from JSON.
- Raises:¶
ValueError – The file’s
"format"key does not matchSTORE_FORMAT_VERSION(including files with no"format"key at all — everything predating this store-format version). There is no migration shim; the message tells the caller to regenerate the store.