From 24cb63254f79dd7483b22913376542f421cd6942 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:49:15 +0000 Subject: [PATCH 1/6] Return only unique files in Find-File --- src/Pester.RSpec.ps1 | 15 ++++++--------- tst/Pester.Tests.ps1 | 5 +++++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Pester.RSpec.ps1 b/src/Pester.RSpec.ps1 index 9f043ea04..3689b5f31 100644 --- a/src/Pester.RSpec.ps1 +++ b/src/Pester.RSpec.ps1 @@ -8,9 +8,7 @@ [string] $Extension ) - - $files = - foreach ($p in $Path) { + $files = foreach ($p in $Path) { if ([String]::IsNullOrWhiteSpace($p)) { continue } @@ -66,11 +64,13 @@ } } - Filter-Excluded -Files $files -ExcludePath $ExcludePath | & $SafeCommands['Where-Object'] { $_ } + # Deduplicate files if overlapping -Path values + $uniquePaths = [System.Collections.Generic.HashSet[string]]::new(@($files).Count) + $uniqueFiles = foreach ($f in $files) { if ($uniquePaths.Add($f.FullName)) { $f } } + Filter-Excluded -Files $uniqueFiles -ExcludePath $ExcludePath | & $SafeCommands['Where-Object'] { $_ } } function Filter-Excluded ($Files, $ExcludePath) { - if ($null -eq $ExcludePath -or @($ExcludePath).Length -eq 0) { return @($Files) } @@ -81,12 +81,9 @@ function Filter-Excluded ($Files, $ExcludePath) { $excluded = $false foreach ($exclusion in (@($ExcludePath) -replace "/", "\")) { - if ($excluded) { - continue - } - if ($p -like $exclusion) { $excluded = $true + continue } } diff --git a/tst/Pester.Tests.ps1 b/tst/Pester.Tests.ps1 index 068748b94..3510cac91 100644 --- a/tst/Pester.Tests.ps1 +++ b/tst/Pester.Tests.ps1 @@ -244,6 +244,11 @@ InPesterModuleScope { ($paths -contains (Join-Path $testDrive "SomeOtherFile.Tests.ps1")) | Should -Be $true } + It 'Deduplicates filepaths when the provided paths overlaps' { + $result = @(Find-File 'TestDrive:\*.ps1','TestDrive:\*.ps1' -Extension '.Tests.ps1') + $result.Count | Should -Be 2 + } + # It 'Assigns empty array and hashtable to the Arguments and Parameters properties when none are specified by the caller' { # $result = @(Find-File 'TestDrive:\SomeFile.ps1' -Extension ".Tests.ps1") From c6a096cb1a7179a2e7639d3b9a0065eb954e520e Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:51:53 +0000 Subject: [PATCH 2/6] Remove redundant loop in New-PesterContainer --- src/Pester.RSpec.ps1 | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Pester.RSpec.ps1 b/src/Pester.RSpec.ps1 index 3689b5f31..e0bd5cf30 100644 --- a/src/Pester.RSpec.ps1 +++ b/src/Pester.RSpec.ps1 @@ -569,12 +569,10 @@ function New-PesterContainer { # the @() is significant here, it will make it iterate even if there are no data # which allows files without data to run foreach ($d in @($dt)) { - foreach ($p in $Path) { - # resolve the path we are given in the same way we would resolve -Path on Invoke-Pester - $files = @(Find-File -Path $p -ExcludePath $PesterPreference.Run.ExcludePath.Value -Extension $PesterPreference.Run.TestExtension.Value) - foreach ($file in $files) { - New-BlockContainerObject -File $file -Data $d - } + # resolve the path we are given in the same way we would resolve -Path on Invoke-Pester + $files = @(Find-File -Path $Path -ExcludePath $PesterPreference.Run.ExcludePath.Value -Extension $PesterPreference.Run.TestExtension.Value) + foreach ($file in $files) { + New-BlockContainerObject -File $file -Data $d } } } From 0f798804c99d84d161175b4bc3adba774c11ef86 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:57:27 +0000 Subject: [PATCH 3/6] Refactor Get-CodeCoverageFilePaths and remove duplicates --- src/functions/Coverage.ps1 | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/src/functions/Coverage.ps1 b/src/functions/Coverage.ps1 index d54c803cb..596e27944 100644 --- a/src/functions/Coverage.ps1 +++ b/src/functions/Coverage.ps1 @@ -209,7 +209,7 @@ function Resolve-CoverageInfo { $filePaths = Get-CodeCoverageFilePaths -Paths $resolvedPaths -IncludeTests $includeTests -RecursePaths $recursePaths - $params = @{ + $commonParams = @{ StartLine = $UnresolvedCoverageInfo.StartLine EndLine = $UnresolvedCoverageInfo.EndLine Class = $UnresolvedCoverageInfo.Class @@ -217,46 +217,27 @@ function Resolve-CoverageInfo { } foreach ($filePath in $filePaths) { - $params['Path'] = $filePath - New-CoverageInfo @params + New-CoverageInfo @commonParams -Path $filePath } } function Get-CodeCoverageFilePaths { param ( - [object]$Paths, + [string[]]$Paths, [bool]$IncludeTests, [bool]$RecursePaths ) $testsPattern = "*$($PesterPreference.Run.TestExtension.Value)" - $filePaths = foreach ($path in $Paths) { - $item = & $SafeCommands['Get-Item'] -LiteralPath $path - if ($item -is [System.IO.FileInfo] -and ('.ps1', '.psm1') -contains $item.Extension -and ($IncludeTests -or $item.Name -notlike $testsPattern)) { - $item.FullName - } - elseif ($item -is [System.IO.DirectoryInfo]) { - $children = foreach ($i in & $SafeCommands['Get-ChildItem'] -LiteralPath $item) { - # if we recurse paths return both directories and files so they can be resolved in the - # recursive call to Get-CodeCoverageFilePaths, otherwise return just files - if ($RecursePaths) { - $i.PSPath - } - elseif (-not $i.PSIsContainer) { - $i.PSPath - } - } - Get-CodeCoverageFilePaths -Paths $children -IncludeTests $IncludeTests -RecursePaths $RecursePaths - } - elseif (-not $item.PsIsContainer) { - # todo: enable this warning for non wildcarded paths? otherwise it prints a ton of warnings for documentation and so on when using "folder/*" wildcard - # & $SafeCommands['Write-Warning'] "CodeCoverage path '$path' resolved to a non-PowerShell file '$($item.FullName)'; this path will not be part of the coverage report." + [string[]] $filteredFiles = @(foreach ($file in (& $SafeCommands['Get-ChildItem'] -LiteralPath $Paths -File -Recurse:$RecursePaths)) { + if (('.ps1', '.psm1') -contains $file.Extension -and ($IncludeTests -or $file.Name -notlike $testsPattern)) { + $file.FullName } - } - - return $filePaths + }) + $uniqueFiles = [System.Collections.Generic.HashSet[string]]::new($filteredFiles) + return $uniqueFiles } function Get-CoverageBreakpoints { From e69a522b890deb74f28e93297654ede1ed5d8bbe Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:33:08 +0000 Subject: [PATCH 4/6] Improve perf in New-PesterContainer --- src/Pester.RSpec.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Pester.RSpec.ps1 b/src/Pester.RSpec.ps1 index e0bd5cf30..fdc95dffa 100644 --- a/src/Pester.RSpec.ps1 +++ b/src/Pester.RSpec.ps1 @@ -566,12 +566,12 @@ function New-PesterContainer { } if ("Path" -eq $kind) { - # the @() is significant here, it will make it iterate even if there are no data - # which allows files without data to run - foreach ($d in @($dt)) { - # resolve the path we are given in the same way we would resolve -Path on Invoke-Pester - $files = @(Find-File -Path $Path -ExcludePath $PesterPreference.Run.ExcludePath.Value -Extension $PesterPreference.Run.TestExtension.Value) - foreach ($file in $files) { + # resolve the path we are given in the same way we would resolve -Path on Invoke-Pester + $files = @(Find-File -Path $Path -ExcludePath $PesterPreference.Run.ExcludePath.Value -Extension $PesterPreference.Run.TestExtension.Value) + foreach ($file in $files) { + # the @() is significant here, it will make it iterate even if there are no data + # which allows files without data to run + foreach ($d in @($dt)) { New-BlockContainerObject -File $file -Data $d } } From 9c6122964175fc35c0407cdea4b7c18a4be1d87f Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Wed, 10 Jul 2024 22:13:53 +0200 Subject: [PATCH 5/6] Update src/functions/Coverage.ps1 --- src/functions/Coverage.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/functions/Coverage.ps1 b/src/functions/Coverage.ps1 index 596e27944..8548c3162 100644 --- a/src/functions/Coverage.ps1 +++ b/src/functions/Coverage.ps1 @@ -236,7 +236,7 @@ function Get-CodeCoverageFilePaths { } }) - $uniqueFiles = [System.Collections.Generic.HashSet[string]]::new($filteredFiles) + $uniqueFiles = & $SafeCommands['New-Object'] -TypeName 'System.Collections.Generic.HashSet[string]' -ArgumentList (,$filteredFiles) return $uniqueFiles } From c32201fd43fa6e5ff199349d82d25db04ee79e65 Mon Sep 17 00:00:00 2001 From: Frode Flaten <3436158+fflaten@users.noreply.github.com> Date: Wed, 10 Jul 2024 22:14:00 +0200 Subject: [PATCH 6/6] Update src/Pester.RSpec.ps1 --- src/Pester.RSpec.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pester.RSpec.ps1 b/src/Pester.RSpec.ps1 index fdc95dffa..fd8abfb8a 100644 --- a/src/Pester.RSpec.ps1 +++ b/src/Pester.RSpec.ps1 @@ -65,7 +65,7 @@ } # Deduplicate files if overlapping -Path values - $uniquePaths = [System.Collections.Generic.HashSet[string]]::new(@($files).Count) + $uniquePaths = & $SafeCommands['New-Object'] -TypeName 'System.Collections.Generic.HashSet[string]' -ArgumentList (,@($files).Count) $uniqueFiles = foreach ($f in $files) { if ($uniquePaths.Add($f.FullName)) { $f } } Filter-Excluded -Files $uniqueFiles -ExcludePath $ExcludePath | & $SafeCommands['Where-Object'] { $_ } }