Provide class level coverage data via OpenCover module (#3088)

This commit is contained in:
James Truher [MSFT] 2017-02-06 23:04:45 -08:00 committed by Jason Shirk
parent 29818062e8
commit 5871e1ac4b
3 changed files with 183 additions and 11 deletions

View file

@ -0,0 +1,125 @@
<Configuration>
<ViewDefinitions>
<View>
<Name>CoverageDataTable</Name>
<ViewSelectedBy>
<TypeName>OpenCover.CoverageData</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>CoverageLogFile</Label></TableColumnHeader>
<TableColumnHeader><Label>CoverageSummary</Label></TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem><PropertyName>CoverageLogFile</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>CoverageSummary</PropertyName></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>ClassCoverageDeltaTable</Name>
<ViewSelectedBy>
<TypeName>ClassCoverageDelta</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>ClassName</Label></TableColumnHeader>
<TableColumnHeader><Label>Sequence</Label><Alignment>Right</Alignment></TableColumnHeader>
<TableColumnHeader><Label>SequenceDelta</Label><Alignment>Right</Alignment></TableColumnHeader>
<TableColumnHeader><Label>Branch</Label><Alignment>Right</Alignment></TableColumnHeader>
<TableColumnHeader><Label>BranchDelta</Label><Alignment>Right</Alignment></TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem><PropertyName>ClassName</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Sequence</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>SequenceDelta</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Branch</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>BranchDelta</PropertyName></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>AssemblyCoverageChangeTable</Name>
<ViewSelectedBy>
<TypeName>OpenCover.AssemblyCoverageChange</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>AssemblyName</Label></TableColumnHeader>
<TableColumnHeader><Label>Sequence</Label><Alignment>Right</Alignment></TableColumnHeader>
<TableColumnHeader><Label>SequenceDelta</Label><Alignment>Right</Alignment></TableColumnHeader>
<TableColumnHeader><Label>Branch</Label><Alignment>Right</Alignment></TableColumnHeader>
<TableColumnHeader><Label>BranchDelta</Label><Alignment>Right</Alignment></TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem><PropertyName>AssemblyName</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Sequence</PropertyName></TableColumnItem>
<TableColumnItem><FormatString>{0:N}</FormatString><PropertyName>SequenceDelta</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Branch</PropertyName></TableColumnItem>
<TableColumnItem><FormatString>{0:N}</FormatString><PropertyName>BranchDelta</PropertyName></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>AssemblyCoverageDataTable</Name>
<ViewSelectedBy>
<TypeName>OpenCover.AssemblyCoverageData</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>AssemblyName</Label></TableColumnHeader>
<TableColumnHeader><Label>Sequence</Label><Alignment>Right</Alignment></TableColumnHeader>
<TableColumnHeader><Label>Branch</Label><Alignment>Right</Alignment></TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem><PropertyName>AssemblyName</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Sequence</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Branch</PropertyName></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>CoverageChangeTable</Name>
<ViewSelectedBy>
<TypeName>OpenCover.CoverageChange</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader><Label>Sequence</Label></TableColumnHeader>
<TableColumnHeader><Label>SequenceDelta</Label></TableColumnHeader>
<TableColumnHeader><Label>Branch</Label></TableColumnHeader>
<TableColumnHeader><Label>BranchDelta</Label></TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem><PropertyName>Sequence</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>SequenceDelta</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>Branch</PropertyName></TableColumnItem>
<TableColumnItem><PropertyName>BranchDelta</PropertyName></TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>

View file

@ -7,6 +7,7 @@ CompanyName = 'Microsoft Corporation'
Copyright = '(c) Microsoft Corporation. All rights reserved.'
Description = 'Module to install OpenCover and run Powershell tests to collect code coverage'
DotNetFrameworkVersion = 4.5
FormatsToProcess = @('OpenCover.Format.ps1xml')
FunctionsToExport = @('Get-CodeCoverage','Compare-CodeCoverage', 'Install-OpenCover', 'Invoke-OpenCover')
CmdletsToExport = @()
VariablesToExport = @()

View file

@ -8,11 +8,13 @@ if ((Get-Command -Name 'git' -ErrorAction Ignore) -ne $Null) {
function Get-AssemblyCoverageData([xml.xmlelement] $element)
{
$coverageSummary = (Get-CoverageSummary -element $element.Summary)
$classCoverage = Get-ClassCoverageData $element
$AssemblyCoverageData = [PSCustomObject] @{
AssemblyName = $element.ModuleName
CoverageSummary = $coverageSummary
Branch = $coverageSummary.BranchCoverage
Sequence = $coverageSummary.SequenceCoverage
ClassCoverage = $classCoverage
}
$AssemblyCoverageData | Add-Member -MemberType ScriptMethod -Name ToString -Value { "{0} ({1})" -f $this.AssemblyName,$this.CoverageSummary.BranchCoverage } -Force
@ -21,11 +23,45 @@ function Get-AssemblyCoverageData([xml.xmlelement] $element)
return $AssemblyCoverageData
}
function Get-CodeCoverageChange($r1, $r2)
function Get-ClassCoverageData([xml.xmlelement]$element)
{
$classes = [system.collections.arraylist]::new()
foreach ( $class in $element.classes.class )
{
# skip classes with names like <>f__AnonymousType6`4
if ( $class.fullname -match "<>" ) { continue }
$name = $class.fullname
$branch = $class.summary.branchcoverage
$sequence = $class.summary.sequenceCoverage
$o = [pscustomobject]@{ ClassName = $name; Branch = $branch; Sequence = $sequence}
$o.psobject.TypeNames.Insert(0, "ClassCoverageData")
$null = $classes.Add($o)
}
return $classes
}
function Get-CodeCoverageChange($r1, $r2, [string[]]$ClassName)
{
$h = @{}
$Deltas = new-object "System.Collections.ArrayList"
if ( $ClassName ) {
foreach ( $Class in $ClassName ) {
$c1 = $r1.Assembly.ClassCoverage | ?{$_.ClassName -eq $Class }
$c2 = $r2.Assembly.ClassCoverage | ?{$_.ClassName -eq $Class }
$ClassCoverageChange = [pscustomobject]@{
ClassName = $Class
Branch = $c2.Branch
BranchDelta = $c2.Branch - $c1.Branch
Sequence = $c2.Sequence
SequenceDelta = $c2.Sequence - $c1.sequence
}
$ClassCoverageChange.psobject.typenames.insert(0,"ClassCoverageDelta")
Write-Output $ClassCoverageChange
}
return
}
$r1.assembly | ForEach-Object { $h[$_.assemblyname] = @($_) }
$r2.assembly | ForEach-Object {
if($h.ContainsKey($_.assemblyname))
@ -42,16 +78,16 @@ function Get-CodeCoverageChange($r1, $r2)
{
$runs = @($h[$kvPair.Name])
$assemblyCoverageChange = (Get-AssemblyCoverageChange -r1 $runs[0] -r2 $runs[1])
$Deltas.Add($assemblyCoverageChange) | Out-Null
$null = $Deltas.Add($assemblyCoverageChange)
}
$CoverageChange = [PSCustomObject] @{
Run1 = $r1
Run2 = $r2
Branch = $r2.Summary.BranchCoverage
Sequence = $r2.Summary.SequenceCoverage
BranchDelta = [double] ($r2.Summary.BranchCoverage - $r1.Summary.BranchCoverage)
SequenceDelta = [double] ($r2.Summary.SequenceCoverage - $r1.Summary.SequenceCoverage)
Branch = $r2.CoverageSummary.BranchCoverage
Sequence = $r2.CoverageSummary.SequenceCoverage
BranchDelta = [double] ($r2.CoverageSummary.BranchCoverage - $r1.CoverageSummary.BranchCoverage)
SequenceDelta = [double] ($r2.CoverageSummary.SequenceCoverage - $r1.CoverageSummary.SequenceCoverage)
Deltas = $Deltas
}
$CoverageChange.PSTypeNames.Insert(0,"OpenCover.CoverageChange")
@ -80,7 +116,6 @@ function Get-AssemblyCoverageChange($r1, $r2)
SequenceDelta = $r2.Sequence - $r1.Sequence
}
$AssemblyCoverageChange.PSTypeNames.Insert(0,"OpenCover.AssemblyCoverageChange")
return $AssemblyCoverageChange
}
@ -96,11 +131,12 @@ function Get-CoverageData($xmlPath)
}
$CoverageData = [PSCustomObject] @{
CoverageLogFile = $xmlPath
CoverageSummary = (Get-CoverageSummary -element $CoverageXml.CoverageSession.Summary)
Assembly = $assemblies
}
$CoverageData.PSTypeNames.Insert(0,"OpenCover.CoverageData")
Add-Member -InputObject $CoverageData -MemberType ScriptMethod -Name GetClassCoverage -Value { param ( $name ) $this.assembly.classcoverage | ?{$_.classname -match $name } }
$null = $CoverageXml
## Adding explicit garbage collection as the $CoverageXml object tends to be very large, in order of 1 GB.
@ -292,7 +328,9 @@ function Compare-CodeCoverage
[Parameter(Mandatory=$true,Position=0,ParameterSetName="file")][string]$RunFile1,
[Parameter(Mandatory=$true,Position=1,ParameterSetName="file")][string]$RunFile2,
[Parameter(Mandatory=$true,Position=0,ParameterSetName="coverage")][Object]$Run1,
[Parameter(Mandatory=$true,Position=1,ParameterSetName="coverage")][Object]$Run2
[Parameter(Mandatory=$true,Position=1,ParameterSetName="coverage")][Object]$Run2,
[Parameter()][String[]]$ClassName,
[Parameter()][switch]$Summary
)
if ( $PSCmdlet.ParameterSetName -eq "file" )
@ -304,7 +342,15 @@ function Compare-CodeCoverage
$Run2 = (Get-CoverageData -xmlPath $xmlPath2)
}
(Get-CodeCoverageChange -r1 $Run1 -r2 $Run2)
$change = Get-CodeCoverageChange -r1 $Run1 -r2 $Run2 -Class $ClassName
if ( $Summary -or $ClassName )
{
$change
}
else
{
$change.Deltas
}
}
<#
@ -473,4 +519,4 @@ function Invoke-OpenCover
Remove-Item "$env:temp\unelevated.ps1" -force -ErrorAction SilentlyContinue
}
}
}
}