mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Fix Enter-PSHostProcess tests flakiness (#9007)
This commit is contained in:
committed by
Aditya Patwardhan
parent
6c049d0cb3
commit
a26d639a47
@@ -62,4 +62,4 @@ else
|
||||
$IP | Where-Object {$_ -ne '127.0.0.1'}
|
||||
```
|
||||
|
||||
[run-ps]:https://windowsitpro.com/powershell/running-powershell-scripts-easy-1-2-3
|
||||
[run-ps]:https://www.itprotoday.com/powershell/running-powershell-scripts-easy-1-2-3
|
||||
|
||||
@@ -334,7 +334,7 @@ For more details, see [Create and Run PowerShell Script Guide][create-run-script
|
||||
[create-ps-module]:https://www.business.com/articles/powershell-modules/
|
||||
[remoting]:https://channel9.msdn.com/Series/GetStartedPowerShell3/06
|
||||
[in-depth]: https://channel9.msdn.com/events/MMS/2012/SV-B406
|
||||
[remote-mgmt]:https://windowsitpro.com/powershell/powershell-basics-remote-management
|
||||
[remote-mgmt]:https://www.itprotoday.com/powershell/powershell-basics-remote-management
|
||||
[remote-commands]:https://docs.microsoft.com/powershell/scripting/core-powershell/running-remote-commands?view=powershell-6
|
||||
[examples]:https://examples.oreilly.com/9780596528492/
|
||||
[examples-ps-module]:https://msdn.microsoft.com/library/dd878340%28v=vs.85%29.aspx
|
||||
|
||||
@@ -119,6 +119,7 @@ Describe "ConsoleHost unit tests" -tags "Feature" {
|
||||
{ & $powershell -input blah -comm { $input } } | Should -Throw -ErrorId "IncorrectValueForFormatParameter"
|
||||
}
|
||||
}
|
||||
|
||||
Context "CommandLine" {
|
||||
It "simple -args" {
|
||||
& $powershell -noprofile { $args[0] } -args "hello world" | Should -Be "hello world"
|
||||
@@ -611,6 +612,26 @@ foo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context "CustomPipeName startup tests" {
|
||||
|
||||
It "Should create pipe file if CustomPipeName is specified" {
|
||||
$pipeName = [System.IO.Path]::GetRandomFileName()
|
||||
$pipePath = Get-PipePath $pipeName
|
||||
|
||||
# The pipePath should be created by the time the -Command is executed.
|
||||
& $powershell -CustomPipeName $pipeName -Command "Test-Path '$pipePath'" | Should -BeTrue
|
||||
}
|
||||
|
||||
It "Should throw if CustomPipeName is too long on Linux or macOS" -Skip:($IsWindows) {
|
||||
# Generate a string that is larger than the max pipe name length.
|
||||
$longPipeName = [string]::new("A", 200)
|
||||
|
||||
"`$pid" | & $powershell -CustomPipeName $longPipeName -c -
|
||||
# 64 is the ExitCode for BadCommandLineParameter
|
||||
$LASTEXITCODE | Should -Be 64
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "WindowStyle argument" -Tag Feature {
|
||||
|
||||
@@ -1,134 +1,181 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
$powershell = Join-Path -Path $PsHome -ChildPath "pwsh"
|
||||
|
||||
function Wait-JobPid {
|
||||
param (
|
||||
$Job
|
||||
)
|
||||
|
||||
# This is to prevent hanging in the test.
|
||||
$startTime = [DateTime]::Now
|
||||
$TimeoutInMilliseconds = 10000
|
||||
|
||||
# This will receive the pid of the Job process and nothing more since that was the only thing written to the pipeline.
|
||||
do {
|
||||
Start-Sleep -Seconds 1
|
||||
$pwshId = Receive-Job $Job
|
||||
|
||||
if (([DateTime]::Now - $startTime).TotalMilliseconds -gt $timeoutInMilliseconds) {
|
||||
throw "Unable to receive PowerShell process id."
|
||||
}
|
||||
} while (!$pwshId)
|
||||
|
||||
$pwshId
|
||||
}
|
||||
|
||||
# Executes the Enter/Exit PSHostProcess script that returns the pid of the process that's started.
|
||||
function Invoke-PSHostProcessScript {
|
||||
param (
|
||||
[string] $ArgumentString,
|
||||
[int] $Id,
|
||||
[int] $Retry = 5 # Default retry of 5 times
|
||||
)
|
||||
|
||||
$sb = {
|
||||
# use $i as an incrementally growing pause based on the attempt number
|
||||
# so that it's more likely to succeed.
|
||||
$commandStr = @'
|
||||
Start-Sleep -Seconds {0}
|
||||
Enter-PSHostProcess {1} -ErrorAction Stop
|
||||
$pid
|
||||
Exit-PSHostProcess
|
||||
'@ -f $i, $ArgumentString
|
||||
|
||||
($commandStr | & $powershell -c -) -eq $Id
|
||||
}
|
||||
|
||||
$result = $false
|
||||
$failures = 0
|
||||
foreach ($i in 1..$Retry) {
|
||||
if ($sb.Invoke()) {
|
||||
$result = $true
|
||||
break
|
||||
}
|
||||
|
||||
$failures++
|
||||
}
|
||||
|
||||
if($failures) {
|
||||
Write-Warning "Enter-PSHostProcess script failed $i out of $Retry times."
|
||||
}
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
Describe "Enter-PSHostProcess tests" -Tag Feature {
|
||||
Context "By Process Id" {
|
||||
BeforeAll {
|
||||
$params = @{
|
||||
FilePath = "pwsh"
|
||||
PassThru = $true
|
||||
RedirectStandardOutput = "TestDrive:\pwsh_out.log"
|
||||
RedirectStandardError = "TestDrive:\pwsh_err.log"
|
||||
}
|
||||
$pwsh = Start-Process @params
|
||||
|
||||
if ($IsWindows) {
|
||||
$params = @{
|
||||
FilePath = "powershell"
|
||||
PassThru = $true
|
||||
RedirectStandardOutput = "TestDrive:\powershell_out.log"
|
||||
RedirectStandardError = "TestDrive:\powershell_err.log"
|
||||
BeforeEach {
|
||||
# Start a normal job where the first thing it does is return $pid. After that, spin forever.
|
||||
# We will use this job as the target process for Enter-PSHostProcess
|
||||
$pwshJob = Start-Job {
|
||||
$pid
|
||||
while ($true) {
|
||||
Start-Sleep -Seconds 30 | Out-Null
|
||||
}
|
||||
$powershell = Start-Process @params
|
||||
}
|
||||
|
||||
$pwshId = Wait-JobPid $pwshJob
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
$pwsh | Stop-Process
|
||||
AfterEach {
|
||||
$pwshJob | Stop-Job -PassThru | Remove-Job
|
||||
}
|
||||
|
||||
if ($IsWindows) {
|
||||
$powershell | Stop-Process
|
||||
It "Can enter, exit, and re-enter another PSHost" {
|
||||
Wait-UntilTrue { [bool](Get-PSHostProcessInfo -Id $pwshId) }
|
||||
|
||||
# This will enter and exit another process
|
||||
Invoke-PSHostProcessScript -ArgumentString "-Id $pwshId" -Id $pwshId |
|
||||
Should -BeTrue -Because "The script was able to enter another process and grab the pid of '$pwshId'."
|
||||
|
||||
# Re-enter and exit the other process
|
||||
Invoke-PSHostProcessScript -ArgumentString "-Id $pwshId" -Id $pwshId |
|
||||
Should -BeTrue -Because "The script was able to re-enter another process and grab the pid of '$pwshId'."
|
||||
}
|
||||
|
||||
It "Can enter, exit, and re-enter another Windows PowerShell PSHost" -Skip:(!$IsWindows) {
|
||||
# Start a Windows PowerShell job where the first thing it does is return $pid. After that, spin forever.
|
||||
# We will use this job as the target process for Enter-PSHostProcess
|
||||
$powershellJob = Start-Job -PSVersion 5.1 {
|
||||
$pid
|
||||
while ($true) {
|
||||
Start-Sleep -Seconds 30 | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
It "Can enter and exit another PSHost" -Pending:$true {
|
||||
Wait-UntilTrue { (Get-PSHostProcessInfo -Id $pwsh.Id) -ne $null }
|
||||
$powershellId = Wait-JobPid $powershellJob
|
||||
|
||||
"Enter-PSHostProcess -Id $($pwsh.Id) -ErrorAction Stop
|
||||
`$pid
|
||||
Exit-PSHostProcess" | pwsh -c - | Should -Be $pwsh.Id
|
||||
}
|
||||
|
||||
It "Can enter and exit another Windows PowerShell PSHost" -Skip:(!$IsWindows) {
|
||||
Wait-UntilTrue { (Get-PSHostProcessInfo -Id $powershell.Id) -ne $null }
|
||||
|
||||
"Enter-PSHostProcess -Id $($powershell.Id) -ErrorAction Stop
|
||||
`$pid
|
||||
Exit-PSHostProcess" | pwsh -c - | Should -Be $powershell.Id
|
||||
}
|
||||
|
||||
It "Can enter using NamedPipeConnectionInfo" -Pending:$true {
|
||||
try {
|
||||
$npInfo = [System.Management.Automation.Runspaces.NamedPipeConnectionInfo]::new($pwsh.Id)
|
||||
Wait-UntilTrue { [bool](Get-PSHostProcessInfo -Id $powershellId) }
|
||||
|
||||
# This will enter and exit another process
|
||||
Invoke-PSHostProcessScript -ArgumentString "-Id $powershellId" -Id $powershellId |
|
||||
Should -BeTrue -Because "The script was able to enter another process and grab the pid of '$powershellId'."
|
||||
|
||||
# Re-enter and exit the other process
|
||||
Invoke-PSHostProcessScript -ArgumentString "-Id $powershellId" -Id $powershellId |
|
||||
Should -BeTrue -Because "The script was able to re-enter another process and grab the pid of '$powershellId'."
|
||||
|
||||
} finally {
|
||||
$powershellJob | Stop-Job -PassThru | Remove-Job
|
||||
}
|
||||
}
|
||||
|
||||
It "Can enter using NamedPipeConnectionInfo" {
|
||||
try {
|
||||
Wait-UntilTrue { [bool](Get-PSHostProcessInfo -Id $pwshId) }
|
||||
|
||||
$npInfo = [System.Management.Automation.Runspaces.NamedPipeConnectionInfo]::new($pwshId)
|
||||
$rs = [runspacefactory]::CreateRunspace($npInfo)
|
||||
$rs.Open()
|
||||
$ps = [powershell]::Create()
|
||||
$ps.Runspace = $rs
|
||||
$ps.AddScript('$pid').Invoke() | Should -Be $pwsh.Id
|
||||
$ps.AddScript('$pid').Invoke() | Should -Be $pwshId
|
||||
} finally {
|
||||
$rs.Dispose()
|
||||
$ps.Dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context "By CustomPipeName" {
|
||||
BeforeAll {
|
||||
# Helper function to get the correct path for the named pipe.
|
||||
function Get-PipePath {
|
||||
param (
|
||||
$PipeName
|
||||
)
|
||||
if ($IsWindows) {
|
||||
return "\\.\pipe\$PipeName"
|
||||
}
|
||||
"$([System.IO.Path]::GetTempPath())CoreFxPipe_$PipeName"
|
||||
}
|
||||
|
||||
It "Can enter, exit, and re-enter using CustomPipeName" {
|
||||
$pipeName = [System.IO.Path]::GetRandomFileName()
|
||||
$params = @{
|
||||
FilePath = "pwsh"
|
||||
ArgumentList = @("-CustomPipeName",$pipeName)
|
||||
PassThru = $true
|
||||
RedirectStandardOutput = "TestDrive:\pwsh_out.log"
|
||||
RedirectStandardError = "TestDrive:\pwsh_err.log"
|
||||
}
|
||||
$pwsh = Start-Process @params
|
||||
|
||||
$pipePath = Get-PipePath -PipeName $pipeName
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
$pwsh | Stop-Process
|
||||
}
|
||||
# Start a job where the first thing it does is set the custom pipe name, then return $pid.
|
||||
# After that, spin forever.
|
||||
# We will use this job as the target process for Enter-PSHostProcess
|
||||
$pwshJob = Start-Job -ArgumentList $pipeName {
|
||||
[System.Management.Automation.Remoting.RemoteSessionNamedPipeServer]::CreateCustomNamedPipeServer($args[0])
|
||||
$pid
|
||||
while ($true) { Start-Sleep -Seconds 30 | Out-Null }
|
||||
}
|
||||
|
||||
It "Can enter using CustomPipeName" -Pending:$true {
|
||||
Wait-UntilTrue { Test-Path $pipePath }
|
||||
$pwshId = Wait-JobPid $pwshJob
|
||||
|
||||
"Enter-PSHostProcess -CustomPipeName $pipeName -ErrorAction Stop
|
||||
`$pid
|
||||
Exit-PSHostProcess" | pwsh -c - | Should -Be $pwsh.Id
|
||||
}
|
||||
try {
|
||||
Wait-UntilTrue { Test-Path $pipePath }
|
||||
|
||||
It "Can enter, exit, and re-enter using CustomPipeName" -Pending:$true {
|
||||
Wait-UntilTrue { Test-Path $pipePath }
|
||||
# This will enter and exit another process
|
||||
Invoke-PSHostProcessScript -ArgumentString "-CustomPipeName $pipeName" -Id $pwshId |
|
||||
Should -BeTrue -Because "The script was able to enter another process and grab the pipe of '$pipeName'."
|
||||
|
||||
"Enter-PSHostProcess -CustomPipeName $pipeName -ErrorAction Stop
|
||||
`$pid
|
||||
Exit-PSHostProcess" | pwsh -c - | Should -Be $pwsh.Id
|
||||
# Re-enter and exit the other process
|
||||
Invoke-PSHostProcessScript -ArgumentString "-CustomPipeName $pipeName" -Id $pwshId |
|
||||
Should -BeTrue -Because "The script was able to re-enter another process and grab the pipe of '$pipeName'."
|
||||
|
||||
"Enter-PSHostProcess -CustomPipeName $pipeName -ErrorAction Stop
|
||||
`$pid
|
||||
Exit-PSHostProcess" | pwsh -c - | Should -Be $pwsh.Id
|
||||
} finally {
|
||||
$pwshJob | Stop-Job -PassThru | Remove-Job
|
||||
}
|
||||
}
|
||||
|
||||
It "Should throw if CustomPipeName does not exist" {
|
||||
Wait-UntilTrue { Test-Path $pipePath }
|
||||
|
||||
{ Enter-PSHostProcess -CustomPipeName badpipename } | Should -Throw -ExpectedMessage "No named pipe was found with CustomPipeName: badpipename."
|
||||
}
|
||||
|
||||
It "Should throw if CustomPipeName is too long on Linux or macOS" {
|
||||
$longPipeName = "DoggoipsumwaggywagssmolborkingdoggowithalongsnootforpatsdoingmeafrightenporgoYapperporgolongwatershoobcloudsbigolpupperlengthboy"
|
||||
|
||||
if (!$IsWindows) {
|
||||
"`$pid" | pwsh -CustomPipeName $longPipeName -c -
|
||||
# 64 is the ExitCode for BadCommandLineParameter
|
||||
$LASTEXITCODE | Should -Be 64
|
||||
} else {
|
||||
"`$pid" | pwsh -CustomPipeName $longPipeName -c -
|
||||
$LASTEXITCODE | Should -Be 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ Copyright = 'Copyright (c) Microsoft Corporation. All rights reserved.'
|
||||
|
||||
Description = 'Temporary module for remoting tests'
|
||||
|
||||
FunctionsToExport = 'New-RemoteRunspace', 'New-RemoteSession', 'Enter-RemoteSession', 'Invoke-RemoteCommand', 'Connect-RemoteSession', 'New-RemoteRunspacePool'
|
||||
FunctionsToExport = 'New-RemoteRunspace', 'New-RemoteSession', 'Enter-RemoteSession', 'Invoke-RemoteCommand', 'Connect-RemoteSession', 'New-RemoteRunspacePool', 'Get-PipePath'
|
||||
|
||||
}
|
||||
|
||||
@@ -248,3 +248,13 @@ function Connect-RemoteSession
|
||||
$parameters = CreateParameters -ComputerName $ComputerName -Name $Name -Session $Session -ConfigurationName $ConfigurationName
|
||||
Connect-PSSession @parameters
|
||||
}
|
||||
|
||||
function Get-PipePath {
|
||||
param (
|
||||
$PipeName
|
||||
)
|
||||
if ($IsWindows) {
|
||||
return "\\.\pipe\$PipeName"
|
||||
}
|
||||
"$([System.IO.Path]::GetTempPath())CoreFxPipe_$PipeName"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user