Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion Functions/Coverage.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,34 @@ InModuleScope Pester {
$jaCoCoReportXml = $jaCoCoReportXml -replace 'start="[0-9]*"','start=""'
$jaCoCoReportXml = $jaCoCoReportXml -replace 'dump="[0-9]*"','dump=""'
$jaCoCoReportXml = $jaCoCoReportXml -replace "$([System.Environment]::NewLine)",''
$jaCoCoReportXml | should -be '<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.0//EN" "report.dtd"><report name="Pester (date)"><sessioninfo id="this" start="" dump="" /><counter type="INSTRUCTION" missed="1" covered="7" /><counter type="LINE" missed="1" covered="7" /><counter type="METHOD" missed="1" covered="4" /><counter type="CLASS" missed="0" covered="2" /></report>'
[String]$ReferenceReport = [String]'<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.0//EN" "report.dtd"><report name="Pester (date)"><sessioninfo id="this" start="" dump="" /><counter type="INSTRUCTION" missed="1" covered="7" /><counter type="LINE" missed="1" covered="7" /><counter type="METHOD" missed="1" covered="4" /><counter type="CLASS" missed="0" covered="2" /></report>'
$jaCoCoReportXml | should -Be $ReferenceReport
}
Exit-CoverageAnalysis -PesterState $testState
}

Context 'Entire file detailed coverage' {
$testState = New-PesterState -Path $root

# Path deliberately duplicated to make sure the code doesn't produce multiple breakpoints for the same commands
Enter-CoverageAnalysis -CodeCoverage "$root\TestScript.ps1", "$root\TestScript.ps1" -PesterState $testState

It 'Has the proper number of breakpoints defined' {
$testState.CommandCoverage.Count | Should -Be 7
}

$null = & "$root\TestScript.ps1"
$coverageReport = Get-CoverageReport -PesterState $testState

It 'JaCoCo report must be correct'{
[String]$jaCoCoReportXml = Get-JaCoCoReportXml -PesterState $testState -CoverageReport $coverageReport -DetailedCodeCoverage
$jaCoCoReportXml = $jaCoCoReportXml -replace 'Pester \([^\)]*','Pester (date'
$jaCoCoReportXml = $jaCoCoReportXml -replace 'start="[0-9]*"','start=""'
$jaCoCoReportXml = $jaCoCoReportXml -replace 'dump="[0-9]*"','dump=""'
$jaCoCoReportXml = $jaCoCoReportXml -replace "$([System.Environment]::NewLine)",''
$jaCoCoReportXml = $jaCoCoReportXml.Replace($root,'')
[String]$ReferenceReport = "{0}{1}{2}" -f [String]'<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.0//EN" "report.dtd"><report name="Pester (date)"><sessioninfo id="this" start="" dump="" /><package name="Powershell"><sourcefile name="', $([System.IO.Path]::DirectorySeparatorChar).ToString(), [String]'TestScript.ps1"><line nr="5" ci="1" mi="0" /><line nr="6" ci="1" mi="0" /><line nr="9" ci="1" mi="0" /><line nr="11" ci="1" mi="0" /><line nr="12" ci="1" mi="0" /><line nr="18" ci="0" mi="1" /><line nr="21" ci="1" mi="0" /><counter type="LINE" missed="1" covered="6" /></sourcefile></package><counter type="INSTRUCTION" missed="1" covered="6" /><counter type="LINE" missed="1" covered="6" /><counter type="METHOD" missed="1" covered="3" /><counter type="CLASS" missed="0" covered="1" /></report>'
$jaCoCoReportXml | should -Be $ReferenceReport
}
Exit-CoverageAnalysis -PesterState $testState
}
Expand Down
86 changes: 84 additions & 2 deletions Functions/Coverage.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ function Get-CoverageReport

$missedCommands = @(Get-CoverageMissedCommands -CommandCoverage $PesterState.CommandCoverage | & $SafeCommands['Select-Object'] File, Line, Function, Command)
$hitCommands = @(Get-CoverageHitCommands -CommandCoverage $PesterState.CommandCoverage | & $SafeCommands['Select-Object'] File, Line, Function, Command)
$allCommands = @($PesterState.CommandCoverage | & $SafeCommands['Select-Object'] File, Line, Function, Command, Breakpoint)
$analyzedFiles = @($PesterState.CommandCoverage | & $SafeCommands['Select-Object'] -ExpandProperty File -Unique)
$fileCount = $analyzedFiles.Count

Expand All @@ -500,6 +501,7 @@ function Get-CoverageReport
NumberOfCommandsMissed = $missedCommands.Count
MissedCommands = $missedCommands
HitCommands = $hitCommands
AllCommands = $allCommands
AnalyzedFiles = $analyzedFiles
}
}
Expand Down Expand Up @@ -580,7 +582,9 @@ function Get-JaCoCoReportXml {
[parameter(Mandatory=$true)]
$PesterState,
[parameter(Mandatory=$true)]
[object] $CoverageReport
[object] $CoverageReport,

[Switch]$DetailedCodeCoverage
)

if ($null -eq $CoverageReport -or ($pester.Show -eq [Pester.OutputTypes]::None) -or $CoverageReport.NumberOfCommandsAnalyzed -eq 0)
Expand Down Expand Up @@ -611,7 +615,11 @@ function Get-JaCoCoReportXml {
$jaCoCoReport = "<?xml version=""1.0"" encoding=""UTF-8"" standalone=""no""?>$([System.Environment]::NewLine)"
$jaCoCoReport += "<report name="""">$([System.Environment]::NewLine)"
$jaCoCoReport += "<sessioninfo id=""this"" start="""" dump="""" />$([System.Environment]::NewLine)"
$jaCoCoReport += "<counter type=""INSTRUCTION"" missed="""" covered=""""/>$([System.Environment]::NewLine)"
if ($DetailedCodeCoverage)
{
$jaCoCoReport += "<package name=""Powershell""/>$([System.Environment]::NewLine)"
}
$jaCoCoReport += "<counter type=""INSTRUCTION"" missed="""" covered=""""/>"
$jaCoCoReport += "<counter type=""LINE"" missed="""" covered=""""/>$([System.Environment]::NewLine)"
$jaCoCoReport += "<counter type=""METHOD"" missed="""" covered=""""/>$([System.Environment]::NewLine)"
$jaCoCoReport += "<counter type=""CLASS"" missed="""" covered=""""/>$([System.Environment]::NewLine)"
Expand All @@ -621,6 +629,80 @@ function Get-JaCoCoReportXml {
$jaCoCoReportXml.report.name = "Pester ($now)"
$jaCoCoReportXml.report.sessioninfo.start=$startTime.ToString()
$jaCoCoReportXml.report.sessioninfo.dump=$endTime.ToString()

if ($DetailedCodeCoverage)
{

$fileName = ""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable assigned but not used. Can you check why?

$lineNr = -1
$report = $jaCoCoReportXml.ChildNodes[1]
$package = $report.ChildNodes[1]
$mi = 0
$ci = 0
$missed = 0
$covered = 0
$line = $null
foreach($row in $CoverageReport.AllCommands)
{
if ($sourceName -ne $row.File)
{
$xmlFile = $jaCoCoReportXml.CreateElement("sourcefile")
$xmlName = $jaCoCoReportXml.CreateAttribute("name")
$xmlName.value = $row.File
$null = $xmlFile.Attributes.Append($xmlName)
$null = $package.AppendChild($xmlFile)
$sourceName = $row.File
$lineNr = -1;
$counter = $jaCoCoReportXml.CreateElement("counter")

$missed = 0
$covered = 0

$typeCounter = $counter.Attributes.Append($jaCoCoReportXml.CreateAttribute("type"))
$typeCounter.Value = "LINE"

$miCounter = $counter.Attributes.Append($jaCoCoReportXml.CreateAttribute("missed"))
$miCounter.value = $missed

$ciCounter = $counter.Attributes.Append($jaCoCoReportXml.CreateAttribute("covered"))
$ciCounter.value = $covered

$null = $xmlFile.AppendChild($counter)
}

if ($lineNr -ne $row.Line)
{
$line = $jaCoCoReportXml.CreateElement("line")
$nr = $jaCoCoReportXml.CreateAttribute("nr")
$nr.value = $row.Line
$mi = 0
$ci = 0
$null = $line.Attributes.Append($nr)
$ciLine = $line.Attributes.Append($jaCoCoReportXml.CreateAttribute("ci"))
$ciLine.value = 0
$miLine = $line.Attributes.Append($jaCoCoReportXml.CreateAttribute("mi"))
$miLine.value = 0
$null = $xmlFile.InsertBefore($line, $counter)
$lineNr = $row.Line
}

if ($row.Breakpoint.HitCount -eq 0)
{
$mi += 1
$miLine.value = $mi
$missed += 1
$miCounter.value = $missed
}
else
{
$ci += 1
$ciLine.value = $ci
$covered += 1
$ciCounter.value = $covered
}
}
}

$jaCoCoReportXml.report.counter[0].missed = $CoverageReport.MissedCommands.Count.ToString()
$jaCoCoReportXml.report.counter[0].covered = $CoverageReport.HitCommands.Count.ToString()
$jaCoCoReportXml.report.counter[1].missed = $missedLines.ToString()
Expand Down
23 changes: 22 additions & 1 deletion Functions/Describe.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ Optional parameter containing an array of strings. When calling Invoke-Pester,
it is possible to specify a -Tag parameter which will only execute Describe blocks
containing the same Tag.

.PARAMETER CodeCoverage
Adds a code coverage report to the Pester tests. Takes strings or hash table values.

A code coverage report lists the lines of code that did and did not run during
a Pester test. This report does not tell whether code was tested; only whether
the code ran during the test.

.EXAMPLE
function Add-Numbers($a, $b) {
return $a + $b
Expand Down Expand Up @@ -69,6 +76,8 @@ about_TestDrive
[Alias('Tags')]
[string[]] $Tag=@(),

[object[]] $CodeCoverage = @(),

[Parameter(Position = 1)]
[ValidateNotNull()]
[ScriptBlock] $Fixture = $(Throw "No test script block is provided. (Have you put the open curly brace on the next line?)")
Expand All @@ -82,7 +91,17 @@ about_TestDrive
$script:mockTable = @{}
}

DescribeImpl @PSBoundParameters -CommandUsed 'Describe' -Pester $Pester -DescribeOutputBlock ${function:Write-Describe} -TestOutputBlock ${function:Write-PesterResult}
if ($Pester.FindCodeCoverage)
{
foreach($cc in $CodeCoverage)
{
$Pester.CodeCoverage += $cc
}
}
else
{
DescribeImpl @PSBoundParameters -CommandUsed 'Describe' -Pester $Pester -DescribeOutputBlock ${function:Write-Describe} -TestOutputBlock ${function:Write-PesterResult}
}
}

function DescribeImpl {
Expand All @@ -93,6 +112,8 @@ function DescribeImpl {
[Alias('Tags')]
$Tag=@(),

[object[]] $CodeCoverage = @(),

[Parameter(Position = 1)]
[ValidateNotNull()]
[ScriptBlock] $Fixture = $(Throw "No test script block is provided. (Have you put the open curly brace on the next line?)"),
Expand Down
5 changes: 5 additions & 0 deletions Functions/PesterState.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ function New-PesterState
$script:Show = $Show
$script:InTest = $false

$script:FindCodeCoverage = $false
$script:CodeCoverage = @()

$script:TestResult = @()

$script:TotalCount = 0
Expand Down Expand Up @@ -333,6 +336,8 @@ function New-PesterState
"TestResult",
"SessionState",
"CommandCoverage",
"FindCodeCoverage",
"CodeCoverage",
"Strict",
"Show",
"Time",
Expand Down
2 changes: 1 addition & 1 deletion Pester.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ $manifestPath = (Join-Path $here 'Pester.psd1')
$changeLogPath = (Join-Path $here 'CHANGELOG.md')

# DO NOT CHANGE THIS TAG NAME; IT AFFECTS THE CI BUILD.

#
Describe -Tags 'VersionChecks' "Pester manifest and changelog" {
$script:manifest = $null
$script:tagVersion = $null
Expand Down
41 changes: 38 additions & 3 deletions Pester.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,9 @@ Default value is: JaCoCo.
Currently supported formats are:
- JaCoCo - this XML file format is compatible with the VSTS/TFS

.PARAMETER DetailedCodeCoverage
Add the sourcefile names and lines covered and missed to the codecoverage file.

.PARAMETER Strict
Makes Pending and Skipped tests to Failed tests. Useful for continuous
integration where you need to make sure all tests passed.
Expand Down Expand Up @@ -779,6 +782,8 @@ New-PesterOption
[ValidateSet('JaCoCo')]
[String]$CodeCoverageOutputFileFormat = "JaCoCo",

[Switch]$DetailedCodeCoverage = $false,

[Switch]$Strict,

[Parameter(Mandatory = $true, ParameterSetName = 'NewOutputSet')]
Expand Down Expand Up @@ -824,7 +829,6 @@ New-PesterOption

try
{
Enter-CoverageAnalysis -CodeCoverage $CodeCoverage -PesterState $pester
Write-PesterStart $pester $Script

$invokeTestScript = {
Expand All @@ -843,6 +847,34 @@ New-PesterOption

$testScripts = @(ResolveTestScripts $Script)


if ($DetailedCodeCoverage)
{
$pester.FindCodeCoverage = $true
$pester.CodeCoverage = $CodeCoverage

# find describe codecoverage here
foreach ($testScript in $testScripts)
{
try
{
do
{
& $invokeTestScript -Path $testScript.Path -Arguments $testScript.Arguments -Parameters $testScript.Parameters
} until ($true)
}
catch
{ }
}


$pester.FindCodeCoverage = $false
$CodeCoverage = $pester.CodeCoverage
}


Enter-CoverageAnalysis -CodeCoverage $CodeCoverage -PesterState $pester

foreach ($testScript in $testScripts)
{
try
Expand Down Expand Up @@ -875,10 +907,13 @@ New-PesterOption

$pester | Write-PesterReport
$coverageReport = Get-CoverageReport -PesterState $pester
Write-CoverageReport -CoverageReport $coverageReport
if ($DetailedCodeCoverage -eq $false)
{
Write-CoverageReport -CoverageReport $coverageReport
}
if ((& $script:SafeCommands['Get-Variable'] -Name CodeCoverageOutputFile -ValueOnly -ErrorAction $script:IgnoreErrorPreference) `
-and (& $script:SafeCommands['Get-Variable'] -Name CodeCoverageOutputFileFormat -ValueOnly -ErrorAction $script:IgnoreErrorPreference) -eq 'JaCoCo') {
$jaCoCoReport = Get-JaCoCoReportXml -PesterState $pester -CoverageReport $coverageReport
$jaCoCoReport = Get-JaCoCoReportXml -PesterState $pester -CoverageReport $coverageReport -DetailedCodeCoverage:$DetailedCodeCoverage
$jaCoCoReport | & $SafeCommands['Out-File'] $CodeCoverageOutputFile -Encoding utf8
}
Exit-CoverageAnalysis -PesterState $pester
Expand Down
Loading