Skip to content
Merged
Show file tree
Hide file tree
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
53 changes: 48 additions & 5 deletions src/Pester.RSpec.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
[string] $Extension
)

# Folders we never want to descend into during discovery. .git in particular can
# hold hundreds of thousands of small files on a real repo; enumerating them only
# to filter the results out later wastes a lot of time, so the recursive walk
# below stops as soon as it sees one of these names.
$skipFolders = @('.git', '.svn', '.hg')

$files = foreach ($p in $Path) {
if ([String]::IsNullOrWhiteSpace($p)) {
continue
Expand All @@ -29,8 +35,11 @@

foreach ($item in $items) {
if ($item.PSIsContainer) {
# this is an existing directory search it for tests file
& $SafeCommands['Get-ChildItem'] -Recurse -Path $item -Filter "*$Extension" -File
# Walk the directory tree ourselves so we never open the contents of
# VCS folders. Get-ChildItem -Force returns hidden items so this works
# both for dot-prefixed folders on Linux and for folders with the
# Hidden file attribute on Windows.
Find-FileInDirectory -Directory $item -Extension $Extension -SkipFolders $skipFolders
}
elseif ("FileSystem" -ne $item.PSProvider.Name) {
# item is not a directory and exists but is not a file so we are not interested
Expand Down Expand Up @@ -58,9 +67,22 @@
}
}
else {
# this is a path that does not exist so let's hope it is
# a wildcarded path that will resolve to some files
& $SafeCommands['Get-ChildItem'] -Recurse -Path $p -Filter "*$Extension" -File
# The path didn't resolve to anything, so let Get-ChildItem try to expand
# whatever shape it is (typically a wildcard pattern that currently has no
# matches). Use -Force so hidden folders are still considered, and strip any
# results that landed inside a VCS metadata directory.
foreach ($f in (& $SafeCommands['Get-ChildItem'] -Recurse -Path $p -Filter "*$Extension" -File -Force)) {
$inSkipFolder = $false
$parent = $f.Directory
while ($null -ne $parent) {
if ($skipFolders -contains $parent.Name) {
$inSkipFolder = $true
break
}
$parent = $parent.Parent
}
if (-not $inSkipFolder) { $f }
}
}
}

Expand All @@ -70,6 +92,27 @@
Filter-Excluded -Files $uniqueFiles -ExcludePath $ExcludePath | & $SafeCommands['Where-Object'] { $_ }
}

function Find-FileInDirectory {
param(
[Parameter(Mandatory = $true)]
[System.IO.DirectoryInfo] $Directory,
[Parameter(Mandatory = $true)]
[string] $Extension,
[string[]] $SkipFolders
)

# Files in this directory first, then descend into each subdirectory that we are
# allowed to enter. The set returned is equivalent to
# `Get-ChildItem -Recurse -Filter "*$Extension" -File -Force` rooted at $Directory,
# minus anything under one of $SkipFolders.
& $SafeCommands['Get-ChildItem'] -LiteralPath $Directory.FullName -Filter "*$Extension" -File -Force

foreach ($d in (& $SafeCommands['Get-ChildItem'] -LiteralPath $Directory.FullName -Directory -Force)) {
if ($SkipFolders -contains $d.Name) { continue }
Find-FileInDirectory -Directory $d -Extension $Extension -SkipFolders $SkipFolders
}
}

function Filter-Excluded ($Files, $ExcludePath) {
if ($null -eq $ExcludePath -or @($ExcludePath).Length -eq 0) {
return @($Files)
Expand Down
68 changes: 68 additions & 0 deletions tst/Pester.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,74 @@ InPesterModuleScope {
$result.Count | Should -Be 2
}

Context 'Hidden folders and VCS metadata' {
BeforeAll {
# Reset TestDrive contents to a known shape for these tests.
Get-ChildItem 'TestDrive:\' -Force | Remove-Item -Recurse -Force

# Visible test file in a normal subfolder.
New-Item -ItemType Directory 'TestDrive:\normal' | Out-Null
New-Item -ItemType File 'TestDrive:\normal\Visible.Tests.ps1'

# Hidden (dot-prefixed) folder that we still want to discover.
New-Item -ItemType Directory 'TestDrive:\.hidden' | Out-Null
New-Item -ItemType File 'TestDrive:\.hidden\InHidden.Tests.ps1'

# Hidden folder one level deep.
New-Item -ItemType Directory 'TestDrive:\.config\tests' | Out-Null
New-Item -ItemType File 'TestDrive:\.config\tests\NestedHidden.Tests.ps1'

# .git tree that must never be enumerated. Files at multiple depths
# so we can tell if the walker descended into it.
New-Item -ItemType Directory 'TestDrive:\.git\objects\aa' | Out-Null
New-Item -ItemType File 'TestDrive:\.git\HeadLevel.Tests.ps1'
New-Item -ItemType File 'TestDrive:\.git\objects\aa\DeepInGit.Tests.ps1'

# On Windows the dot-prefix alone does not flag a folder as hidden,
# so set the attribute explicitly. Without this the original bug
# would not reproduce on Windows because Get-ChildItem already
# returned dot-prefixed folders. Use a version short-circuit so
# $IsWindows is not evaluated under Strict mode in PowerShell 5.1
# (where the automatic variable does not exist).
if (($PSVersionTable.PSVersion.Major -lt 6) -or $IsWindows) {
foreach ($d in '.hidden', '.config', '.git') {
$di = Get-Item -LiteralPath "TestDrive:\$d" -Force
$di.Attributes = $di.Attributes -bor [System.IO.FileAttributes]::Hidden
}
}
}

It 'discovers test files inside dot-prefixed (hidden) folders' {
$names = @(Find-File -Path 'TestDrive:\' -Extension '.Tests.ps1' | Select-Object -ExpandProperty Name)
$names | Should -Contain 'InHidden.Tests.ps1'
}

It 'discovers test files in nested hidden folders' {
$names = @(Find-File -Path 'TestDrive:\' -Extension '.Tests.ps1' | Select-Object -ExpandProperty Name)
$names | Should -Contain 'NestedHidden.Tests.ps1'
}

It 'does not descend into .git directories' {
# Both files live under .git at different depths; neither should
# appear in the result, proving the walker stops at .git without
# opening its contents.
$names = @(Find-File -Path 'TestDrive:\' -Extension '.Tests.ps1' | Select-Object -ExpandProperty Name)
$names | Should -Not -Contain 'HeadLevel.Tests.ps1'
$names | Should -Not -Contain 'DeepInGit.Tests.ps1'
}

It 'returns the same set as Get-ChildItem -Recurse -Force minus VCS folders' {
$found = @(Find-File -Path 'TestDrive:\' -Extension '.Tests.ps1' | Select-Object -ExpandProperty FullName | Sort-Object)
$expected = @(
Get-ChildItem -Path 'TestDrive:\' -Recurse -Filter '*.Tests.ps1' -File -Force |
Where-Object { $_.FullName -notmatch '[\\/]\.git[\\/]' } |
Select-Object -ExpandProperty FullName |
Sort-Object
)
$found | Should -Be $expected
}
}

# 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")

Expand Down
Loading