.net for sonar with tests for mutliple projects in Azure pipelines

Hi,

I tested in different ways and I am still not sure the proper way of sonar analysis for mutliple projects with .NET. The problem is that sonar start analysis, creates cobertura files with sending to azure, but fails on an analysis steps with indexing some files from sources twice

  • ALM used: Azure DevOps
  • CI system used: Azure DevOps
  • scanner mode: dotnet
  • Languages of the repository: .NET

steps:

  • task: SonarCloudPrepare@4
    inputs:
    SonarQube: ‘***’
    organization: '***’
    scannerMode: ‘dotnet’
    projectKey: ‘***’
    projectName: '***’
    extraProperties: |
    sonar.scm.provider=\*\*\*
    sonar.cs.vstest.reportsPaths=$(System.DefaultWorkingDirectory)\\source\\TestResults\*.trx
    sonar.cs.opencover.reportsPaths=$(System.DefaultWorkingDirectory)\\source\\TestResults\*.opencover.xml
    sonar.verbose=true
    sonar.sources=$(System.DefaultWorkingDirectory)\\source
    sonar.exclusions=\*\*\*
    sonar.coverage.exclusions=\*\*\*
    
    

used: scannerMode: ‘dotnet’

  • task: PowerShell@2
    
    dotnet test $project.Path --configuration Debug --no-build --filter "TestCategory!=OnlineOnly" --collect:"XPlat Code Coverage" --results-directory "$projResults" --logger "trx;LogFileName=$logFileName" /p:CollectCoverage=true /p:CoverletOutput="$coverageOutput" /p:CoverletOutputFormat=opencover
    
    
  • task: SonarCloudAnalyze@4
    
    displayName: ‘Run Qube Cloud Analysis’
    
    inputs:
    
    jdkversion: ‘JAVA_HOME’
    
    continueOnError: true
    
    

trx and opencover files are in several dirs, that cause a problem with indexing by sonar?

If anybody had similar case?

Hi,

Welcome to the community!

Here’s your problem:

For all the other scanner flavors you can/should specify sonar.sources, but SonarScanner for .NET doesn’t play nicely with it, and the error you’re seeing is the result.

Remove that from the config and it should work.

 
HTH,
Ann

Hi, it sent the report, but didn’t send correctly results, as only some of the dirs and all of that were not covered.

Hi,

If I understand correctly, analysis now succeeds, but doesn’t include what you expect it to?

When you provided your pipeline configuration in the first post, was this a literal copy/paste or a redaction?

Because with this exact syntax I’m not sure how it would be read, but there’s a chance it would be seen as excluding everything (**/*).

You’ve already got sonar.verbose turned on. Can you provide the resulting analysis log?

 
Ann

I did a test to do not exclude and include any files to see results: configuration was like:

      extraProperties: |

        sonar.scm.provider=git
        sonar.cs.vstest.reportsPaths=$(System.DefaultWorkingDirectory)\source\TestResults\*.trx
        sonar.cs.opencover.reportsPaths=$(System.DefaultWorkingDirectory)\source\TestResults\*.opencover.xml
        sonar.verbose=true
    sonar.exclusions=**\*.xml,**\*.sth.cs
    sonar.coverage.exclusions=**\Test\**,**\path_test_app\**,**\path1\**,**\path2\**,**\path3\**,**\path4\**,**\path5\**,**\path6\**,**\path7\**,**\path8\**,**\path9\**,**\path11

I tested in opposite sign in path as / but returned paths in a way: C:\path\path/path

Hi,

 
Ann

as there are thousands of a lines I am not sure what to see (any errors appear, sometimes not found files i analyse step)

2026-02-02T13:13:04.6822904Z 14:13:04.681  The scanner engine has finished successfully
2026-02-02T13:13:04.6829584Z 14:13:04.681  Post-processing succeeded.
2026-02-02T13:13:04.6875552Z 
2026-02-02T13:13:04.7080208Z ##[debug]Process exited with code 0 and signal null for tool 'C:\azgent\_work\_tasks\SonarCloudPrepare_\4.0.2\classic-sonar-scanner-msbuild\SonarScanner.MSBuild.exe'
2026-02-02T13:13:04.7081437Z ##[debug]STDIO streams have closed and received exit code 0 and signal null for tool 'C:\agent\_work\_tasks\SonarCloudPrepare_number\4.0.2\classic-sonar-scanner-msbuild\SonarScanner.MSBuild.exe'

what is weird and I am not sure that under TestResults are created coverage.cobertura in a two dirs = thePojectDir/svc***/ln/name_of_host/cobertura.coverage.xml and randomnumber/cobertura.coverage.xml

Hi,

I’m not going to be able to proceed without logs.

 
Ann

Hi, I’m not sure what the problem is exactly and, as Ann said, without logs it’s very difficult to figure out anything.

That being said, you mention Cobertura here, and you should know that the .NET analyzer does not support the Cobertura format yet. This is in the works for Q2, but not available yet.

In the meantime, make sure you use one of the supported formats.

Denis

1 Like

I can’t provide the exact logs from reasons: there are thousands of lines, as testing last 4 hours and the second: internal company data.

As I saw configuration for other smaller projects with the one opencover file and single trx it wasn’t problem.

I am testing parrallel many projects in one repository under one source and I am not sure about syntax for similar case (in sonar website some directories are not send, but checking that one by line is impossible because of amount of data to check)

That’s why it would be easier to apply solution if any of you had a situation with generation many trx files and opencover during sonar scan in .NET repo

Unfortunately, without some indication of

  • what you are doing (your pipeline)
  • what actually happens (the logs)

it’s extremely difficult to infer what is wrong.
Would it be possible to share the redacted pipeline so we get an idea of how it is organized and how you invoke the scanner, the build, and the coverage?

Denis

that is my pipeline with changed data:

- task: SonarCloudPrepare@4

    inputs:

      SonarQube: 'sonar_sc'

      organization: 'org'

      scannerMode: 'dotnet'

      projectKey: 'project'

      projectName: 'project'

      extraProperties: |

        sonar.scm.provider=git

        sonar.cs.vstest.reportsPaths=$(System.DefaultWorkingDirectory)\source\TestResults\*.trx

        sonar.cs.opencover.reportsPaths=$(System.DefaultWorkingDirectory)\source\TestResults\*.opencover.xml

        sonar.verbose=true

        sonar.sources=$(System.DefaultWorkingDirectory)\source

        sonar.exclusions=**\*.xml,**\*.sth.cs

        sonar.coverage.exclusions=**\Test\**,**\path_test_app\**,**\path1\**,**\path2\**,**\path3\**,**\path4\**,**\path5\**,**\path6\**,**\path7\**,**\path8\**,**\path9\**,**\path11




  - task: VSBuild@1

    displayName: 'Build solution source/project.sln'

    condition: succeeded()

    inputs:

      solution: '$(solution)'

      vsVersion: 'latest'

      msbuildArgs: '/p:TreatWarningsAsErrors="true"'

      platform: '$(BuildPlatform)'

      configuration: '$(buildConfiguration)'

      clean: true

      msbuildArchitecture: 'x64'

      enableDefaultLoggers: true

      logProjectEvents: true




  - task: PowerShell@2

    displayName: 'Run .net 60 tests in Parallel'

    inputs:

      targetType: 'inline'

      script: |

        $sourceDir = "$env:BUILD_SOURCESDIRECTORY"

        Write-Host "Source directory: $sourceDir"




        $netForSonarProjects = @(

          @{ Path = "$sourceDir\source\Test\proj1.csproj";        Name = "name1" },

          @{ Path = "$sourceDir\source\Test\proj2.csproj";      Name = "name2" },

          @{ Path = "$sourceDir\source\Test\proj3.csproj"; Name = "name3" }

        )




        $resultsRoot = Join-Path $sourceDir "source\TestResults"

        foreach ($proj in $netForSonarProjects) {

          $projDir = Join-Path $resultsRoot $proj.Name

          if (-not (Test-Path $projDir)) { New-Item -ItemType Directory -Path $projDir | Out-Null }

        }




        # Run filtered tests

        $netJobs = foreach ($proj in $netForSonarProjects) {

          Start-Job -ScriptBlock {

            param($project, $sourcePath)

            $projResults = Join-Path (Join-Path $sourcePath "source\TestResults") $project.Name

            $logFileName = "$($project.Name).trx"

            $coverageOutput = Join-Path (Join-Path $sourcePath "source\TestResults") $project.Name




            dotnet test $project.Path --configuration Debug --no-build --filter "TestCategory!=OnlineOnly" --collect:"XPlat Code Coverage" --results-directory "$projResults" --logger "trx;LogFileName=$logFileName" /p:CollectCoverage=true /p:CoverletOutput="$coverageOutput" /p:CoverletOutputFormat=opencover

            if ($LASTEXITCODE -ne 0) {

                exit 1

            }

            exit 0

          } -ArgumentList $proj, $sourceDir

        }




        #some of code 

        

        foreach ($job in $allJobs) {

            $output = Receive-Job $job

            if ($output) {

                Write-Host "Output from job '$($job.Name)': $output"

            }

            Remove-Job $job

        }




        $items = Get-ChildItem -Filter "*.trx" -Recurse $resultsRoot | Select-Object -ExpandProperty FullName

        foreach ($item in $items) { Move-Item -Path $item -Destination $resultsRoot }




  - task: DotNetCoreCLI@2

    displayName: "Install tool dotnet-coverageconverter"

    inputs:

      command: 'custom'

      custom: 'tool'

      arguments: 'update --global dotnet-coverageconverter'

 

  - task: CmdLine@2

    displayName: Convert test coverage

    inputs:

      script: |

        dotnet-coverageconverter --CoverageFilesFolder "$(System.DefaultWorkingDirectory)\source\TestResults"




  - task: PublishCodeCoverageResults@2

    inputs:

     summaryFileLocation: '$(Build.SourcesDirectory)/source/TestResults/**/*coverage.cobertura.xml'




  - task: PublishTestResults@2

    displayName: 'Publish Test Results source/TestResults/*.trx'

    condition: always()

    inputs:

      testResultsFormat: 'VSTest'

      testResultsFiles: '**/TestResults/*.trx'

      searchFolder: '$(System.DefaultWorkingDirectory)'

      mergeTestResults: true

      publishRunAttachments: true




  - task: SonarCloudAnalyze@4

    displayName: 'Run Qube Cloud Analysis'

    inputs:

      jdkversion: 'JAVA_HOME'

    continueOnError: true




  - task: SonarCloudPublish@4

    inputs:

      pollingTimeoutSec: '300'

the structure of the repository:

ls -lar
source/
pull_r_template.md
Dir/
Pipelines/
NuGetConfiguration/
Build/
external/

$ ls -lar source/
Utilities/
TestApplications/
Test/
sth/
prj.snk
prjestSettings.runsettings
prj.sln
prj.prjx
proj1/
proj2/
proj3/

after set the branch in properties I received some results - sonar had problem with different than master, without removing sonar sources it don’t work, do I need to exclude dirs, to do not include inproper files?