diff --git a/src/Pester.Runtime.ps1 b/src/Pester.Runtime.ps1 index 371068f84..d7db4e8fd 100644 --- a/src/Pester.Runtime.ps1 +++ b/src/Pester.Runtime.ps1 @@ -2073,6 +2073,7 @@ function PostProcess-DiscoveredBlock { } $blockShouldRun = $false + $allTestsSkipped = $true if ($tests.Count -gt 0) { foreach ($t in $tests) { $t.Block = $b @@ -2152,11 +2153,19 @@ function PostProcess-DiscoveredBlock { $testsToRun[-1].Last = $true $blockShouldRun = $true } + + foreach ($t in $testsToRun) { + if (-not $t.Skip) { + $allTestsSkipped = $false + break + } + } } } $childBlocks = $b.Blocks $anyChildBlockShouldRun = $false + $allChildBlockSkipped = $true if ($childBlocks.Count -gt 0) { foreach ($cb in $childBlocks) { $cb.Parent = $b @@ -2171,9 +2180,17 @@ function PostProcess-DiscoveredBlock { $childBlocksToRun[0].First = $true $childBlocksToRun[-1].Last = $true } + + foreach ($cb in $childBlocksToRun) { + if (-not $cb.Skip) { + $allChildBlockSkipped = $false + break + } + } } $shouldRunBasedOnChildren = $blockShouldRun -or $anyChildBlockShouldRun + $shouldSkipBasedOnChildren = $allTestsSkipped -and $allChildBlockSkipped if ($b.ShouldRun -and -not $shouldRunBasedOnChildren) { if ($PesterPreference.Debug.WriteDebugMessages.Value) { @@ -2182,6 +2199,26 @@ function PostProcess-DiscoveredBlock { } $b.ShouldRun = $shouldRunBasedOnChildren + + if ($b.ShouldRun) { + if (-not $b.Skip -and $shouldSkipBasedOnChildren) { + if ($PesterPreference.Debug.WriteDebugMessages.Value) { + if ($b.IsRoot) { + Write-PesterDebugMessage -Scope Skip "($($b.BlockContainer)) Container will be skipped because all included children are marked as skipped." + } else { + Write-PesterDebugMessage -Scope Skip "($($b.Path -join '.')) Block will be skipped because all included children are marked as skipped." + } + } + $b.Skip = $true + } elseif ($b.Skip -and -not $shouldSkipBasedOnChildren) { + if ($PesterPreference.Debug.WriteDebugMessages.Value) { + Write-PesterDebugMessage -Scope Skip "($($b.Path -join '.')) Block was marked as skipped, but one or more children are explicitly requested to be run, so the block itself will not be skipped." + } + # This is done to execute setup and teardown before explicitly included tests, e.g. using line filter + # Remaining children have already inherited block-level Skip earlier in this function as expected + $b.Skip = $false + } + } } } diff --git a/tst/Pester.Runtime.ts.ps1 b/tst/Pester.Runtime.ts.ps1 index 18755dee8..5df69986d 100644 --- a/tst/Pester.Runtime.ts.ps1 +++ b/tst/Pester.Runtime.ts.ps1 @@ -1225,52 +1225,49 @@ i -PassThru:$PassThru { $container.EachTestTeardown | Verify-Equal 0 } - t "skipping all items in a block will skip the parent block" { - # this is not implemented, but is a possible feature - # which could be implemented together with "if any test is explicitly unskipped in child - # then the block should run, this will be needed for running tests explicitly by path I think - # it also should be taken into consideration whether or not adding a lazySkip is a good idea and how it would - # affect implementation of this. Right now skipping the block goes from parent down, and skipping all items in a block - # will not prevent the parent block setups from running - - # $container = @{ - # OneTimeTestSetup = 0 - # OneTimeTestTeardown = 0 - # EachTestSetup = 0 - # EachTestTeardown = 0 - # TestRun = 0 - # } + t 'skipping all items in a block will skip the parent block' { + $container = @{ + OneTimeTestSetup = 0 + OneTimeTestTeardown = 0 + EachTestSetup = 0 + EachTestTeardown = 0 + TestRun = 0 + } + + $actual = Invoke-Test -SessionState $ExecutionContext.SessionState -BlockContainer (New-BlockContainerObject -ScriptBlock { + New-OneTimeTestSetup -ScriptBlock { $container.OneTimeTestSetup++ } + New-OneTimeTestTeardown -ScriptBlock { $container.OneTimeTestTeardown++ } + + New-Block 'parent block' { + New-OneTimeTestSetup -ScriptBlock { $container.OneTimeTestSetup++ } + New-OneTimeTestTeardown -ScriptBlock { $container.OneTimeTestTeardown++ } + + New-EachTestSetup -ScriptBlock { $container.EachTestSetup++ } + New-EachTestTeardown -ScriptBlock { $container.EachTestTeardown++ } + + New-Test 'test1' -Skip { + $container.TestRun++ + 'a' + } - # $actual = Invoke-Test -SessionState $ExecutionContext.SessionState -BlockContainer (New-BlockContainerObject -ScriptBlock { - # New-Block "parent block" { - # New-Block "parent block" { - # # putting this in child block because each test setup is not supported in root block - # New-OneTimeTestSetup -ScriptBlock { $container.OneTimeTestSetup++ } - # New-OneTimeTestTeardown -ScriptBlock { $container.OneTimeTestTeardown++ } - - # New-EachTestSetup -ScriptBlock { $container.EachTestSetup++ } - # New-EachTestTeardown -ScriptBlock { $container.EachTestTeardown++ } - - # New-Test "test1" -Skip { - # $container.TestRun++ - # "a" - # } - - # New-Test "test2" -Skip { - # $container.TestRun++ - # "a" - # } - # } - # } - # }) - - # # $actual.Blocks[0].Skip | Verify-True - # $actual.Blocks[0].ErrorRecord.Count | Verify-Equal 0 - # $container.TestRun | Verify-Equal 0 - # $container.OneTimeTestSetup | Verify-Equal 0 - # $container.OneTimeTestTeardown | Verify-Equal 0 - # $container.EachTestSetup | Verify-Equal 0 - # $container.EachTestTeardown | Verify-Equal 0 + New-Block 'inner block' -Skip { + New-Test 'test2' { + $container.TestRun++ + 'a' + } + } + } + }) + + # Should be marked as Skip by runtime + $actual.Blocks[0].Skip | Verify-True + $actual.Blocks[0].ErrorRecord.Count | Verify-Equal 0 + + $container.TestRun | Verify-Equal 0 + $container.OneTimeTestSetup | Verify-Equal 0 + $container.OneTimeTestTeardown | Verify-Equal 0 + $container.EachTestSetup | Verify-Equal 0 + $container.EachTestTeardown | Verify-Equal 0 } } @@ -1341,6 +1338,52 @@ i -PassThru:$PassThru { $container.EachBlockTeardown1 | Verify-Equal 1 # $container.OneTimeBlockTeardown1 | Verify-Equal 1 } + + t 'setup and teardown are executed on skipped parent blocks when a test is explicitly included' { + $container = @{ + OneTimeTestSetup = 0 + OneTimeTestTeardown = 0 + EachTestSetup = 0 + EachTestTeardown = 0 + TestRun = 0 + } + + $sb = { + New-OneTimeTestSetup -ScriptBlock { $container.OneTimeTestSetup++ } + New-OneTimeTestTeardown -ScriptBlock { $container.OneTimeTestTeardown++ } + + New-Block 'parent block' -Skip { + New-OneTimeTestSetup -ScriptBlock { $container.OneTimeTestSetup++ } + New-OneTimeTestTeardown -ScriptBlock { $container.OneTimeTestTeardown++ } + + New-EachTestSetup -ScriptBlock { $container.EachTestSetup++ } + New-EachTestTeardown -ScriptBlock { $container.EachTestTeardown++ } + + New-Test 'test1' -Skip { # <--- Linefilter here ($sb assignment + 11 lines). Should run + $container.TestRun++ + 'a' + } + + New-Test 'test2' -Skip { # Should not run + $container.TestRun++ + 'a' + } + } + } + + $f = New-FilterObject -Line "$($sb.File):$($sb.StartPosition.StartLine + 11)" + $actual = Invoke-Test -SessionState $ExecutionContext.SessionState -BlockContainer (New-BlockContainerObject -ScriptBlock $sb) -Filter $f + + # Should be marked as Skip = false by runtime + $actual.Blocks[0].Skip | Verify-False + $actual.Blocks[0].ErrorRecord.Count | Verify-Equal 0 + + $container.TestRun | Verify-Equal 1 + $container.OneTimeTestSetup | Verify-Equal 2 + $container.OneTimeTestTeardown | Verify-Equal 2 + $container.EachTestSetup | Verify-Equal 1 + $container.EachTestTeardown | Verify-Equal 1 + } } b "plugins" {