Union code coverage across all six pipelines#3716
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a “Total Code Coverage” aggregation mechanism to provide a more accurate combined (union) coverage number across DAB’s six independent Azure DevOps pipelines by downloading each pipeline’s Cobertura output and merging them into a single report.
Changes:
- Introduces
scripts/merge-coverage.ps1, a dependency-free Cobertura union merger that can also emit a merged Cobertura XML for publishing. - Updates the unit-test pipeline to publish its coverage as a pipeline artifact and adds a new best-effort job that downloads latest DB-pipeline coverage and publishes a combined coverage report.
- Extends the shared build/unit-test template to stage and publish the unit coverage artifact used by the aggregation job.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| scripts/merge-coverage.ps1 | Adds a PowerShell Cobertura union-merger (line + branch) and optional merged XML emitter. |
| .pipelines/unittest-pipelines.yml | Converts the unit pipeline to multi-job and adds a best-effort “Total Code Coverage” aggregation job. |
| .pipelines/templates/build-pipelines.yml | Publishes unit Cobertura files as a pipeline artifact (coverage-unit) for later aggregation. |
…ad by name for aggregation
…or, warn on absent sources, map pipeline IDs
…r-edge branch union with floor fallback; guard 0/0 and de-dup inputs
…eport below either bound
| # Publish the RAW cobertura as a named pipeline artifact so the unit pipeline's | ||
| # 'Total Code Coverage' job can download and union it. (PublishCodeCoverageResults@1 | ||
| # only exposes an HTML report artifact, not the raw XML, so aggregation needs this.) | ||
| - task: CopyFiles@2 | ||
| displayName: 'Stage raw coverage for aggregation' | ||
| inputs: | ||
| sourceFolder: '$(Agent.TempDirectory)' | ||
| contents: '**/*.cobertura.xml' | ||
| targetFolder: '$(Build.ArtifactStagingDirectory)/coverage' | ||
| - task: PublishPipelineArtifact@1 | ||
| displayName: 'Publish coverage artifact for aggregation' | ||
| inputs: | ||
| targetPath: '$(Build.ArtifactStagingDirectory)/coverage' | ||
| artifact: 'coverage-cosmos' |
There was a problem hiding this comment.
could this be moved to a separate template file? seems these are duplicated in all the pipeline yamls.
| - task: DownloadPipelineArtifact@2 | ||
| displayName: 'Download MsSql coverage (latest)' | ||
| continueOnError: true | ||
| inputs: | ||
| source: 'specific' | ||
| project: '$(System.TeamProject)' | ||
| pipeline: '2' | ||
| runVersion: 'latest' | ||
| patterns: '**/*.cobertura.xml' | ||
| path: '$(Pipeline.Workspace)/db/mssql' | ||
| - task: DownloadPipelineArtifact@2 | ||
| displayName: 'Download PostgreSql coverage (latest)' | ||
| continueOnError: true | ||
| inputs: | ||
| source: 'specific' | ||
| project: '$(System.TeamProject)' | ||
| pipeline: '4' | ||
| runVersion: 'latest' | ||
| patterns: '**/*.cobertura.xml' | ||
| path: '$(Pipeline.Workspace)/db/pg' | ||
| - task: DownloadPipelineArtifact@2 | ||
| displayName: 'Download MySql coverage (latest)' | ||
| continueOnError: true | ||
| inputs: | ||
| source: 'specific' | ||
| project: '$(System.TeamProject)' | ||
| pipeline: '3' | ||
| runVersion: 'latest' | ||
| patterns: '**/*.cobertura.xml' | ||
| path: '$(Pipeline.Workspace)/db/mysql' | ||
| - task: DownloadPipelineArtifact@2 | ||
| displayName: 'Download DwSql coverage (latest)' | ||
| continueOnError: true | ||
| inputs: | ||
| source: 'specific' | ||
| project: '$(System.TeamProject)' | ||
| pipeline: '1' | ||
| runVersion: 'latest' | ||
| patterns: '**/*.cobertura.xml' | ||
| path: '$(Pipeline.Workspace)/db/dwsql' | ||
| - task: DownloadPipelineArtifact@2 | ||
| displayName: 'Download CosmosDb coverage (latest)' | ||
| continueOnError: true | ||
| inputs: | ||
| source: 'specific' | ||
| project: '$(System.TeamProject)' | ||
| pipeline: '6' | ||
| runVersion: 'latest' | ||
| patterns: '**/*.cobertura.xml' | ||
| path: '$(Pipeline.Workspace)/db/cosmos' |
There was a problem hiding this comment.
DownloadPipelineArtifact with no artifact: pulls every artifact of the run.. means all of the referenced run's artifacts are considered. The patterns: '**/.cobertura.xml' filter limits which files are downloaded, but this can still be slower/heavier than targeting the specifically-named coverage- artifacts you now publish. Since you control the artifact names, consider filtering to them to reduce download volume
Why make this change?
Closes #3715
What is this change?
pipeline (
unittest-pipelines.yml) that surfaces the true combined coverageacross all six test pipelines, instead of the misleadingly low per-pipeline
numbers.
PostgreSql, MySql, DwSql, CosmosDb). Each publishes its own coverage tab and
nothing merges them, so every pipeline in isolation reads much lower than the union.
buildjob publishes this run's unit cobertura as acoverage-unitpipeline artifact.
total_code_coveragejob (dependsOn: build) downloads that plus thelatest available cobertura from the five DB pipelines
(
DownloadPipelineArtifact@2, definition IDs DwSql=1, MsSql=2, MySql=3,PgSql=4, Cosmos=6).
scripts/merge-coverage.ps1unions all reports and emits one combinedCobertura.xml, whichPublishCodeCoverageResults@2publishes.with
runVersion: latest, so it may reflect an older commit ADO offers noprimitive to block on all six sibling runs from inside one of them. A true
per-PR guarantee would require consolidating all suites into one pipeline with
a
dependsOn-all aggregation job. This is not necessary for tracking code coverage.continueOnErroron the job and every step, so it can neverfail or block unit CI. If aggregation fails, the build job's own unit-only
coverage remains as the tab fallback.
so points union correctly across Windows/Linux agents, and excludes
*.Testsassemblies.
How was this tested?
Validated
scripts/merge-coverage.ps1locally against real cobertura from all sixpipelines (combined = 76.77% branch / 83.33% line) and against synthetic reports
covering cross-agent path union, branch-condition max-merge, test-assembly
exclusion, and well-formed Cobertura output. Pipeline changes are CI-only.
Sample Request(s)