Sonarcloud Code coverage

I starting to pull my hair because I can’t get the coverage to work. I have read documents on sonarcloud, visual studio, forums both here and stack, asked chatgpt etc etc. I can generate a testfile as you will see in my workflow. Here is the workflow

name: SonarCloud
on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened]
jobs:
  build:
    name: Build and analyze
    runs-on: windows-latest
    steps:
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: 17
          distribution: 'zulu'

      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Cache SonarCloud packages
        uses: actions/cache@v3
        with:
          path: ~\sonar\cache
          key: ${{ runner.os }}-sonar
          restore-keys: ${{ runner.os }}-sonar
    
      - name: Cache SonarCloud scanner
        id: cache-sonar-scanner
        uses: actions/cache@v3
        with:
          path: .\.sonar\scanner
          key: ${{ runner.os }}-sonar-scanner
          restore-keys: ${{ runner.os }}-sonar-scanner
    
      - name: Install SonarCloud scanner
        if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
        shell: powershell
        run: |
          New-Item -Path .\.sonar\scanner -ItemType Directory
          dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
     
      - name: Install dotnet-coverage tool
        run: dotnet tool install --global dotnet-coverage
     
      - name: Build and analyze
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        shell: powershell
        run: |
          .\.sonar\scanner\dotnet-sonarscanner begin /k:"lind-exe_Statify" /o:"lind-exe" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.dotcover.reportsPaths="coverage.xml"
  
          dotnet build --no-incremental
          dotnet test # Run your tests
          
          # Collect coverage using dotnet-coverage in Cobertura and output to coverage.xml
          dotnet-coverage collect "dotnet test" -f cobertura -o "coverage.xml"
          
          # Display content of coverage.xml for debugging
          Get-Content -Path "coverage.xml"
    
          .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"

When I build and do the debugg part to se the file I find it

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:    12, Skipped:     0, Total:    12, Duration: 57 ms - Statify.Tests.dll (net7.0)
Code coverage results: coverage.xml.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<coverage line-rate="0.3877551020408163" branch-rate="0.10576923076923077" complexity="420" version="1.9" timestamp="1701428294" lines-covered="266" lines-valid="686" branches-covered="11" branches-valid="104">

Then when I want Sonarcloud to upload it it cannot read the test report, It seems it want a JaCoCo?

INFO: Sensor JaCoCo XML Report Importer [jacoco]

3455INFO: ‘sonar.coverage.jacoco.xmlReportPaths’ is not defined. Using default locations: target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml,build/reports/jacoco/test/jacocoTestReport.xml

3456INFO: No report imported, no coverage information will be imported by JaCoCo XML Report Importer

And my coveragereport get this:

WARN: Could not import coverage report ‘.\coverage.xml’ because ‘Only dotCover HTML reports which start with “” are supported.’. Troubleshooting guide: [Coverage] Troubleshooting guide for .NET code coverage import

I have only worked with workflows for 3 weeks beside building a project. I’m studying a .NET course and have only studied for 1 year and 3 months. So this is kinda confusing. How do I get the code cevarge to display on Sonarcloud.

Hi,

Per the docs it looks like you need to tweak your coverage command slightly to

dotnet test --collect "Code Coverage"

The resulting coverage will be read automatically, with no need (for analysis purposes, anyway) to do this:

 
HTH,
Ann

1 Like
INFO: Aggregating the HTML reports from 'D:\a\Statify\Statify\.\Statify.Tests\TestResults\39c0e905-353c-4cc9-ad8c-729302d6c55a\48d846f1-0e60-422d-859e-729084be50d8.coverage'.
WARN: Could not import coverage report '.\Statify.Tests\TestResults\39c0e905-353c-4cc9-ad8c-729302d6c55a\48d846f1-0e60-422d-859e-729084be50d8.coverage' because 'Only dotCover HTML reports which start with "<!DOCTYPE html>" are supported.'. Troubleshooting guide: https://community.sonarsource.com/t/37151
INFO: Sensor C# Tests Coverage Report Import [csharp] (done) | time=362ms

Then I get this instead in my workflow.

Hi,

Could you share your updated workflow?

 
Thx,
Ann

Here is the full workflow now. I find the file but it doesn’t get pushed to my Sonarcloud. Everything else is shown.

name: SonarCloud
on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened]
jobs:
  build:
    name: Build and analyze
    runs-on: windows-latest
    steps:
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: 17
          distribution: 'zulu'

      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Cache SonarCloud packages
        uses: actions/cache@v3
        with:
          path: ~\sonar\cache
          key: ${{ runner.os }}-sonar
          restore-keys: ${{ runner.os }}-sonar
    
      - name: Cache SonarCloud scanner
        id: cache-sonar-scanner
        uses: actions/cache@v3
        with:
          path: .\.sonar\scanner
          key: ${{ runner.os }}-sonar-scanner
          restore-keys: ${{ runner.os }}-sonar-scanner
    
      - name: Install SonarCloud scanner
        if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
        shell: powershell
        run: |
          New-Item -Path .\.sonar\scanner -ItemType Directory
          dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
     
      - name: Install dotnet-coverage tool
        run: dotnet tool install --global dotnet-coverage
     
      - name: Build and analyze
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        shell: powershell
        run: |
          $coverageFilePath = $env:COVERAGE_FILE_PATH
          
          .\.sonar\scanner\dotnet-sonarscanner begin /k:"lind-exe_Statify" /o:"lind-exe" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.dotcover.reportsPaths="**/*.coverage"
          dotnet build --no-incremental
          dotnet test --collect "Code Coverage"
    
          # Attempt to find the coverage.cobertura.xml file
          $coverageFile = Get-ChildItem -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -Recurse -Filter "coverage.cobertura.xml" | Select-Object -ExpandProperty FullName

          if ($coverageFile) {
          # Display content of coverage.cobertura.xml for debugging
          Get-Content -Path $coverageFile

          .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
          } else {
          Write-Host "coverage.cobertura.xml not found."
          exit 1
          }

Hi,

This part looks right to me:

This part I don’t understand why you would make running the end step conditional on finding an unrelated file:

I also don’t understand

 
Ann

I was trying to find the file and specify where sonarcloud should look for it.
Now I have just this in the last step

- name: Build and analyze
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        shell: powershell
        run: |
          $coverageFilePath = $env:COVERAGE_FILE_PATH
          
          .\.sonar\scanner\dotnet-sonarscanner begin /k:"lind-exe_Statify" /o:"lind-exe" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" 
          dotnet test --collect "Code Coverage"
          .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"

And it gives me the analys on all thing (ANALYSIS SUCCESSFUL, you can find the results at: SonarCloud) but not the coverage part. Here is what I can find in my workflow. The dotnet coverage tool is Cobertura right? But why does it look for a JaCoco file? Therefore I tried to give the it a specific path.

INFO: Sensor JaCoCo XML Report Importer [jacoco]

[147](https://github.com/lind-exe/Statify/actions/runs/7126020883/job/19403078370#step:8:148)INFO: 'sonar.coverage.jacoco.xmlReportPaths' is not defined. Using default locations: target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml,build/reports/jacoco/test/jacocoTestReport.xml

[148](https://github.com/lind-exe/Statify/actions/runs/7126020883/job/19403078370#step:8:149)INFO: No report imported, no coverage information will be imported by JaCoCo XML Report Importer

[149](https://github.com/lind-exe/Statify/actions/runs/7126020883/job/19403078370#step:8:150)INFO: Sensor JaCoCo XML Report Importer [jacoco] (done) | time=3ms

[150](https://github.com/lind-exe/Statify/actions/runs/7126020883/job/19403078370#step:8:151)INFO: Sensor IaC CloudFormation Sensor [iac]

Hi,

All the sensors run in every analysis. Each one basically pokes its head up to see if there’s any work for it to do, and if not, then just turns back off. That’s why you see log lines about JaCoCo. You can safely ignore them.

The options are detailed here in the docs, with a concise listing across languages here. Cobertura is only supported for Flex and Python.

Can you provide a fresh, debug analysis log?

This guide will help you find them.

 
Ann

1 Like
name: SonarCloud
on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened]
jobs:
  build:
    name: Build and analyze
    runs-on: windows-latest
    steps:
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: 17
          distribution: 'zulu'

      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Cache SonarCloud packages
        uses: actions/cache@v3
        with:
          path: ~\sonar\cache
          key: ${{ runner.os }}-sonar
          restore-keys: ${{ runner.os }}-sonar
    
      - name: Cache SonarCloud scanner
        id: cache-sonar-scanner
        uses: actions/cache@v3
        with:
          path: .\.sonar\scanner
          key: ${{ runner.os }}-sonar-scanner
          restore-keys: ${{ runner.os }}-sonar-scanner
    
      - name: Install SonarCloud scanner
        if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
        shell: powershell
        run: |
          New-Item -Path .\.sonar\scanner -ItemType Directory
          dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
     
      - name: Install dotnet-coverage tool
        run: dotnet tool install --global dotnet-coverage
     
      - name: Build and analyze
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        shell: powershell
        run: |
        
          .\.sonar\scanner\dotnet-sonarscanner begin /k:"lind-exe_Statify" /o:"lind-exe" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml
          dotnet build --no-incremental
          dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml" 
          dotnet test --collect "Code Coverage"
          .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"

Now it works :slight_smile: I followed the guides again. Thanks for pointing me to the right direction.

3 Likes