-
-
Notifications
You must be signed in to change notification settings - Fork 473
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Cobertura coverage format #2298
Merged
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
932d9b4
wip: Implement Cobertura coverage format
joeskeen 36af738
fix unit test
joeskeen 14e36af
revert editor settings change
joeskeen 658b358
remove ?? operator
joeskeen 1f6525b
fix attribute ordering
joeskeen a2e5eb0
make coverage report test work on all platforms
joeskeen 61eefc1
Fix windows paths
joeskeen 6c3db56
fix unit test for Windows paths
joeskeen 3b6e06b
kick the build
joeskeen 14e8ef7
re-implement Cobertura coverage report generation
joeskeen ab0fa57
fix compatibility issues
joeskeen 70ed47a
fix tests
joeskeen 386cd3c
removing Cobertura from v4 parameter options
joeskeen f0ac943
fix compatibility with ReportGenerator
joeskeen a272120
Update src/functions/Coverage.ps1
joeskeen f9ed4a0
fix whitespace
joeskeen c20f260
Merge branch 'Cobertura' of github.com:joeskeen/Pester into Cobertura
joeskeen 6a1ddc3
fix output
joeskeen 9d78186
fix windows paths
joeskeen 0bd5244
order packages,classes,methods by name
joeskeen 719047a
change Cobertura DTD to loose
joeskeen 9116f06
Tune coverage report for performance
joeskeen 45bdc68
Merge remote-tracking branch 'upstream/main' into pr/joeskeen/2298
fflaten 13c13a7
Remove outdated condition
fflaten 505afef
Merge remote-tracking branch 'upstream/main' into pr/joeskeen/2298
fflaten a12b1c7
Add Cobertura DTD file
fflaten 13f0b75
Apply suggestions from code review
fflaten b1f7f2e
Fix typo and update JaCoCo starttime
fflaten c643f5e
Fix tests
fflaten 80f2120
Use epoch time for Cobertura and JaCoCo
fflaten 58590c9
Merge remote-tracking branch 'upstream/main' into pr/joeskeen/2298
fflaten 98b7881
Update test
fflaten File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -810,13 +810,12 @@ function Get-JaCoCoReportXml { | |
|
||
$isGutters = "CoverageGutters" -eq $Format | ||
|
||
if ($null -eq $CoverageReport -or ('None' -eq $pester.Show) -or $CoverageReport.NumberOfCommandsAnalyzed -eq 0) { | ||
if ($null -eq $CoverageReport -or $CoverageReport.NumberOfCommandsAnalyzed -eq 0) { | ||
return [string]::Empty | ||
} | ||
|
||
$now = & $SafeCommands['Get-Date'] | ||
$nineteenSeventy = & $SafeCommands['Get-Date'] -Date "01/01/1970" | ||
[long] $endTime = [math]::Floor((New-TimeSpan -start $nineteenSeventy -end $now).TotalMilliseconds) | ||
# Report uses unix epoch time format (milliseconds since midnight 1/1/1970 UTC) | ||
[long] $endTime = [System.DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() | ||
[long] $startTime = [math]::Floor($endTime - $TotalMilliseconds) | ||
|
||
$folderGroups = $CommandCoverage | & $SafeCommands["Group-Object"] -Property { | ||
|
@@ -1038,6 +1037,224 @@ function Get-JaCoCoReportXml { | |
return $xml | ||
} | ||
|
||
function Get-CoberturaReportXml { | ||
param ( | ||
[parameter(Mandatory = $true)] | ||
[object] $CoverageReport, | ||
[parameter(Mandatory = $true)] | ||
[long] $TotalMilliseconds | ||
) | ||
|
||
if ($null -eq $CoverageReport -or $CoverageReport.NumberOfCommandsAnalyzed -eq 0) { | ||
return [string]::Empty | ||
} | ||
|
||
# Report uses unix epoch time format (milliseconds since midnight 1/1/1970 UTC) | ||
[long] $endTime = [System.DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() | ||
[long] $startTime = [math]::Floor($endTime - $TotalMilliseconds) | ||
Comment on lines
+1052
to
+1054
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Source for epoch time format: https://github.com/cobertura/cobertura/blob/0ff963284cecaace30f409a977dccb07c41a5a8f/cobertura/src/main/java/net/sourceforge/cobertura/reporting/xml/XMLReport.java#L89 Backport version will need to use: $nineteenSeventy = & $SafeCommands['New-Object'] 'System.DateTime' -ArgumentList @(1970, 1, 1, 0, 0, 0, [System.DateTimeKind]::Utc)
$now = [DateTime]::Now.ToUniversalTime()
[long] $endTime = [math]::Floor(($now - $nineteenSeventy).TotalMilliseconds)
[long] $startTime = [math]::Floor($endTime - $TotalMilliseconds) |
||
|
||
$commonRoot = Get-CommonParentPath -Path $CoverageReport.AnalyzedFiles | ||
|
||
$allLines = [System.Collections.Generic.List[object]]@() | ||
$allLines.AddRange($CoverageReport.MissedCommands) | ||
$allLines.AddRange($CoverageReport.HitCommands) | ||
$packages = @{} | ||
foreach ($command in $allLines) { | ||
$package = & $SafeCommands["Split-Path"] $command.File -Parent | ||
if (!$packages[$package]) { | ||
$packages[$package] = @{ | ||
Classes = @{} | ||
} | ||
} | ||
|
||
$class = $command.File | ||
if (!$packages[$package].Classes[$class]) { | ||
$packages[$package].Classes[$class] = @{ | ||
Methods = @{} | ||
Lines = @{} | ||
} | ||
} | ||
|
||
if (!$packages[$package].Classes[$class].Lines[$command.Line]) { | ||
$packages[$package].Classes[$class].Lines[$command.Line] = [ordered]@{ number = $command.Line ; hits = 0 } | ||
} | ||
$packages[$package].Classes[$class].Lines[$command.Line].hits += $command.HitCount | ||
|
||
$method = $command.Function | ||
if (!$method) { | ||
continue | ||
} | ||
|
||
if (!$packages[$package].Classes[$class].Methods[$method]) { | ||
$packages[$package].Classes[$class].Methods[$method] = @{} | ||
} | ||
|
||
if (!$packages[$package].Classes[$class].Methods[$method][$command.Line]) { | ||
$packages[$package].Classes[$class].Methods[$method][$command.Line] = [ordered]@{ number = $command.Line ; hits = 0 } | ||
} | ||
$packages[$package].Classes[$class].Methods[$method][$command.Line].hits += $command.HitCount | ||
} | ||
|
||
$packages = foreach ($packageGroup in $packages.GetEnumerator()) { | ||
$classGroups = $packageGroup.Value.Classes | ||
$classes = foreach ($classGroup in $classGroups.GetEnumerator()) { | ||
$methodGroups = $classGroup.Value.Methods | ||
$methods = foreach ($methodGroup in $methodGroups.GetEnumerator()) { | ||
$lines = ([object[]]$methodGroup.Value.Values) | New-LineNode | ||
$coveredLines = foreach ($line in $lines) { if (0 -lt $line.attributes.hits) { $line } } | ||
|
||
$method = [ordered]@{ | ||
name = 'method' | ||
attributes = [ordered]@{ | ||
name = $methodGroup.Name | ||
signature = '()' | ||
} | ||
children = [ordered]@{ | ||
lines = $lines | & $SafeCommands["Sort-Object"] { [int]$_.attributes.number } | ||
} | ||
totalLines = $lines.Length | ||
coveredLines = $coveredLines.Length | ||
} | ||
|
||
$method | ||
} | ||
|
||
$lines = ([object[]]$classGroup.Value.Lines.Values) | New-LineNode | ||
$coveredLines = foreach ($line in $lines) { if (0 -lt $line.attributes.hits) { $line } } | ||
|
||
$lineRate = Get-LineRate -CoveredLines $coveredLines.Length -TotalLines $lines.Length | ||
$filename = $classGroup.Name.Substring($commonRoot.Length).Replace('\', '/').TrimStart('/') | ||
|
||
$class = [ordered]@{ | ||
name = 'class' | ||
attributes = [ordered]@{ | ||
name = (& $SafeCommands["Split-Path"] $classGroup.Name -Leaf) | ||
filename = $filename | ||
'line-rate' = $lineRate | ||
'branch-rate' = 1 | ||
} | ||
children = [ordered]@{ | ||
methods = $methods | & $SafeCommands["Sort-Object"] { $_.attributes.name } | ||
lines = $lines | & $SafeCommands["Sort-Object"] { [int]$_.attributes.number } | ||
} | ||
totalLines = $lines.Length | ||
coveredLines = $coveredLines.Length | ||
} | ||
|
||
$class | ||
} | ||
|
||
$totalLines = ($classes.totalLines | & $SafeCommands["Measure-Object"] -Sum).Sum | ||
$coveredLines = ($classes.coveredLines | & $SafeCommands["Measure-Object"] -Sum).Sum | ||
$lineRate = Get-LineRate -CoveredLines $coveredLines -TotalLines $totalLines | ||
$packageName = $packageGroup.Name.Substring($commonRoot.Length).Replace('\', '/').TrimStart('/') | ||
|
||
$package = [ordered]@{ | ||
name = 'package' | ||
attributes = [ordered]@{ | ||
name = $packageName | ||
'line-rate' = $lineRate | ||
'branch-rate' = 0 | ||
} | ||
children = [ordered]@{ | ||
classes = $classes | & $SafeCommands["Sort-Object"] { $_.attributes.name } | ||
} | ||
totalLines = $totalLines | ||
coveredLines = $coveredLines | ||
} | ||
|
||
$package | ||
} | ||
|
||
$totalLines = ($packages.totalLines | & $SafeCommands["Measure-Object"] -Sum).Sum | ||
$coveredLines = ($packages.coveredLines | & $SafeCommands["Measure-Object"] -Sum).Sum | ||
$lineRate = Get-LineRate -CoveredLines $coveredLines -TotalLines $totalLines | ||
|
||
$coverage = [ordered]@{ | ||
name = 'coverage' | ||
attributes = [ordered]@{ | ||
'lines-valid' = $totalLines | ||
'lines-covered' = $coveredLines | ||
'line-rate' = $lineRate | ||
'branches-valid' = 0 | ||
'branches-covered' = 0 | ||
'branch-rate' = 1 | ||
timestamp = $startTime | ||
version = 0.1 | ||
} | ||
children = [ordered]@{ | ||
sources = [ordered]@{ | ||
name = 'source' | ||
value = $commonRoot.Replace('\', '/') | ||
} | ||
packages = $packages | & $SafeCommands["Sort-Object"] { $_.attributes.name } | ||
} | ||
} | ||
|
||
$xmlDeclaration = '<?xml version="1.0" ?>' | ||
fflaten marked this conversation as resolved.
Show resolved
Hide resolved
|
||
$docType = '<!DOCTYPE coverage SYSTEM "coverage-loose.dtd">' | ||
$coverageXml = ConvertTo-XmlElement -Node $coverage | ||
$document = "$xmlDeclaration`n$docType`n$($coverageXml.OuterXml)" | ||
|
||
$document | ||
} | ||
|
||
function New-LineNode { | ||
param( | ||
[parameter(Mandatory = $true, ValueFromPipeline = $true)] [object] $LineObject | ||
) | ||
|
||
process { | ||
[ordered]@{ | ||
name = 'line' | ||
attributes = $LineObject | ||
} | ||
} | ||
} | ||
|
||
function Get-LineRate { | ||
param( | ||
[parameter(Mandatory = $true)] [int] $CoveredLines, | ||
[parameter(Mandatory = $true)] [int] $TotalLines | ||
) | ||
|
||
[double]$denominator = if ($TotalLines) { $TotalLines } else { 1 } | ||
|
||
joeskeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
$CoveredLines / $denominator | ||
} | ||
|
||
function ConvertTo-XmlElement { | ||
param( | ||
[parameter(Mandatory = $true)] [object] $Node | ||
) | ||
|
||
$element = ([xml]"<$($Node.name)/>").DocumentElement | ||
if ($node.attributes) { | ||
$attributes = $node.attributes | ||
foreach ($attr in $attributes.GetEnumerator()) { | ||
$element.SetAttribute($attr.Name, $attr.Value) | ||
} | ||
} | ||
if ($node.children) { | ||
$children = $node.children | ||
foreach ($child in $children.GetEnumerator()) { | ||
$childElement = ([xml]"<$($child.Name)/>").DocumentElement | ||
foreach ($value in $child.Value) { | ||
$childXml = ConvertTo-XmlElement $value | ||
$importedChildXml = $childElement.OwnerDocument.ImportNode($childXml, $true) | ||
$null = $childElement.AppendChild($importedChildXml) | ||
} | ||
$importedChild = $element.OwnerDocument.ImportNode($childElement, $true) | ||
$null = $element.AppendChild($importedChild) | ||
} | ||
} | ||
if ($node.value) { | ||
$element.InnerText = $node.value | ||
} | ||
|
||
$element | ||
} | ||
|
||
function Add-XmlElement { | ||
param ( | ||
[parameter(Mandatory = $true)] [System.Xml.XmlNode] $Parent, | ||
|
@@ -1046,14 +1263,23 @@ function Add-XmlElement { | |
) | ||
$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 | ||
} | ||
Add-XmlAttribute -Element $element -Attributes $Attributes | ||
} | ||
return $element | ||
} | ||
|
||
function Add-XmlAttribute { | ||
param( | ||
[parameter(Mandatory = $true)] [System.Xml.XmlNode] $Element, | ||
[parameter(Mandatory = $true)] [System.Collections.IDictionary] $Attributes | ||
) | ||
|
||
foreach ($key in $Attributes.Keys) { | ||
$attribute = $Element.Attributes.Append($Element.OwnerDocument.CreateAttribute($key)) | ||
$attribute.Value = $Attributes.$key | ||
} | ||
} | ||
|
||
function Add-JaCoCoCounter { | ||
param ( | ||
[parameter(Mandatory = $true)] [ValidateSet('Instruction', 'Line', 'Method', 'Class')] [string] $Type, | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nohwnd I included this bugfix for JaCoCo as well.
[DateTime]"01/01/1970"
is not proper epoch origin (missing UTC kind), so off by one hour in my timezone.Source for epoch time format:
https://github.com/jacoco/jacoco/blob/77d2af5ec31faf04419d945e1a0c34da49f8a702/org.jacoco.core/src/org/jacoco/core/data/SessionInfo.java#L33-L38
Backport version will need to use: