feat(ci_visibility): coverage itr backfill#18978
Conversation
…' into gnufede/coverage-itr-fill
Parse meta.coverage from skippable tests API response, accumulate per-test bitmaps into a session-level aggregate, merge with skipped tests' coverage at session end, and set test.code_coverage.backfilled tag on session, module, and suite events when backfilling is applied. Also fix pre-existing mypy errors in test_api_client.py (wrong import source for ITRSkippingLevel) and test_session_manager.py (untyped dict and Mock attribute access on Protocol-typed api_client). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Expose CoverageLines via ddtrace.testing.internal.tracer_api.coverage (using the explicit `as CoverageLines` re-export idiom for mypy) so that all consumers within ddtrace/testing/internal/ import through the intended interface rather than bypassing it with the deep internal path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When running with pytest-xdist, each worker accumulates its own _session_coverage dict. Add infrastructure to merge all workers' coverage into the main process before the session event is written, so the ITR backfill merge in pytest_sessionfinish operates on the complete aggregated coverage. Changes: - Worker pytest_sessionfinish serializes _session_coverage as dict[str, bytes] into workeroutput["dd_session_coverage"] when backfilling is active (bytes, not bytearray, for execnet compat) - XdistTestOptPlugin gains _workers_session_coverage dict and merges worker bitmaps incrementally in pytest_testnodedown - New pytest_sessionfinish(tryfirst=True) on XdistTestOptPlugin transfers accumulated worker coverage to the main plugin's _session_coverage before TestOptPlugin.pytest_sessionfinish runs - Tests: TestXdistITRCoverageBackfill with 3 subprocess-based cases - Fix pre-existing mypy errors in test_pytest_xdist.py Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove dead merge code in pytest_sessionfinish: the library's role is only to set the backfilled tag; the backend computes the merged percentage from per-test bitmaps already uploaded via put_coverage - Add test_get_skippable_tests_missing_line_coverage_flag: asserts has_missing=True when any item carries _missing_line_code_coverage - Add test_get_skippable_tests_malformed_coverage_bitmap: verifies graceful skip of bad base64 entries with warning log, while valid entries in the same response are still parsed - Refactor MockCIVisibilityServerWithITR to extend MockCIVisibilityServer rather than duplicating ~100 lines; _ITRMockCIVisibilityHandler extends _MockCIVisibilityHandler and overrides only the two differing paths Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Circular import analysis
|
Codeowners resolved as |
|
BenchmarksBenchmark execution time: 2026-07-10 11:27:52 Comparing candidate commit e16737a in PR branch Found 0 performance improvements and 7 performance regressions! Performance is the same for 613 metrics, 10 unstable metrics. scenario:iastaspects-add_aspect
scenario:iastaspects-casefold_noaspect
scenario:iastaspects-strip_aspect
scenario:iastaspects-upper_noaspect
scenario:iastaspectsospath-ospathbasename_aspect
scenario:iastaspectssplit-rsplit_aspect
scenario:span-start
|
E2E Test Report: SUCCESS ✅Tested by: Shepherd Agent (autonomous QA for Datadog Test Optimization) Test Environment
ResultsPython — flask playground
Java — jolokia playground (dd-trace-java v1.64.0, for comparison)
Issues Found During Testing
Behavioral Comparison: Python vs Java
Test Methodology
This E2E test was performed by Shepherd — autonomous QA agent for Datadog Test Optimization |
Summary
Implements ITR coverage backfilling so that
test.code_coverage.lines_pctis accurate even whenITR skips tests. Without this, skipped tests' covered lines are missing from the tally, producing
artificially low coverage percentages.
What it does:
meta.coverage(base64 bitmaps) and_missing_line_code_coveragefrom the/api/v2/ci/tests/skippableAPI responsemeta.coverageand recomputes
lines_pctfrom the merged resulttest.code_coverage.backfilled = "true"on session, module, and suite events wheneverITR skipping is enabled
_missing_line_code_coverageare excluded fromskippable_items(they run freshto collect live coverage), matching Java tracer behaviour
xdist support: Workers propagate their
_session_coveragebitmaps viaworkeroutputto themain process, which merges all workers' coverage before computing the final percentage.
Key design decisions
_missing_line_code_coverageis per-test, not per-session. Flagged tests are not skipped(excluded from
skippable_items), so their coverage is always collected. This matches Java andavoids losing coverage for those tests entirely.
meta.coverageisempty or tests were actually skipped. This tells the backend to trust
lines_pct.byte-aligned bitmap length) to avoid counting intra-byte padding as phantom lines.
CoverageLinesre-exported fromddtrace.testing.internal.tracer_api.coverage— themodule's docstring already declares it as the coverage API boundary for
ddtrace.testing.Files changed
api_client.pymeta.coverage, exclude_missing_line_code_coverageitems from skippablecached_file_provider.pyTestOptDataProviderprotocol +CachedFileDataProvidersignaturessession_manager.pyitr_covered_filesfrom API responsetest_data.pyCODE_COVERAGE_BACKFILLEDtag constantplugin.pytracer_api/coverage.pyCoverageLinesTest plan
_missing_line_code_coverageexclusion, malformed base64 handlingenabled with coverage, missing flag, empty coverage, ITR disabled, no tests skipped)
🤖 Generated with Claude Code