Tests/Analyze-Coverage.ps1
|
# Analyze Pester Code Coverage (Command-based, not line-based) param( [string]$CoverageFile = "Coverage.xml" ) if (-not (Test-Path $CoverageFile)) { Write-Error "Coverage file not found: $CoverageFile" exit 1 } Write-Host "`n========================================" -ForegroundColor Cyan Write-Host "Pester Code Coverage Analysis (Commands)" -ForegroundColor Cyan Write-Host "========================================`n" -ForegroundColor Cyan [xml]$coverage = Get-Content $CoverageFile # Calculate overall metrics by summing all files $sourceFiles = $coverage.report.package.sourcefile $totalCoveredCommands = 0 $totalMissedCommands = 0 foreach ($file in $sourceFiles) { foreach ($line in $file.line) { $totalCoveredCommands += [int]$line.ci $totalMissedCommands += [int]$line.mi } } $totalCommands = $totalCoveredCommands + $totalMissedCommands $coveragePercent = if ($totalCommands -gt 0) { [math]::Round(($totalCoveredCommands / $totalCommands) * 100, 2) } else { 0 } Write-Host "Overall Coverage (Pester Commands)" -ForegroundColor Yellow Write-Host "===================================" -ForegroundColor Yellow Write-Host "Total Commands: " -NoNewline; Write-Host $totalCommands -ForegroundColor White Write-Host "Covered Commands: " -NoNewline; Write-Host $totalCoveredCommands -ForegroundColor Green Write-Host "Missed Commands: " -NoNewline; Write-Host $totalMissedCommands -ForegroundColor Red Write-Host "Coverage Percent: " -NoNewline $color = if ($coveragePercent -ge 75) { 'Green' } elseif ($coveragePercent -ge 60) { 'Yellow' } else { 'Red' } Write-Host "$coveragePercent%" -ForegroundColor $color # Analyze per-file coverage Write-Host "`n========================================" -ForegroundColor Cyan Write-Host "Per-File Command Coverage" -ForegroundColor Cyan Write-Host "========================================`n" -ForegroundColor Cyan $fileStats = @() foreach ($file in $sourceFiles) { $fileName = $file.name # Count covered and missed instructions per line $coveredCommands = 0 $missedCommands = 0 foreach ($line in $file.line) { $covered = [int]$line.ci $missed = [int]$line.mi $coveredCommands += $covered $missedCommands += $missed } $totalCommands = $coveredCommands + $missedCommands $filePercent = if ($totalCommands -gt 0) { [math]::Round(($coveredCommands / $totalCommands) * 100, 2) } else { 0 } $fileStats += [PSCustomObject]@{ File = $fileName TotalCommands = $totalCommands CoveredCommands = $coveredCommands MissedCommands = $missedCommands CoveragePercent = $filePercent } } # Sort by coverage percentage (lowest first) $fileStats = $fileStats | Sort-Object CoveragePercent foreach ($stat in $fileStats) { $color = if ($stat.CoveragePercent -ge 80) { 'Green' } elseif ($stat.CoveragePercent -ge 60) { 'Yellow' } else { 'Red' } Write-Host "$($stat.File.PadRight(45)) " -NoNewline Write-Host "$($stat.CoveragePercent.ToString().PadLeft(6))% " -NoNewline -ForegroundColor $color Write-Host "($($stat.CoveredCommands.ToString().PadLeft(4))/$($stat.TotalCommands.ToString().PadLeft(4)) commands)" -ForegroundColor Gray } # Method-level analysis Write-Host "`n========================================" -ForegroundColor Cyan Write-Host "Method/Function Coverage" -ForegroundColor Cyan Write-Host "========================================`n" -ForegroundColor Cyan $classes = $coverage.report.package.class $methodStats = @() foreach ($class in $classes) { $sourceFile = $class.sourcefilename foreach ($method in $class.method) { $methodName = $method.name $methodLine = $method.line # Get method counters $methodInstructions = $method.counter | Where-Object { $_.type -eq 'INSTRUCTION' } if ($methodInstructions) { $methodCovered = [int]$methodInstructions.covered $methodMissed = [int]$methodInstructions.missed $methodTotal = $methodCovered + $methodMissed $methodPercent = if ($methodTotal -gt 0) { [math]::Round(($methodCovered / $methodTotal) * 100, 2) } else { 0 } $methodStats += [PSCustomObject]@{ File = $sourceFile Method = $methodName Line = $methodLine CoveragePercent = $methodPercent CoveredCommands = $methodCovered TotalCommands = $methodTotal } } } } # Show methods with low coverage $lowCoverageMethods = $methodStats | Where-Object { $_.CoveragePercent -lt 70 } | Sort-Object CoveragePercent if ($lowCoverageMethods.Count -gt 0) { Write-Host "Methods with < 70% Coverage:" -ForegroundColor Yellow foreach ($method in $lowCoverageMethods | Select-Object -First 20) { $color = if ($method.CoveragePercent -ge 50) { 'Yellow' } else { 'Red' } Write-Host " $($method.File):$($method.Line) " -NoNewline -ForegroundColor Gray Write-Host "$($method.Method) " -NoNewline -ForegroundColor White Write-Host "($($method.CoveragePercent)%)" -ForegroundColor $color } if ($lowCoverageMethods.Count -gt 20) { Write-Host " ... and $($lowCoverageMethods.Count - 20) more" -ForegroundColor Gray } } else { Write-Host "All methods have good coverage (>= 70%)!" -ForegroundColor Green } # Summary and recommendations Write-Host "`n========================================" -ForegroundColor Cyan Write-Host "Coverage Improvement Recommendations" -ForegroundColor Cyan Write-Host "========================================`n" -ForegroundColor Cyan $lowCoverageFiles = $fileStats | Where-Object { $_.CoveragePercent -lt 70 } if ($lowCoverageFiles.Count -gt 0) { Write-Host "Files needing attention (< 70% command coverage):" -ForegroundColor Yellow foreach ($file in $lowCoverageFiles) { $commandsNeeded = [math]::Ceiling($file.TotalCommands * 0.70) - $file.CoveredCommands Write-Host " $($file.File):" -ForegroundColor Red Write-Host " Current: $($file.CoveragePercent)% ($($file.CoveredCommands)/$($file.TotalCommands) commands)" -ForegroundColor Gray Write-Host " To reach 70%: Need $commandsNeeded more commands covered" -ForegroundColor Gray } } else { Write-Host "All files have good coverage (>= 70%)!" -ForegroundColor Green } Write-Host "`nNote: Pester measures 'commands' (executable statements) not 'lines'" -ForegroundColor Cyan Write-Host "One line of code may contain multiple commands or no commands at all." -ForegroundColor Cyan Write-Host "" |