Skip to content

Commit

Permalink
Reattach TestDrive and TestRegistry after running inner Invoke-Pester (
Browse files Browse the repository at this point in the history
  • Loading branch information
fflaten committed May 9, 2023
1 parent 9227418 commit 7a101ce
Show file tree
Hide file tree
Showing 6 changed files with 290 additions and 66 deletions.
11 changes: 8 additions & 3 deletions src/Main.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,10 @@ function Invoke-Pester {
# todo: move mock cleanup to BeforeAllBlockContainer when there is any?
Remove-MockFunctionsAndAliases -SessionState $PSCmdlet.SessionState
}
else {
# this will inherit to child scopes and affect behavior of ex. TestDrive/TestRegistry
$runningPesterInPester = $true
}

# this will inherit to child scopes and allow Pester to run in Pester, not checking if this is
# already defined because we want a clean state for this Invoke-Pester even if it runs inside another
Expand Down Expand Up @@ -1121,7 +1125,7 @@ function Invoke-Pester {
Invoke-PluginStep -Plugins $Plugins -Step Start -Context @{
Containers = $containers
Configuration = $pluginConfiguration
GlobalPluginData = $state.PluginData
GlobalPluginData = $pluginData
WriteDebugMessages = $PesterPreference.Debug.WriteDebugMessages.Value
Write_PesterDebugMessage = if ($PesterPreference.Debug.WriteDebugMessages.Value) { $script:SafeCommands['Write-PesterDebugMessage'] }
} -ThrowOnFailure
Expand Down Expand Up @@ -1165,8 +1169,9 @@ function Invoke-Pester {
$steps = $Plugins.End
if ($null -ne $steps -and 0 -lt @($steps).Count) {
Invoke-PluginStep -Plugins $Plugins -Step End -Context @{
TestRun = $run
Configuration = $pluginConfiguration
TestRun = $run
Configuration = $pluginConfiguration
GlobalPluginData = $pluginData
} -ThrowOnFailure
}

Expand Down
6 changes: 6 additions & 0 deletions src/functions/Environment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ function Get-TempDirectory {
}

function Get-TempRegistry {
# The Pester root key is created once and then stays in place.
# In TestDrive we use system Temp folder, but such key exists for registry so we create our own.
# Removing it would cleanup remaining keys from cancelled runs, but could break parallell or nested runs, so leaving it

$pesterTempRegistryRoot = 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Pester'
try {
# Test-Path returns true and doesn't throw access denied when path exists but user missing permission unless -PathType Container is used
if (-not (& $script:SafeCommands['Test-Path'] $pesterTempRegistryRoot -PathType Container -ErrorAction Stop)) {
# Don't use -Force parameter here because that deletes the folder and creates a race condition see
# https://github.com/pester/Pester/issues/1181
$null = & $SafeCommands['New-Item'] -Path $pesterTempRegistryRoot -ErrorAction Stop
}
}
Expand Down
94 changes: 75 additions & 19 deletions src/functions/TestDrive.ps1
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
function Get-TestDrivePlugin {
New-PluginObject -Name "TestDrive" -Start {
$p = @{
Name = 'TestDrive'
}

$p.Start = {
param($Context)

if (& $script:SafeCommands['Test-Path'] TestDrive:\) {
& $SafeCommands['Remove-Item'] (& $SafeCommands['Get-PSDrive'] TestDrive -ErrorAction Stop).Root -Force -Recurse -Confirm:$false
$existingDrive = & $SafeCommands['Get-PSDrive'] TestDrive -ErrorAction Stop
$existingDriveRoot = "$($existingDrive.Provider)::$($existingDrive.Root)"

if ($runningPesterInPester) {
# If nested run, store location and only remove PSDrive so we can re-attach it during End-step
$Context.GlobalPluginData.TestDrive = @{
ExistingTestDrivePath = $existingDriveRoot
}
}
else {
& $SafeCommands['Remove-Item'] $existingDriveRoot -Force -Recurse -Confirm:$false
}
& $SafeCommands['Remove-PSDrive'] TestDrive
}
} -EachBlockSetupStart {
}

$p.EachBlockSetupStart = {
param($Context)

if ($Context.Block.IsRoot) {
Expand All @@ -24,39 +43,76 @@
TestDrivePath = $testDrivePath
})
}
} -EachBlockTearDownEnd {
}

$p.EachBlockTearDownEnd = {
param($Context)

# Remap drive and variable if missing/wrong? Ex. if nested run was cancelled and didn't re-attach this drive
if (-not (& $script:SafeCommands['Test-Path'] TestDrive:\)) {
New-TestDrive -Path $Context.Block.PluginData.TestDrive.TestDrivePath
}

if ($Context.Block.IsRoot) {
# this is top-level block remove test drive
Remove-TestDrive -TestDrivePath $Context.Block.PluginData.TestDrive.TestDrivePath
}
else {
Clear-TestDrive -TestDrivePath $Context.Block.PluginData.TestDrive.TestDrivePath -Exclude ( $Context.Block.PluginData.TestDrive.TestDriveContent )
Clear-TestDrive -TestDrivePath $Context.Block.PluginData.TestDrive.TestDrivePath -Exclude ($Context.Block.PluginData.TestDrive.TestDriveContent)
}
}

$p.End = {
param($Context)

if ($Context.GlobalPluginData.TestDrive.ExistingTestDrivePath) {
# If nested run, reattach previous TestDrive PSDrive and variable
New-TestDrive -Path $Context.GlobalPluginData.TestDrive.ExistingTestDrivePath
}
}

New-PluginObject @p
}

function New-TestDrive ([Switch]$PassThru, [string] $Path) {
$directory = New-RandomTempDirectory
$DriveName = "TestDrive"
$null = & $SafeCommands['New-PSDrive'] -Name $DriveName -PSProvider FileSystem -Root $directory -Scope Global -Description "Pester test drive"
function New-TestDrive {
param(
[string] $Path
)

#publish the global TestDrive variable used in few places within the module
if (-not (& $SafeCommands['Test-Path'] "Variable:Global:$DriveName")) {
& $SafeCommands['New-Variable'] -Name $DriveName -Scope Global -Value $directory
if ($Path -notmatch '\S') {
$directory = New-RandomTempDirectory
}
else {
# We have a path, so probably a remap after losing the PSDrive (ex. cancelled nested Pester run)
if (-not (& $SafeCommands['Test-Path'] -Path $Path)) {
# If this runs, something deleted the container-specific folder, so we create a new folder
$null = & $SafeCommands['New-Item'] -Path $Path -ItemType Directory -ErrorAction Stop
}

$directory = & $SafeCommands['Get-Item'] $Path
}

$DriveName = 'TestDrive'
$null = & $SafeCommands['New-PSDrive'] -Name $DriveName -PSProvider FileSystem -Root $directory -Scope Global -Description 'Pester test drive'

# publish the global TestDrive variable used in few places within the module.
# using Set-Variable to support new variable + override existing (remap)
& $SafeCommands['Set-Variable'] -Name $DriveName -Scope Global -Value $directory

$directory
}


function Clear-TestDrive ([String[]]$Exclude, [string]$TestDrivePath) {
function Clear-TestDrive {
param(
[String[]] $Exclude,
[string] $TestDrivePath
)
if ([IO.Directory]::Exists($TestDrivePath)) {

Remove-TestDriveSymbolicLinks -Path $TestDrivePath

foreach ($i in [IO.Directory]::GetFileSystemEntries($TestDrivePath, "*.*", [System.IO.SearchOption]::AllDirectories)) {
foreach ($i in [IO.Directory]::GetFileSystemEntries($TestDrivePath, '*.*', [System.IO.SearchOption]::AllDirectories)) {
if ($Exclude -contains $i) {
continue
}
Expand All @@ -77,7 +133,7 @@ function New-RandomTempDirectory {

function Get-TestDriveChildItem ($TestDrivePath) {
if ([IO.Directory]::Exists($TestDrivePath)) {
[IO.Directory]::GetFileSystemEntries($TestDrivePath, "*.*", [System.IO.SearchOption]::AllDirectories)
[IO.Directory]::GetFileSystemEntries($TestDrivePath, '*.*', [System.IO.SearchOption]::AllDirectories)
}
}

Expand All @@ -93,23 +149,23 @@ function Remove-TestDriveSymbolicLinks ([String] $Path) {

# issue 621 was fixed before PowerShell 6.1
# now there is an issue with calling the Delete method in recent (6.1) builds of PowerShell
if ( (GetPesterPSVersion) -ge 6) {
if ((GetPesterPSVersion) -ge 6) {
return
}

# powershell 2-compatible
$reparsePoint = [System.IO.FileAttributes]::ReparsePoint
& $SafeCommands["Get-ChildItem"] -Recurse -Path $Path |
& $SafeCommands['Get-ChildItem'] -Recurse -Path $Path |
& $SafeCommands['Where-Object'] { ($_.Attributes -band $reparsePoint) -eq $reparsePoint } |
& $SafeCommands['Foreach-Object'] { $_.Delete() }
}

function Remove-TestDrive ($TestDrivePath) {
$DriveName = "TestDrive"
$DriveName = 'TestDrive'
$Drive = & $SafeCommands['Get-PSDrive'] -Name $DriveName -ErrorAction Ignore
$Path = ($Drive).Root

if ($pwd -like "$DriveName*" ) {
if ($pwd -like "$DriveName*") {
#will staying in the test drive cause issues?
#TODO: review this
& $SafeCommands['Write-Warning'] -Message "Your current path is set to ${pwd}:. You should leave ${DriveName}:\ before leaving Describe."
Expand Down

0 comments on commit 7a101ce

Please sign in to comment.