Created
August 1, 2019 21:46
-
-
Save JPRuskin/92b6993ca627c5efb690982c0492bdb0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class fileregion { | |
hidden [string]$OriginalFilePath | |
[string]$RelativeFilePath | |
[int]$StartLine | |
[int]$EndLine | |
} | |
function Get-FileRegions { | |
param( | |
[string[]]$Path | |
) | |
begin { | |
$Results = @() | |
} | |
process { | |
foreach ($Path in $Path) { | |
$i = 1 | |
switch -File $Path -Regex { | |
"^# BEGIN (?<Path>.+)$" { | |
$Current = [fileregion]@{ | |
OriginalFilePath = $Path | |
RelativeFilePath = $Matches.Path | |
StartLine = ($i++) | |
} | |
} | |
"^# END (?<Path>.+)$" { | |
$Current.EndLine = ($i++) | |
$Results += $Current | |
} | |
default {$i++} | |
} | |
} | |
} | |
end { | |
$Results | |
} | |
} | |
function Add-XmlElement { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory)] | |
[System.Xml.XmlNode]$Parent, | |
[ValidateNotNullOrEmpty()] | |
[string]$Name = $( | |
$MyInvocation.InvocationName -replace '^Add-(.+)Element$','$1' | |
), | |
[System.Collections.DictionaryEntry]$Attributes | |
) | |
if ($Name -eq 'Xml') { | |
throw "Need to specify Element type" | |
} | |
$Element = $Parent.AppendChild($Parent.OwnerDocument.CreateElement($Name)) | |
if ($Attributes) { | |
foreach ($Key in $Attributes.Keys) { | |
$attribute = $Element.Attributes.Append($Parent.OwnerDocument.CreateAttribute($key)) | |
$attribute.Value = $Attributes.$Key | |
} | |
} | |
return $Element | |
} | |
function Add-JaCoCoCounter { | |
[Alias('Add-InstructionCounter', 'Add-LineCounter', 'Add-MethodCounter', 'Add-ClassCounter')] | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Piped')] | |
$InputObject, | |
[Parameter(Mandatory)] | |
[System.Xml.XmlNode]$Parent, | |
[ValidateNotNullOrEmpty()] | |
[ValidateSet('Instruction', 'Line', 'Method', 'Class')] | |
[string]$Type = $( | |
$MyInvocation.InvocationName -replace '^Add-(.+)Counter$','$1' | |
), | |
[Parameter(ParameterSetName = 'Specified')] | |
[int]$Missing, | |
[Parameter(ParameterSetName = 'Specified')] | |
[int]$Covered | |
) | |
process { | |
} | |
} | |
<# | |
TODO: | |
- Fix it so it removes the original nodes (unsure?), or allows you to create a new "CodeCoverage-Split.xml" file ✓ | |
- Rewrite Add-JoCoCoCounter again to be more forgiving / allow for pipelining / defaulting to 0 if no value is found | |
- | |
#> | |
function Update-JaCoCoCoverageFile { | |
<# | |
.Synopsis | |
A function to modify code coverage to point at source-files in a Module-Builder (or similar) compiled module | |
.Example | |
Update-JaCoCoCoverageFile -Path .\CodeCoverage.xml -CompiledPath $env:ArtifactStagingDirectory | |
Updates the coverage file in place, resolving paths to the original source based on the regions in the compiled psm1 | |
#> | |
[CmdletBinding()] | |
param( | |
# Path of the JaCoCo Code Coverage data | |
[string]$Path = "C:\Users\james.ruskin\VSO\Platform\QMODHelper\CodeCoverage.xml", | |
# Path to the compiled artifacts | |
[string[]]$CompiledPath = "C:\Users\james.ruskin\VSO\Platform\QMODHelper\output\", | |
# The path to save the updated Code Coverage data to. Defaults to the original file path. | |
[string]$OutputFile = $Path, | |
# Leaves original Code Coverage data in the file | |
[switch]$NoCleanup | |
) | |
begin { | |
# We're stealing Pester's XML tools that are used to generate this report in an interesting way | |
$CoverageTools = Join-Path (Get-Module Pester -ListAvailable -Verbose:$false)[0].ModuleBase "Functions/Coverage.ps1" | |
. $CoverageTools # Specifically Add-XmlElement and Add-JoCoCoCounter | |
$ConvertedCoverage = Get-FileRegions -Path (Get-ChildItem -Path $CompiledPath -Recurse -Filter *.psm1).FullName | |
function FindRegion ($Line) { | |
$Converter.Where({$_.StartLine -le $Line -and $_.EndLine -ge $Line}, "First") | |
} | |
} | |
process { | |
foreach ($Path in $Path) { | |
[xml]$CoverageFile = Get-Content -Path $Path | |
$reportElement = $CoverageFile.report[1..$($coverageFile.report.Count)] # DOCTYPE string injection takes 0? | |
foreach ($Package in $reportElement.package) { | |
Write-Verbose "Converting '$($Package.name)'" | |
$Converter = $ConvertedCoverage | ? OriginalFilePath -Match "$(Split-Path $Package.sourceFile.Name -Leaf)$" | |
$packageElement = Add-XmlElement $reportElement "package" @{ | |
name = "$($Package.Name)" | |
} | |
# Adding Classes (per source file) | |
foreach ($FileGroup in ($Package.class.method | Group-Object {(FindRegion $_.Line).RelativeFilePath}).Where{$_.Name}) { | |
Write-Verbose " Adding items from '$($FileGroup.Name)'" | |
$classElement = Add-XmlElement $packageElement 'class' -Attributes ([ordered] @{ | |
name = "$($FileGroup.Name -replace '^\.\\(.+)\.ps1$','$1' -replace '\\','.')" | |
sourcefilename = $FileGroup.Name | |
}) | |
foreach ($method in $FileGroup.Group) { | |
Write-Verbose " Adding '$($method.Name)'" | |
$methodElement = Add-XmlElement $classElement "method" -Attributes ([ordered] @{ | |
name = $method.Name | |
desc = " ($(Split-Path (Split-Path $FileGroup.Name -Parent) -Leaf))" | |
line = $_.Line - (FindRegion $FileGroup.Line).StartLine | |
}) | |
# Adding Method (Function) Counters | |
foreach ($counter in $method.counter) { | |
Add-JaCoCoCounter $counter.Type @{"$($counter.Type)" = @{missed = 0+$counter.Missed; covered = 0+$counter.Covered}} $methodElement | |
} | |
} | |
foreach ($counter in $FileGroup.group.counter.type | Sort -Unique) { | |
Add-JaCoCoCounter $Counter @{ | |
"$Counter" = @{ | |
missed = [int](0+($FileGroup.group.counter.Where{$_.Type -eq $Counter}.missed | Measure-Object -Sum).sum) | |
covered = [int](0+($FileGroup.group.counter.Where{$_.Type -eq $Counter}.covered | Measure-Object -Sum).sum) | |
} | |
} $classElement | |
} | |
Add-JaCoCoCounter Class @{ | |
Class = @{ | |
missed = 0+($FileGroup.Group.Where{$_.counter.missed -ne 0}.Count) | |
covered = 0+($FileGroup.Group.Where{$_.counter.covered -eq 0}.Count) | |
} | |
} $classElement | |
} | |
Write-Verbose "Adding Line Coverage" | |
foreach ($File in $Converter) { | |
Write-Verbose " Adding '$($File.RelativeFilePath)'" | |
$FileLines = $Package.sourcefile.line | Where {[int]($_.nr) -ge $File.StartLine -and [int]($_.nr) -le $File.EndLine} | |
$sourceFileElement = Add-XmlElement $packageElement 'sourcefile' -Attributes ([ordered] @{ | |
name = $File.RelativeFilePath | |
}) | |
foreach ($Line in $FileLines) { | |
$null = Add-XmlElement $sourceFileElement 'line' -Attributes ([ordered]@{ | |
nr = $Line.nr - $File.StartLine | |
mi = $Line.mi | |
ci = $Line.ci | |
}) | |
} | |
$FileCoverage = $packageElement.class.Where{$_.sourceFileName -eq $File.RelativeFilePath}.counter | |
@("Instruction","Line","Method","Class").ForEach{ | |
Add-JaCoCoCounter $_ @{$_ = @{ | |
missed=[int](0 + ($FileCoverage | Where Type -eq $_).Missed) | |
covered=[int](0 + ($FileCoverage | Where Type -eq $_).Covered) | |
}} $sourceFileElement | |
} | |
} | |
@("Instruction","Line","Method","Class").ForEach{ | |
Add-JaCoCoCounter $_ @{$_ = @{ | |
missed=0 | |
covered=0 | |
}} $packageElement | |
} | |
} | |
@("Instruction","Line","Method","Class").ForEach{ | |
Add-JaCoCoCounter $_ @{$_ = @{ | |
missed=0 | |
covered=0 | |
}} $reportElement | |
} | |
} | |
} | |
end { | |
Write-Verbose "Saving XML File to '$($OutputFile)'" | |
$CoverageFile.Save($OutputFile) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment