Skip to content

Commit

Permalink
Add error formatting back in (#819)
Browse files Browse the repository at this point in the history
Add the improved error formatting back in, one of the tests is commented out becuase it behaves weirdly, because Should changed.

Fix #815
  • Loading branch information
nohwnd authored Aug 12, 2017
1 parent 13547c7 commit 4c305b0
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 2 deletions.
141 changes: 141 additions & 0 deletions Functions/Output.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,145 @@ InModuleScope -ModuleName Pester -ScriptBlock {
Assert-MockCalled Format-PesterPath -ParameterFilter {$Path -eq $expected}
}
}

Describe ConvertTo-FailureLines {
$testPath = Join-Path $TestDrive test.ps1
$escapedTestPath = [regex]::Escape($testPath)
It 'produces correct message lines.' {
try { throw 'message' } catch { $e = $_ }

$r = $e | ConvertTo-FailureLines

$r.Message[0] | Should be 'RuntimeException: message'
$r.Message.Count | Should be 1
}
It 'failed should produces correct message lines.' {
try { 'One' | Should be 'Two' } catch { $e = $_ }
$r = $e | ConvertTo-FailureLines

$r.Message[0] | Should be 'String lengths are both 3. Strings differ at index 0.'
$r.Message[1] | Should be 'Expected: {Two}'
$r.Message[2] | Should be 'But was: {One}'
$r.Message[3] | Should be '-----------^'
$r.Message[4] | Should match "'One' | Should be 'Two'"
$r.Message.Count | Should -be 5
}
# # commented out because it does not work becuase of should, hopefully we can fix that later
# Context 'should fails in file' {
# Set-Content -Path $testPath -Value @'
# $script:IgnoreErrorPreference = 'SilentlyContinue'
# 'One' | Should Be 'Two'
# '@

# try { & $testPath } catch { $e = $_ }
# $r = $e | ConvertTo-FailureLines

# It 'produces correct message lines.' {


# $r.Message[0] | Should be 'String lengths are both 3. Strings differ at index 0.'
# $r.Message[1] | Should be 'Expected: {Two}'
# $r.Message[2] | Should be 'But was: {One}'
# $r.Message[3] | Should be '-----------^'
# $r.Message[4] | Should be "2: 'One' | Should be 'Two'"
# $r.Message.Count | Should be 5
# }
# if ( $e | Get-Member -Name ScriptStackTrace )
# {
# It 'produces correct trace lines.' {
# $r.Trace[0] | Should be "at <ScriptBlock>, $testPath`: line 2"
# $r.Trace[1] -match 'at <ScriptBlock>, .*\\Functions\\Output.Tests.ps1: line [0-9]*$' |
# Should be $true
# $r.Trace.Count | Should be 3
# }
# }
# else
# {
# It 'produces correct trace lines.' {
# $r.Trace[0] | Should be "at line: 2 in $testPath"
# $r.Trace.Count | Should be 1
# }
# }
# }
Context 'exception thrown in nested functions in file' {
Set-Content -Path $testPath -Value @'
function f1 {
throw 'f1 message'
}
function f2 {
f1
}
f2
'@

try { & $testPath } catch { $e = $_ }

$r = $e | ConvertTo-FailureLines

It 'produces correct message lines.' {
$r.Message[0] | Should be 'RuntimeException: f1 message'
}
if ( $e | Get-Member -Name ScriptStackTrace )
{
It 'produces correct trace lines.' {
$r.Trace[0] | Should be "at f1, $testPath`: line 2"
$r.Trace[1] | Should be "at f2, $testPath`: line 5"
$r.Trace[2] | Should be "at <ScriptBlock>, $testPath`: line 7"
$r.Trace[3] -match 'at <ScriptBlock>, .*\\Functions\\Output.Tests.ps1: line [0-9]*$' |
Should be $true
$r.Trace.Count | Should be 5
}
}
else
{
It 'produces correct trace lines.' {
$r.Trace[0] | Should be "at line: 2 in $testPath"
$r.Trace.Count | Should be 1
}
}
}
Context 'nested exceptions thrown in file' {
Set-Content -Path $testPath -Value @'
try
{
throw New-Object System.ArgumentException(
'inner message',
'param_name'
)
}
catch
{
throw New-Object System.FormatException(
'outer message',
$_.Exception
)
}
'@

try { & $testPath } catch { $e = $_ }

$r = $e | ConvertTo-FailureLines

It 'produces correct message lines.' {
$r.Message[0] | Should be 'ArgumentException: inner message'
$r.Message[1] | Should be 'Parameter name: param_name'
$r.Message[2] | Should be 'FormatException: outer message'
}
if ( $e | Get-Member -Name ScriptStackTrace )
{
It 'produces correct trace line.' {
$r.Trace[0] | Should be "at <ScriptBlock>, $testPath`: line 10"
$r.Trace[1] -match 'at <ScriptBlock>, .*\\Functions\\Output.Tests.ps1: line [0-9]*$'
$r.Trace.Count | Should be 3
}
}
else
{
It 'produces correct trace line.' {
$r.Trace[0] | Should be "at line: 10 in $testPath"
$r.Trace.Count | Should be 1
}
}
}
}
}
78 changes: 76 additions & 2 deletions Functions/Output.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,10 @@ function Write-PesterResult {
& $SafeCommands['Write-Host'] -ForegroundColor $ReportTheme.Fail $($TestResult.failureMessage -replace '(?m)^',$error_margin)
}
else {
& $SafeCommands['Write-Host'] -ForegroundColor $ReportTheme.Fail $($TestResult.failureMessage -replace '(?m)^',$error_margin)
& $SafeCommands['Write-Host'] -ForegroundColor $ReportTheme.Fail $($TestResult.stackTrace -replace '(?m)^',$error_margin)
$TestResult.ErrorRecord |
ConvertTo-FailureLines |
foreach {$_.Message + $_.Trace} |
foreach { & $SafeCommands['Write-Host'] -ForegroundColor $ReportTheme.Fail $($_ -replace '(?m)^',$error_margin) }
}
break
}
Expand Down Expand Up @@ -373,3 +375,75 @@ function Write-CoverageReport {
& $SafeCommands['Write-Host'] ($ReportStrings.CoverageMessage -f $command, $file, $executedPercent, $totalCommandCount, $fileCount) -Foreground $ReportTheme.Coverage
}
}

function ConvertTo-FailureLines
{
param (
[Parameter(mandatory=$true, valueFromPipeline=$true)]
$ErrorRecord
)
process {
$lines = & $script:SafeCommands['New-Object'] psobject -Property @{
Message = @()
Trace = @()
}

## convert the exception messages
$exception = $ErrorRecord.Exception
$exceptionLines = @()
while ($exception)
{
$exceptionName = $exception.GetType().Name
$thisLines = $exception.Message.Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)
if ($ErrorRecord.FullyQualifiedErrorId -ne 'PesterAssertionFailed')
{
$thisLines[0] = "$exceptionName`: $($thisLines[0])"
}
[array]::Reverse($thisLines)
$exceptionLines += $thisLines
$exception = $exception.InnerException
}
[array]::Reverse($exceptionLines)
$lines.Message += $exceptionLines
if ($ErrorRecord.FullyQualifiedErrorId -eq 'PesterAssertionFailed')
{
$lines.Message += "$($ErrorRecord.TargetObject.Line)`: $($ErrorRecord.TargetObject.LineText)".Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)
}

if ( -not ($ErrorRecord | & $SafeCommands['Get-Member'] -Name ScriptStackTrace) )
{
if ($ErrorRecord.FullyQualifiedErrorID -eq 'PesterAssertionFailed')
{
$lines.Trace += "at line: $($ErrorRecord.TargetObject.Line) in $($ErrorRecord.TargetObject.File)"
}
else
{
$lines.Trace += "at line: $($ErrorRecord.InvocationInfo.ScriptLineNumber) in $($ErrorRecord.InvocationInfo.ScriptName)"
}
return $lines
}

## convert the stack trace
$traceLines = $ErrorRecord.ScriptStackTrace.Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)

$count = 0

# omit the lines internal to Pester
foreach ( $line in $traceLines )
{
if ( $line -match '^at (Invoke-Test|Context|Describe|InModuleScope|Invoke-Pester), .*\\Functions\\.*.ps1: line [0-9]*$' )
{
break
}
$count ++
}
$lines.Trace += $traceLines |
& $SafeCommands['Select-Object'] -First $count |
& $SafeCommands['Where-Object'] {
$_ -notmatch '^at Should<End>, .*\\Functions\\Assertions\\Should.ps1: line [0-9]*$' -and
$_ -notmatch '^at Assert-MockCalled, .*\\Functions\\Mock.ps1: line [0-9]*$'
}

return $lines
}
}

0 comments on commit 4c305b0

Please sign in to comment.