Modules/businessdev.ALbuild.Apps/Private/Write-BcCoverageReports.ps1
|
function Write-CoberturaXml { <# .SYNOPSIS Writes Cobertura XML from ALbuild coverage objects (consumed by Azure DevOps / ReportGenerator). Branch coverage is omitted (BC does not provide branch data). #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Writes a report file at an explicit caller-provided path; no destructive ambiguity.')] [CmdletBinding()] param( [Parameter(Mandatory)] [object[]] $Objects, [Parameter(Mandatory)] [object] $Summary, [string] $SourceRoot, [Parameter(Mandatory)] [string] $Path ) $ic = [System.Globalization.CultureInfo]::InvariantCulture $rate = ([math]::Round($Summary.lineCoverage / 100, 4)).ToString($ic) $doc = New-Object System.Xml.XmlDocument $doc.AppendChild($doc.CreateXmlDeclaration('1.0', 'UTF-8', $null)) | Out-Null $cov = $doc.CreateElement('coverage') $cov.SetAttribute('line-rate', $rate) $cov.SetAttribute('branch-rate', '0') $cov.SetAttribute('lines-covered', [string]$Summary.coveredLines) $cov.SetAttribute('lines-valid', [string]$Summary.totalExecutableLines) $cov.SetAttribute('branches-covered', '0') $cov.SetAttribute('branches-valid', '0') $cov.SetAttribute('complexity', '0') $cov.SetAttribute('version', 'ALbuild') $cov.SetAttribute('timestamp', [string][DateTimeOffset]::UtcNow.ToUnixTimeSeconds()) $doc.AppendChild($cov) | Out-Null $sources = $doc.CreateElement('sources') if ($SourceRoot) { $s = $doc.CreateElement('source'); $s.InnerText = $SourceRoot; $sources.AppendChild($s) | Out-Null } $cov.AppendChild($sources) | Out-Null $packages = $doc.CreateElement('packages'); $cov.AppendChild($packages) | Out-Null $package = $doc.CreateElement('package') $package.SetAttribute('name', 'ALbuild') $package.SetAttribute('line-rate', $rate) $package.SetAttribute('branch-rate', '0') $package.SetAttribute('complexity', '0') $packages.AppendChild($package) | Out-Null $classes = $doc.CreateElement('classes'); $package.AppendChild($classes) | Out-Null foreach ($o in $Objects) { $class = $doc.CreateElement('class') $class.SetAttribute('name', ("{0} {1} {2}" -f $o.objectType, $o.objectId, $o.objectName).Trim()) $class.SetAttribute('filename', $(if ($o.filePath) { $o.filePath } else { "$($o.objectType)$($o.objectId)" })) $class.SetAttribute('line-rate', ([math]::Round($o.lineCoverage / 100, 4)).ToString($ic)) $class.SetAttribute('branch-rate', '0') $class.SetAttribute('complexity', '0') $class.AppendChild($doc.CreateElement('methods')) | Out-Null $lines = $doc.CreateElement('lines') foreach ($l in $o.lines) { $line = $doc.CreateElement('line') $line.SetAttribute('number', [string]$l.lineNo) $line.SetAttribute('hits', [string]$l.hits) $line.SetAttribute('branch', 'false') $lines.AppendChild($line) | Out-Null } $class.AppendChild($lines) | Out-Null $classes.AppendChild($class) | Out-Null } $doc.Save($Path) } function Write-CoverageMarkdown { <# .SYNOPSIS Writes a Markdown coverage summary (Azure DevOps / GitHub step summary). #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Writes a report file at an explicit caller-provided path; no destructive ambiguity.')] [CmdletBinding()] param( [Parameter(Mandatory)] [object[]] $Objects, [Parameter(Mandatory)] [object] $Summary, [Parameter(Mandatory)] [string] $Path ) $ic = [System.Globalization.CultureInfo]::InvariantCulture $sb = [System.Text.StringBuilder]::new() [void]$sb.AppendLine('# ALbuild Code Coverage Summary').AppendLine() [void]$sb.AppendLine([string]::Format($ic, '**Overall line coverage: {0}% ({1} / {2} lines)** across {3} object(s).', $Summary.lineCoverage, $Summary.coveredLines, $Summary.totalExecutableLines, $Summary.objectCount)).AppendLine() $worst = @($Objects | Sort-Object lineCoverage | Select-Object -First 10) if ($worst.Count -gt 0) { [void]$sb.AppendLine('## Lowest-covered objects').AppendLine() [void]$sb.AppendLine('| Object | File | Coverage |').AppendLine('|---|---|---:|') foreach ($o in $worst) { $obj = ("{0} {1} {2}" -f $o.objectType, $o.objectId, $o.objectName).Trim() $file = if ($o.filePath) { $o.filePath } else { '-' } [void]$sb.AppendLine([string]::Format($ic, '| {0} | {1} | {2}% |', $obj, $file, $o.lineCoverage)) } } Set-Content -LiteralPath $Path -Value $sb.ToString() -Encoding UTF8 } |