diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 1c59ba5..40401a2 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -25,10 +25,35 @@ jobs: run: dotnet restore - name: Build run: dotnet build --no-restore - - name: Test - run: dotnet test --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutput=TestResults\Coverage\coverage.xml /p:CoverletOutputFormat=cobertura - - name: Report Codecov + - name: Clean TestResults + run: rm -rf ./TestResults + - name: Test Unit Tests with coverage + run: dotnet test tests/Biak.ConsoleApp.UnitTests/Biak.ConsoleApp.UnitTests.csproj --no-build --verbosity normal --collect:"XPlat Code Coverage;Format=cobertura" --results-directory ./TestResults/Unit + - name: Normalize Unit coverage file + run: | + coverage_file="$(find ./TestResults/Unit -name coverage.cobertura.xml -print -quit)" + test -n "$coverage_file" || (echo "Unit coverage file not found under ./TestResults/Unit" && exit 1) + cp "$coverage_file" ./TestResults/unit.cobertura.xml + - name: Test Integration Tests with coverage + run: dotnet test tests/Biak.ConsoleApp.IntegrationTests/Biak.ConsoleApp.IntegrationTests.csproj --no-build --verbosity normal --collect:"XPlat Code Coverage;Format=cobertura" --results-directory ./TestResults/Integration + - name: Normalize Integration coverage file + run: | + coverage_file="$(find ./TestResults/Integration -name coverage.cobertura.xml -print -quit)" + test -n "$coverage_file" || (echo "Integration coverage file not found under ./TestResults/Integration" && exit 1) + cp "$coverage_file" ./TestResults/integration.cobertura.xml + - name: Upload Unit Test Coverage to Codecov uses: codecov/codecov-action@v5 with: - files: ./TestResults/**/coverage.cobertura.xml + files: ./TestResults/unit.cobertura.xml + disable_search: true + flags: unit-tests + name: unit-tests-coverage + verbose: true + - name: Upload Integration Test Coverage to Codecov + uses: codecov/codecov-action@v5 + with: + files: ./TestResults/integration.cobertura.xml + disable_search: true + flags: integration-tests + name: integration-tests-coverage verbose: true diff --git a/.gitignore b/.gitignore index 9491a2f..07fecb1 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,10 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file + +# Local .NET tools +.tools/ + +# Test coverage reports +CoverageReports/ +FodyWeavers.xsd diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..241f4c3 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,20 @@ +codecov: + require_ci_to_pass: true + +coverage: + status: + project: + default: + enabled: false + unit-tests: + flags: + - unit-tests + integration-tests: + flags: + - integration-tests + +flags: + unit-tests: + carryforward: false + integration-tests: + carryforward: false diff --git a/generate-coverage-reports.ps1 b/generate-coverage-reports.ps1 new file mode 100644 index 0000000..e1b2b6f --- /dev/null +++ b/generate-coverage-reports.ps1 @@ -0,0 +1,82 @@ +param( + [switch]$NoBuild +) + +$ErrorActionPreference = 'Stop' + +$repoRoot = $PSScriptRoot +$testResultsRoot = Join-Path $repoRoot 'TestResults' +$reportsRoot = Join-Path $repoRoot 'CoverageReports' +$toolsPath = Join-Path $repoRoot '.tools' +$reportGeneratorExe = Join-Path $toolsPath 'reportgenerator.exe' + +$unitProject = Join-Path $repoRoot 'tests/Biak.ConsoleApp.UnitTests/Biak.ConsoleApp.UnitTests.csproj' +$integrationProject = Join-Path $repoRoot 'tests/Biak.ConsoleApp.IntegrationTests/Biak.ConsoleApp.IntegrationTests.csproj' + +function Get-CoverageFilePath { + param( + [Parameter(Mandatory = $true)] + [string]$Directory + ) + + $coverageFile = Get-ChildItem -Path $Directory -Recurse -Filter 'coverage.cobertura.xml' | + Sort-Object LastWriteTime -Descending | + Select-Object -First 1 + + if (-not $coverageFile) { + throw "Coverage file not found under '$Directory'." + } + + return $coverageFile.FullName +} + +Push-Location $repoRoot +try { + Write-Host 'Cleaning previous coverage artifacts...' + Remove-Item -Path $testResultsRoot -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item -Path $reportsRoot -Recurse -Force -ErrorAction SilentlyContinue + + Write-Host 'Ensuring ReportGenerator tool is available...' + New-Item -ItemType Directory -Path $toolsPath -Force | Out-Null + if (Test-Path $reportGeneratorExe) { + dotnet tool update dotnet-reportgenerator-globaltool --tool-path $toolsPath | Out-Null + } + else { + dotnet tool install dotnet-reportgenerator-globaltool --tool-path $toolsPath | Out-Null + } + + if ($NoBuild) { + Write-Host 'Running unit tests with coverage (no build)...' + dotnet test $unitProject --no-build --collect:"XPlat Code Coverage;Format=cobertura" --results-directory "$testResultsRoot/Unit" + + Write-Host 'Running integration tests with coverage (no build)...' + dotnet test $integrationProject --no-build --collect:"XPlat Code Coverage;Format=cobertura" --results-directory "$testResultsRoot/Integration" + } + else { + Write-Host 'Running unit tests with coverage...' + dotnet test $unitProject --collect:"XPlat Code Coverage;Format=cobertura" --results-directory "$testResultsRoot/Unit" + + Write-Host 'Running integration tests with coverage...' + dotnet test $integrationProject --collect:"XPlat Code Coverage;Format=cobertura" --results-directory "$testResultsRoot/Integration" + } + + $unitCoverageFile = Get-CoverageFilePath -Directory (Join-Path $testResultsRoot 'Unit') + $integrationCoverageFile = Get-CoverageFilePath -Directory (Join-Path $testResultsRoot 'Integration') + + $unitReportDir = Join-Path $reportsRoot 'unit' + $integrationReportDir = Join-Path $reportsRoot 'integration' + + Write-Host 'Generating unit HTML coverage report...' + & $reportGeneratorExe "-reports:$unitCoverageFile" "-targetdir:$unitReportDir" '-reporttypes:Html' + + Write-Host 'Generating integration HTML coverage report...' + & $reportGeneratorExe "-reports:$integrationCoverageFile" "-targetdir:$integrationReportDir" '-reporttypes:Html' + + Write-Host '' + Write-Host 'Done.' + Write-Host "Unit report: $unitReportDir/index.html" + Write-Host "Integration report: $integrationReportDir/index.html" +} +finally { + Pop-Location +}