From a5bdc2f19cca5a955db2646c49953f5ce7e6c854 Mon Sep 17 00:00:00 2001 From: Armaan Mcleod Date: Sun, 27 Jun 2021 16:52:12 +1000 Subject: [PATCH] Added Filter.ExcludeLine option (#2016) * Added ExcludeLine option to FilterConfiguration * Added ExcludeLine to New-FilterObject * Added code to do excluding * Set Explicit to true * Added tests * Modifying test names --- src/Main.ps1 | 1 + src/Pester.RSpec.ps1 | 3 + src/Pester.Runtime.ps1 | 29 ++- src/csharp/Pester/FilterConfiguration.cs | 19 ++ tst/Pester.Runtime.ts.ps1 | 257 +++++++++++++++++++++++ 5 files changed, 304 insertions(+), 5 deletions(-) diff --git a/src/Main.ps1 b/src/Main.ps1 index 1a313dc62..24399d171 100644 --- a/src/Main.ps1 +++ b/src/Main.ps1 @@ -1015,6 +1015,7 @@ function Invoke-Pester { -Tag $PesterPreference.Filter.Tag.Value ` -ExcludeTag $PesterPreference.Filter.ExcludeTag.Value ` -Line $PesterPreference.Filter.Line.Value ` + -ExcludeLine $PesterPreference.Filter.ExcludeLine.Value ` -FullName $PesterPreference.Filter.FullName.Value $containers = @() diff --git a/src/Pester.RSpec.ps1 b/src/Pester.RSpec.ps1 index f3a9a70e1..8b673f397 100644 --- a/src/Pester.RSpec.ps1 +++ b/src/Pester.RSpec.ps1 @@ -322,6 +322,9 @@ function New-PesterConfiguration { Line: Filter by file and scriptblock start line, useful to run parsed tests programatically to avoid problems with expanded names. Example: 'C:\tests\file1.Tests.ps1:37' Default value: @() + ExcludeLine: Exclude by file and scriptblock start line, takes precedence over Line. + Default value: @() + FullName: Full name of test with -like wildcards, joined by dot. Example: '*.describe Get-Item.test1' Default value: @() diff --git a/src/Pester.Runtime.ps1 b/src/Pester.Runtime.ps1 index c23a2b615..9bd85e4d3 100644 --- a/src/Pester.Runtime.ps1 +++ b/src/Pester.Runtime.ps1 @@ -1721,6 +1721,23 @@ function Test-ShouldRun { } } + $excludeLineFilter = $Filter.ExcludeLine + + $line = "$(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) { + if ($PesterPreference.Debug.WriteDebugMessages.Value) { + Write-PesterDebugMessage -Scope Filter "($fullDottedPath) $($Item.ItemType) is excluded, because its path:line '$line' matches line filter '$excludeLineFilter'." + Write-PesterDebugMessage -Scope Filter "($fullDottedPath) $($Item.ItemType) is explicitly excluded, because it matched line filter, and will run even if -Skip is specified on it. Any skipped children will still be skipped." + } + $result.Exclude = $true + $result.Explicit = $true + return $result + } + } + } + # - place exclude filters above this line and include below this line $lineFilter = $Filter.Line @@ -2286,14 +2303,16 @@ function New-FilterObject { [String[]] $FullName, [String[]] $Tag, [String[]] $ExcludeTag, - [String[]] $Line + [String[]] $Line, + [String[]] $ExcludeLine ) [PSCustomObject] @{ - FullName = $FullName - Tag = $Tag - ExcludeTag = $ExcludeTag - Line = $Line + FullName = $FullName + Tag = $Tag + ExcludeTag = $ExcludeTag + Line = $Line + ExcludeLine = $ExcludeLine } } diff --git a/src/csharp/Pester/FilterConfiguration.cs b/src/csharp/Pester/FilterConfiguration.cs index 2dae157ba..dd03c511b 100644 --- a/src/csharp/Pester/FilterConfiguration.cs +++ b/src/csharp/Pester/FilterConfiguration.cs @@ -24,6 +24,7 @@ public class FilterConfiguration : ConfigurationSection private StringArrayOption _tag; private StringArrayOption _excludeTag; private StringArrayOption _line; + private StringArrayOption _excludeLine; private StringArrayOption _fullName; public static FilterConfiguration Default { get { return new FilterConfiguration(); } } @@ -39,6 +40,7 @@ public FilterConfiguration(IDictionary configuration) : this() Tag = configuration.GetArrayOrNull("Tag") ?? Tag; ExcludeTag = configuration.GetArrayOrNull("ExcludeTag") ?? ExcludeTag; Line = configuration.GetArrayOrNull("Line") ?? Line; + ExcludeLine = configuration.GetArrayOrNull("ExcludeLine") ?? ExcludeLine; FullName = configuration.GetArrayOrNull("FullName") ?? FullName; } } @@ -47,6 +49,7 @@ public FilterConfiguration() : base("Filter configuration") Tag = new StringArrayOption("Tags of Describe, Context or It to be run.", new string[0]); ExcludeTag = new StringArrayOption("Tags of Describe, Context or It to be excluded from the run.", new string[0]); Line = new StringArrayOption(@"Filter by file and scriptblock start line, useful to run parsed tests programatically to avoid problems with expanded names. Example: 'C:\tests\file1.Tests.ps1:37'", new string[0]); + ExcludeLine = new StringArrayOption("Exclude by file and scriptblock start line, takes precedence over Line.", new string[0]); FullName = new StringArrayOption("Full name of test with -like wildcards, joined by dot. Example: '*.describe Get-Item.test1'", new string[0]); } @@ -97,6 +100,22 @@ public StringArrayOption Line } } + public StringArrayOption ExcludeLine + { + get { return _excludeLine; } + set + { + if (_excludeLine == null) + { + _excludeLine = value; + } + else + { + _excludeLine = new StringArrayOption(_excludeLine, value?.Value); + } + } + } + public StringArrayOption FullName { get { return _fullName; } diff --git a/tst/Pester.Runtime.ts.ps1 b/tst/Pester.Runtime.ts.ps1 index 357e247c0..495e3ec26 100644 --- a/tst/Pester.Runtime.ts.ps1 +++ b/tst/Pester.Runtime.ts.ps1 @@ -476,6 +476,263 @@ i -PassThru:$PassThru { $actual.Include | Verify-False $actual.Exclude | Verify-False } + + t "Given a test with file path and line number it excludes it when it matches the ExcludeLine filter" { + $t = New-TestObject -Name "test1" -ScriptBlock ($sb = { "test" }) -StartLine $sb.StartPosition.StartLine + + $excludeLines = "$($sb.File):$($sb.StartPosition.StartLine)" + + $f = New-FilterObject -ExcludeLine $excludeLines + + $actual = Test-ShouldRun -Item $t -Filter $f + $actual.Include | Verify-False + $actual.Exclude | Verify-True + } + + t "Given a test with file path and line number it overrides the Line filter when it matches the ExcludeLine filter" { + $t = New-TestObject -Name "test1" -ScriptBlock ($sb = { "test" }) -StartLine $sb.StartPosition.StartLine + + $includeLines = "$($sb.File):$($sb.StartPosition.StartLine)" + $excludeLines = "$($sb.File):$($sb.StartPosition.StartLine)" + + $f = New-FilterObject -Line $includeLines -ExcludeLine $excludeLines + + $actual = Test-ShouldRun -Item $t -Filter $f + $actual.Include | Verify-False + $actual.Exclude | Verify-True + } + + t "Given two tests with file paths and line numbers it excludes both they match the ExcludeLine filter" { + $sb = { + New-Block "block1" { + New-Test "test1" { "a" } + New-Test "test2" { "b" } + } + } + + $actual = Invoke-Test -SessionState $ExecutionContext.SessionState -BlockContainer (New-BlockContainerObject -ScriptBlock $sb) + + $test1 = $actual.Blocks[0].Tests[0] + $test2 = $actual.Blocks[0].Tests[1] + + $excludeLines = "$($sb.File):$($test1.ScriptBlock.StartPosition.StartLine)", "$($sb.File):$($test2.ScriptBlock.StartPosition.StartLine)" + + $f = New-FilterObject -ExcludeLine $excludeLines + + $actual1 = Test-ShouldRun -Item $test1 -Filter $f + $actual1.Include | Verify-False + $actual1.Exclude | Verify-True + + $actual2 = Test-ShouldRun -Item $test2 -Filter $f + $actual2.Include | Verify-False + $actual2.Exclude | Verify-True + } + + t "Given two tests with file paths and line numbers it includes the first one from Line filter and excludes second one from ExcludeLine filter" { + $sb = { + New-Block "block1" { + New-Test "test1" { "a" } + New-Test "test2" { "b" } + } + } + + $actual = Invoke-Test -SessionState $ExecutionContext.SessionState -BlockContainer (New-BlockContainerObject -ScriptBlock $sb) + + $test1 = $actual.Blocks[0].Tests[0] + $test2 = $actual.Blocks[0].Tests[1] + + $includeLines = "$($sb.File):$($test1.ScriptBlock.StartPosition.StartLine)" + $excludeLines = "$($sb.File):$($test2.ScriptBlock.StartPosition.StartLine)" + + $f = New-FilterObject -Line $includeLines -ExcludeLine $excludeLines + + $actual1 = Test-ShouldRun -Item $test1 -Filter $f + $actual1.Include | Verify-True + $actual1.Exclude | Verify-False + + $actual2 = Test-ShouldRun -Item $test2 -Filter $f + $actual2.Include | Verify-False + $actual2.Exclude | Verify-True + } + + t "Given multiple tests with file paths and line numbers it includes the lines that match the Line filter and excludes when overriden with the ExcludeLine filter" { + $sb = { + New-Block "block1" { + New-Test "test1" { "a" } + New-Test "test2" { "b" } + New-Test "test3" { "c" } + } + } + + $actual = Invoke-Test -SessionState $ExecutionContext.SessionState -BlockContainer (New-BlockContainerObject -ScriptBlock $sb) + + $test1 = $actual.Blocks[0].Tests[0] + $test2 = $actual.Blocks[0].Tests[1] + $test3 = $actual.Blocks[0].Tests[2] + + $includeLines = "$($sb.File):$($test1.ScriptBlock.StartPosition.StartLine)", "$($sb.File):$($test2.ScriptBlock.StartPosition.StartLine)", "$($sb.File):$($test3.ScriptBlock.StartPosition.StartLine)" + $excludeLines = "$($sb.File):$($test3.ScriptBlock.StartPosition.StartLine)" + + $f = New-FilterObject -Line $includeLines -ExcludeLine $excludeLines + + $actual1 = Test-ShouldRun -Item $test1 -Filter $f + $actual1.Include | Verify-True + $actual1.Exclude | Verify-False + + $actual2 = Test-ShouldRun -Item $test2 -Filter $f + $actual2.Include | Verify-True + $actual2.Exclude | Verify-False + + $actual3 = Test-ShouldRun -Item $test3 -Filter $f + $actual3.Include | Verify-False + $actual3.Exclude | Verify-True + } + + t "Given multiple tests with file paths and line numbers it excludes selected tests inside a block" { + $sb = { + New-Block "block1" { + New-Test "test1" { "a" } + New-Test "test2" { "b" } + New-Test "test3" { "c" } + } + } + + $actual = Invoke-Test -SessionState $ExecutionContext.SessionState -BlockContainer (New-BlockContainerObject -ScriptBlock $sb) + + $block1 = $actual.Blocks[0] + $test1 = $block1.Tests[0] + $test2 = $block1.Tests[1] + $test3 = $block1.Tests[2] + + $includeLines = "$($sb.File):$($block1.ScriptBlock.StartPosition.StartLine)" + $excludeLines = "$($sb.File):$($test2.ScriptBlock.StartPosition.StartLine)", "$($sb.File):$($test3.ScriptBlock.StartPosition.StartLine)" + + $f = New-FilterObject -Line $includeLines -ExcludeLine $excludeLines + + $actual1 = Test-ShouldRun -Item $block1 -Filter $f + $actual1.Include | Verify-True + $actual1.Exclude | Verify-False + + $actual2 = Test-ShouldRun -Item $test1 -Filter $f + $actual2.Include | Verify-True + $actual2.Exclude | Verify-False + + $actual3 = Test-ShouldRun -Item $test2 -Filter $f + $actual3.Include | Verify-False + $actual3.Exclude | Verify-True + + $actual4 = Test-ShouldRun -Item $test3 -Filter $f + $actual4.Include | Verify-False + $actual4.Exclude | Verify-True + } + + t "Given multiple tests with file paths and line numbers it excludes block" { + $sb = { + New-Block "block1" { + New-Test "test1" { "a" } + New-Test "test2" { "b" } + New-Test "test3" { "c" } + } + } + + $actual = Invoke-Test -SessionState $ExecutionContext.SessionState -BlockContainer (New-BlockContainerObject -ScriptBlock $sb) + + $block1 = $actual.Blocks[0] + + $excludeLines = "$($sb.File):$($block1.ScriptBlock.StartPosition.StartLine)" + + $f = New-FilterObject -ExcludeLine $excludeLines + + $actual1 = Test-ShouldRun -Item $block1 -Filter $f + $actual1.Include | Verify-False + $actual1.Exclude | Verify-True + } + + t "Given multiple tests with file paths and line numbers it excludes nested blocks" { + $sb = { + # Describe + New-Block "block1" { + New-Test "test1" { "a" } + + # Context + New-Block "block2" { + New-Test "test2" { "b" } + New-Test "test3" { "c" } + } + } + } + + $actual = Invoke-Test -SessionState $ExecutionContext.SessionState -BlockContainer (New-BlockContainerObject -ScriptBlock $sb) + + $block1 = $actual.Blocks[0] + $test1 = $block1.Tests[0] + $block2 = $actual.Blocks[0].Blocks[0] + + $includeLines = "$($sb.File):$($block1.ScriptBlock.StartPosition.StartLine)" + $excludeLines = "$($sb.File):$($block2.ScriptBlock.StartPosition.StartLine)" + + $f = New-FilterObject -Line $includeLines -ExcludeLine $excludeLines + + $actual1 = Test-ShouldRun -Item $block1 -Filter $f + $actual1.Include | Verify-True + $actual1.Exclude | Verify-False + + $actual2 = Test-ShouldRun -Item $test1 -Filter $f + $actual2.Include | Verify-True + $actual2.Exclude | Verify-False + + $actual3 = Test-ShouldRun -Item $block2 -Filter $f + $actual3.Include | Verify-False + $actual3.Exclude | Verify-True + } + + t "Given multiple tests with file paths and line numbers it includes nested blocks but excludes selected tests within blocks" { + $sb = { + # Describe + New-Block "block1" { + New-Test "test1" { "a" } + + # Context + New-Block "block2" { + New-Test "test2" { "b" } + New-Test "test3" { "c" } + } + } + } + + $actual = Invoke-Test -SessionState $ExecutionContext.SessionState -BlockContainer (New-BlockContainerObject -ScriptBlock $sb) + + $block1 = $actual.Blocks[0] + $test1 = $block1.Tests[0] + $block2 = $actual.Blocks[0].Blocks[0] + $test2 = $block2.Tests[0] + $test3 = $block2.Tests[1] + + $includeLines = "$($sb.File):$($block1.ScriptBlock.StartPosition.StartLine)", "$($sb.File):$($block2.ScriptBlock.StartPosition.StartLine)", "$($sb.File):$($test2.ScriptBlock.StartPosition.StartLine)" + $excludeLines = "$($sb.File):$($test2.ScriptBlock.StartPosition.StartLine)" + + $f = New-FilterObject -Line $includeLines -ExcludeLine $excludeLines + + $actual1 = Test-ShouldRun -Item $block1 -Filter $f + $actual1.Include | Verify-True + $actual1.Exclude | Verify-False + + $actual2 = Test-ShouldRun -Item $test1 -Filter $f + $actual2.Include | Verify-True + $actual2.Exclude | Verify-False + + $actual3 = Test-ShouldRun -Item $block2 -Filter $f + $actual3.Include | Verify-True + $actual3.Exclude | Verify-False + + $actual4 = Test-ShouldRun -Item $test2 -Filter $f + $actual4.Include | Verify-False + $actual4.Exclude | Verify-True + + $actual5 = Test-ShouldRun -Item $test3 -Filter $f + $actual5.Include | Verify-True + $actual5.Exclude | Verify-False + } } b "path filter" {