Skip to content
Open
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
72 changes: 51 additions & 21 deletions src/Pester.Runtime.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function New-PesterState {
CurrentTest = $null

Plugin = $null
PluginSteps = $null
PluginConfiguration = $null
PluginData = $null
Configuration = $null
Expand Down Expand Up @@ -333,9 +334,9 @@ function Invoke-Block ($previousBlock) {
# no callbacks are provided because we are not transitioning between any states
$frameworkSetupResult = Invoke-ScriptBlock `
-OuterSetup @(
if ($block.First) { $state.Plugin.OneTimeBlockSetupStart }
if ($block.First) { $state.PluginSteps.OneTimeBlockSetupStart }
) `
-Setup @( $state.Plugin.EachBlockSetupStart ) `
-Setup $state.PluginSteps.EachBlockSetupStart `
-Context @{
Context = @{
# context that is visible to plugins
Expand Down Expand Up @@ -442,11 +443,9 @@ function Invoke-Block ($previousBlock) {
}
}

$frameworkEachBlockTeardowns = @($state.Plugin.EachBlockTeardownEnd )
$frameworkOneTimeBlockTeardowns = @( if ($block.Last) { $state.Plugin.OneTimeBlockTeardownEnd } )
# reverse the teardowns so they run in opposite order to setups
[Array]::Reverse($frameworkEachBlockTeardowns)
[Array]::Reverse($frameworkOneTimeBlockTeardowns)
# the cached teardown steps are stored already reversed, so they run in the opposite order to setups
$frameworkEachBlockTeardowns = $state.PluginSteps.EachBlockTeardownEndReversed
$frameworkOneTimeBlockTeardowns = @( if ($block.Last) { $state.PluginSteps.OneTimeBlockTeardownEndReversed } )

# setting those values here so they are available for the teardown
# BUT they are then set again at the end of the block to make them accurate
Expand Down Expand Up @@ -509,9 +508,11 @@ function New-Test {
throw "Test cannot be directly in the root."
}

# avoid managing state by not pushing to the stack only to pop out in finally
# simply concatenate the arrays
$path = @(<# Get full name #> $history = $state.Stack.ToArray(); [Array]::Reverse($history); $history + $name)
# The test's path is its parent block's path plus the test name. The parent is always the current
# block (a test cannot be directly in the root, guarded above), and CurrentBlock.Path already holds
# the reversed stack captured when the block was entered, so reuse it instead of rebuilding the path
# from the stack (Stack.ToArray() + [Array]::Reverse) on every one of the many New-Test calls.
$path = @($state.CurrentBlock.Path) + $name

if ($PesterPreference.Debug.WriteDebugMessages.Value) {
Write-PesterDebugMessage -Scope Runtime "Entering path $($path -join '.')"
Expand Down Expand Up @@ -585,9 +586,9 @@ function Invoke-TestItem {
# no callbacks are provided because we are not transitioning between any states
$frameworkSetupResult = Invoke-ScriptBlock `
-OuterSetup @(
if ($Test.First) { $state.Plugin.OneTimeTestSetupStart }
if ($Test.First) { $state.PluginSteps.OneTimeTestSetupStart }
) `
-Setup @( $state.Plugin.EachTestSetupStart ) `
-Setup $state.PluginSteps.EachTestSetupStart `
-Context @{
Context = @{
# context visible to Plugins
Expand Down Expand Up @@ -740,10 +741,9 @@ function Invoke-TestItem {
$Test.UserDuration = $state.UserCodeStopWatch.Elapsed - $testStartTime
$Test.FrameworkDuration = $state.FrameworkStopWatch.Elapsed - $overheadStartTime

$frameworkEachTestTeardowns = @( $state.Plugin.EachTestTeardownEnd )
$frameworkOneTimeTestTeardowns = @(if ($Test.Last) { $state.Plugin.OneTimeTestTeardownEnd })
[array]::Reverse($frameworkEachTestTeardowns)
[array]::Reverse($frameworkOneTimeTestTeardowns)
# the cached teardown steps are stored already reversed, so they run in the opposite order to setups
$frameworkEachTestTeardowns = $state.PluginSteps.EachTestTeardownEndReversed
$frameworkOneTimeTestTeardowns = @(if ($Test.Last) { $state.PluginSteps.OneTimeTestTeardownEndReversed })

$frameworkTeardownResult = Invoke-ScriptBlock `
-Teardown $frameworkEachTestTeardowns `
Expand Down Expand Up @@ -1751,7 +1751,11 @@ function Test-ShouldRun {
}

$anyIncludeFilters = $false
$fullDottedPath = $Item.Path -join "."
# $fullDottedPath is consumed only by the debug messages and by the FullName filter below,
# so skip the per-item -join when neither can read it (the common case).
$fullDottedPath = if ($PesterPreference.Debug.WriteDebugMessages.Value -or ($null -ne $Filter -and $Filter.FullName)) {
$Item.Path -join "."
}
if ($null -eq $Filter) {
if ($PesterPreference.Debug.WriteDebugMessages.Value) {
Write-PesterDebugMessage -Scope Filter "($fullDottedPath) $($Item.ItemType) is included, because there is no filters."
Expand Down Expand Up @@ -1808,8 +1812,14 @@ function Test-ShouldRun {
}

$excludeLineFilter = $Filter.ExcludeLine
$lineFilter = $Filter.Line

$line = "$(if ($Item.ScriptBlock.File) { $Item.ScriptBlock.File } else { $Item.ScriptBlock.Id }):$($Item.StartLine)" -replace '\\', '/'
# The path:line key is only consumed by the exclude-line and line filters below; building it
# (a string interpolation plus a regex -replace) on every item when neither filter is set - the
# common case - is wasted work, so compute it once and only when one of those filters is present.
$line = if (($excludeLineFilter -and 0 -ne $excludeLineFilter.Count) -or ($lineFilter -and 0 -ne $lineFilter.Count)) {
"$(if ($Item.ScriptBlock.File) { $Item.ScriptBlock.File } else { $Item.ScriptBlock.Id }):$($Item.StartLine)" -replace '\\', '/'
}
if ($excludeLineFilter -and 0 -ne $excludeLineFilter.Count) {
foreach ($l in $excludeLineFilter -replace '\\', '/') {
if ($l -eq $line) {
Expand All @@ -1825,14 +1835,12 @@ function Test-ShouldRun {

# - place exclude filters above this line and include below this line

$lineFilter = $Filter.Line
# use File for saved files or Id for ScriptBlocks without files
# this filter has the ability to set the test to "explicit" so we can run
# the test even if it is marked as skipped run this include as first so we figure it out
# in one place and check if parent was included after this one to short circuit the other
# filters in case parent already knows that it will run

$line = "$(if ($Item.ScriptBlock.File) { $Item.ScriptBlock.File } else { $Item.ScriptBlock.Id }):$($Item.StartLine)" -replace '\\', '/'
# $lineFilter and $line are computed once above, before the exclude-line filter.
if ($lineFilter -and 0 -ne $lineFilter.Count) {
$anyIncludeFilters = $true
foreach ($l in $lineFilter -replace '\\', '/') {
Expand Down Expand Up @@ -2000,6 +2008,28 @@ function Invoke-Test {
}

$state.Plugin = $Plugin
# The plugin set cannot change during the run (this is the only assignment to $state.Plugin), so
# precompute the framework step arrays that Invoke-TestItem and Invoke-Block would otherwise rebuild
# from it for every test and block via member enumeration. The teardown arrays are stored already
# reversed - they run in the opposite order to setups - so the per-test [array]::Reverse goes away too.
$eachTestTeardownEndReversed = @( $Plugin.EachTestTeardownEnd )
[Array]::Reverse($eachTestTeardownEndReversed)
$oneTimeTestTeardownEndReversed = @( $Plugin.OneTimeTestTeardownEnd )
[Array]::Reverse($oneTimeTestTeardownEndReversed)
$eachBlockTeardownEndReversed = @( $Plugin.EachBlockTeardownEnd )
[Array]::Reverse($eachBlockTeardownEndReversed)
$oneTimeBlockTeardownEndReversed = @( $Plugin.OneTimeBlockTeardownEnd )
[Array]::Reverse($oneTimeBlockTeardownEndReversed)
$state.PluginSteps = @{
OneTimeTestSetupStart = @( $Plugin.OneTimeTestSetupStart )
EachTestSetupStart = @( $Plugin.EachTestSetupStart )
EachTestTeardownEndReversed = $eachTestTeardownEndReversed
OneTimeTestTeardownEndReversed = $oneTimeTestTeardownEndReversed
OneTimeBlockSetupStart = @( $Plugin.OneTimeBlockSetupStart )
EachBlockSetupStart = @( $Plugin.EachBlockSetupStart )
EachBlockTeardownEndReversed = $eachBlockTeardownEndReversed
OneTimeBlockTeardownEndReversed = $oneTimeBlockTeardownEndReversed
}
$state.PluginConfiguration = $PluginConfiguration
$state.PluginData = $PluginData
$state.Configuration = $Configuration
Expand Down
Loading