mark
mark

Reputation: 62774

How to publish Cobertura coverage report from the VS coverage produced by dotnet test --collect "Code coverage"?

So the process can be deduced from https://learn.microsoft.com/en-us/azure/devops/pipelines/ecosystems/dotnet-core?view=azure-devops#collect-code-coverage and contains of the following steps:

  1. Run the tests with coverage without publishing the test results, because we need to control the location of the generated binary coverage report (see How to set a custom coverage result file path when running dotnet test --collect "Code coverage"?)
  2. Convert the VS binary coverage result to XML
  3. Install the reportgenerator tool
  4. Convert the VS XML coverage to Cobertura using the reportgenerator tool
  5. Publish the Cobertura report using the Publish Code Coverage Azure DevOps task
  6. Publish the Test Results

These steps can be implemented with the following YAML template:

parameters:
  BuildConfiguration: Debug

steps:
- task: DotNetCoreCLI@2
  name: Test
  displayName: Test
  inputs:
    command: 'test'
    publishTestResults: false
    arguments: '-c ${{ parameters.BuildConfiguration }} --no-build -l trx -r $(Common.TestResultsDirectory)\tests --collect "Code coverage"'

- task: PublishTestResults@2
  displayName: Publish Test Results
  inputs:
    testResultsFormat: VSTest
    testResultsFiles: '*.trx'
    searchFolder: $(Common.TestResultsDirectory)\tests
    testRunTitle: $(Build.DefinitionName)-$(Build.BuildNumber)
  condition: succeededOrFailed()

- powershell: |
    $cc = "$(Get-ToolFolderFromNuGet Microsoft.CodeCoverage)\..\build\netstandard1.0\CodeCoverage\CodeCoverage.exe"
    $BinaryCoverageFile = (Get-Item "$(Common.TestResultsDirectory)\tests\*\*.coverage").FullName
    & $cc analyze /output:$(Common.TestResultsDirectory)\vstest-coverage.xml $BinaryCoverageFile
  displayName: Convert Coverage Result To Xml

- task: DotNetCoreCLI@2
  inputs:
    command: custom
    custom: tool
    arguments: install --tool-path . dotnet-reportgenerator-globaltool
  displayName: Install ReportGenerator tool

- script: .\reportgenerator.exe -reports:$(Common.TestResultsDirectory)\vstest-coverage.xml -targetdir:$(Common.TestResultsDirectory)\coverage\report -reporttypes:"Cobertura"
  displayName: Create Cobertura Coverage Report

- task: PublishCodeCoverageResults@1
  displayName: Publish Coverage Results
  inputs:
    codeCoverageTool: 'Cobertura'
    summaryFileLocation: '$(Common.TestResultsDirectory)/coverage/report/Cobertura.xml'
    failIfCoverageEmpty: true

It uses my powershell function Get-ToolFolderFromNuGet to download a package from NuGet, but other than that no custom code is used.

Anyway, the problem is that the Publish Test Results task publishes the binary coverage result as a hyperlink in the same tab where the coverage results are supposed to be:

enter image description here

This is useless. If I comment out the Publish Test Results, the coverage page shows the expected result:

enter image description here

But now I lost the Tests page, of course:

enter image description here

Does anyone know how to resolve this conundrum?

P.S.

I opened a bug here - https://github.com/microsoft/azure-pipelines-tasks/issues/12670

EDIT 1

I tried to Publish the Test Results at the end and even deleting the binary coverage result file. Nothing helps.

Upvotes: 3

Views: 5703

Answers (2)

Xavier John
Xavier John

Reputation: 9447

I solved the problem by deleting the code coverage files after reportgenerator.

Here are the steps

steps:
  - task: DotNetCoreCLI@2
    displayName: dotnet test
    inputs:
      command: 'test'
      projects: '**/*.Tests.csproj'
      arguments: '--no-build --no-restore --configuration $(BuildConfiguration) --collect "XPlat Code Coverage" --settings CodeCoverage.runsettings --logger trx --results-directory $(Build.SourcesDirectory)/out/TestResults/' 
      publishTestResults: false

  - script: |
        dotnet tool install -g dotnet-reportgenerator-globaltool
        reportgenerator -reports:$(Build.SourcesDirectory)/out/TestResults/**/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/out/CoverageResults "-reporttypes:HtmlInline_AzurePipelines;Cobertura"
    displayName: Create code coverage report

  - task: DeleteFiles@1
    displayName: 'Delete coverage files'
    inputs:
      sourceFolder: $(Build.SourcesDirectory)/out/TestResults
      contents: | 
        \**\*.coverage
        
  - task: PublishTestResults@2
    displayName: 'Publish test results'
    inputs:
      testResultsFormat: VSTest
      testResultsFiles: '$(Build.SourcesDirectory)\out\TestResults\**\*.trx'
      failTaskOnFailedTests: true

  # Publish the combined code coverage to the pipeline
  - task: PublishCodeCoverageResults@1
    displayName: 'Publish code coverage report'
    inputs:
      codeCoverageTool: 'Cobertura'
      summaryFileLocation: '$(Build.SourcesDirectory)/out/CoverageResults/Cobertura.xml'
      reportDirectory: '$(Build.SourcesDirectory)/out/CoverageResults'

Upvotes: 1

mark
mark

Reputation: 62774

By inspecting the diag logs for the Publish Test Results task I managed to figure out how to resolve it. Indeed, from the logs:

2020-04-10T15:48:12.1799774Z ##[debug]pattern: '*.trx'
2020-04-10T15:48:12.1839752Z ##[debug]findPath: 'd:\_wf\06\13\TestResults\tests'
2020-04-10T15:48:12.1840795Z ##[debug]statOnly: 'false'
2020-04-10T15:48:12.1843783Z ##[debug]findPath: 'd:\_wf\06\13\TestResults\tests'
2020-04-10T15:48:12.1844681Z ##[debug]findOptions.allowBrokenSymbolicLinks: 'true'
2020-04-10T15:48:12.1845119Z ##[debug]findOptions.followSpecifiedSymbolicLink: 'true'
2020-04-10T15:48:12.1845244Z ##[debug]findOptions.followSymbolicLinks: 'true'
2020-04-10T15:48:12.1850467Z ##[debug]  d:\_wf\06\13\TestResults\tests (directory)
2020-04-10T15:48:12.1858807Z ##[debug]  d:\_wf\06\13\TestResults\tests\1f627391-dd45-48b3-af92-5213c471eb04 (directory)
2020-04-10T15:48:12.1864156Z ##[debug]  d:\_wf\06\13\TestResults\tests\tfsbuild_TDC5DFC1BLD24_2020-04-10_11_47_29 (directory)
2020-04-10T15:48:12.1868508Z ##[debug]  d:\_wf\06\13\TestResults\tests\tfsbuild_TDC5DFC1BLD24_2020-04-10_11_47_29\In (directory)
2020-04-10T15:48:12.1873069Z ##[debug]  d:\_wf\06\13\TestResults\tests\tfsbuild_TDC5DFC1BLD24_2020-04-10_11_47_29\In\TDC5DFC1BLD24 (directory)
2020-04-10T15:48:12.1880777Z ##[debug]  d:\_wf\06\13\TestResults\tests\tfsbuild_TDC5DFC1BLD24_2020-04-10_11_47_29\In\TDC5DFC1BLD24\tfsbuild_TDC5DFC1BLD24_2020-04-10.11_47_23.coverage (file)
2020-04-10T15:48:12.1884045Z ##[debug]  d:\_wf\06\13\TestResults\tests\tfsbuild_TDC5DFC1BLD24_2020-04-10_11_47_29.trx (file)
2020-04-10T15:48:12.1885576Z ##[debug]7 results

So the binary coverage file is found there. But its original location is different, we can see it from the regular build log of the test task itself:

2020-04-10T15:47:54.0912521Z M i c r o s o f t   ( R )   C o v e r a g e   C o l l e c t i o n   T o o l   V e r s i o n   1 6 . 0 . 3 0 3 1 9 . 3 0 0 2 
2020-04-10T15:47:54.0913009Z  
2020-04-10T15:47:54.0913107Z  
2020-04-10T15:47:54.0913209Z  C o p y r i g h t   ( c )   M i c r o s o f t   C o r p o r a t i o n .     A l l   r i g h t s   r e s e r v e d . 
2020-04-10T15:47:54.0913270Z  
2020-04-10T15:47:54.0913307Z  
2020-04-10T15:47:54.0913340Z  
2020-04-10T15:47:54.0913400Z  
2020-04-10T15:47:54.3182888Z  Results File: d:\_wf\06\13\TestResults\tests\tfsbuild_TDC5DFC1BLD24_2020-04-10_11_47_29.trx
2020-04-10T15:47:54.3183717Z 
2020-04-10T15:47:54.3184201Z Attachments:
2020-04-10T15:47:54.3184419Z   d:\_wf\06\13\TestResults\tests\1f627391-dd45-48b3-af92-5213c471eb04\tfsbuild_TDC5DFC1BLD24_2020-04-10.11_47_23.coverage
2020-04-10T15:47:54.3196805Z Test Run Successful.
2020-04-10T15:47:54.3197219Z Total tests: 445
2020-04-10T15:47:54.3197713Z      Passed: 445
2020-04-10T15:47:54.3199584Z  Total time: 30.1160 Seconds

So, the original binary coverage file is in d:\_wf\06\13\TestResults\tests\1f627391-dd45-48b3-af92-5213c471eb04\tfsbuild_TDC5DFC1BLD24_2020-04-10.11_47_23.coverage, but it was copied over to d:\_wf\06\13\TestResults\tests\tfsbuild_TDC5DFC1BLD24_2020-04-10_11_47_29\In\TDC5DFC1BLD24\tfsbuild_TDC5DFC1BLD24_2020-04-10.11_47_23.coverage

My mistake was to only delete the original location. When deleted in both places, the Code Coverage page became as expected - no more useless hyperlink, yes to the actual coverage.

The corrected powershell task in the YAML looks like this:

- powershell: |
    $cc = "$(Get-NuGetPackageBaseFolder Microsoft.CodeCoverage)\build\netstandard1.0\CodeCoverage\CodeCoverage.exe"
    $BinaryCoverageFile = Get-Item "$(Common.TestResultsDirectory)\tests\*\*.coverage"
    & $cc analyze /output:$(Common.TestResultsDirectory)\vstest-coverage.xml $BinaryCoverageFile.FullName
    Remove-Item (Get-ChildItem -Path "$(Common.TestResultsDirectory)\tests" -Recurse -Filter $BinaryCoverageFile.Name).FullName
  displayName: Convert Coverage Result To Xml

And of course, the Publish Test Results task must be moved after this task.

Upvotes: 1

Related Questions