-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Fix Enter-PSHostProcess tests flakiness #9007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6cb0faa
63fba3c
f7de624
e6e9259
a710574
9a491bc
36a0741
77f80c3
93d2a2b
ab37807
b906140
1f16388
167875c
85829b5
087ac08
589575b
d06c4f1
a02ae9d
2f1f030
0b1e797
af77dff
b5f298b
3e8f707
009a76b
bc12333
c9a3438
de0b604
54b8409
d7c0d1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like we should have a count limit in the loop, in case the script fails for some reason, and throw on failure.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added timeout |
||
| 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 | ||
TylerLeonhardt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $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 { | ||
iSazonov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $pid | ||
| while ($true) { | ||
| Start-Sleep -Seconds 30 | Out-Null | ||
| } | ||
| $powershell = Start-Process @params | ||
| } | ||
|
|
||
| $pwshId = Wait-JobPid $pwshJob | ||
| } | ||
|
|
||
| AfterAll { | ||
| $pwsh | Stop-Process | ||
|
|
||
| if ($IsWindows) { | ||
| $powershell | Stop-Process | ||
| } | ||
| AfterEach { | ||
| $pwshJob | Stop-Job -PassThru | Remove-Job | ||
| } | ||
|
|
||
| It "Can enter and exit another PSHost" -Pending:$true { | ||
| Wait-UntilTrue { (Get-PSHostProcessInfo -Id $pwsh.Id) -ne $null } | ||
| It "Can enter, exit, and re-enter another PSHost" { | ||
| Wait-UntilTrue { [bool](Get-PSHostProcessInfo -Id $pwshId) } | ||
|
|
||
| "Enter-PSHostProcess -Id $($pwsh.Id) -ErrorAction Stop | ||
| `$pid | ||
| Exit-PSHostProcess" | pwsh -c - | Should -Be $pwsh.Id | ||
| # 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 | ||
TylerLeonhardt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Invoke-PSHostProcessScript -ArgumentString "-Id $pwshId" -Id $pwshId | | ||
iSazonov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Should -BeTrue -Because "The script was able to re-enter another process and grab the pid of '$pwshId'." | ||
| } | ||
|
|
||
| It "Can enter and exit another Windows PowerShell PSHost" -Skip:(!$IsWindows) { | ||
| Wait-UntilTrue { (Get-PSHostProcessInfo -Id $powershell.Id) -ne $null } | ||
| 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 { | ||
iSazonov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $pid | ||
| while ($true) { | ||
| Start-Sleep -Seconds 30 | Out-Null | ||
| } | ||
| } | ||
|
|
||
| "Enter-PSHostProcess -Id $($powershell.Id) -ErrorAction Stop | ||
| `$pid | ||
| Exit-PSHostProcess" | pwsh -c - | Should -Be $powershell.Id | ||
| $powershellId = Wait-JobPid $powershellJob | ||
|
|
||
| try { | ||
| 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" -Pending:$true { | ||
| It "Can enter using NamedPipeConnectionInfo" { | ||
| try { | ||
| $npInfo = [System.Management.Automation.Runspaces.NamedPipeConnectionInfo]::new($pwsh.Id) | ||
| 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() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also add
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added |
||
| $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 | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will we see more user friendly message? Should we check it?
Interesting, here parameter is ok but value is not ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not good to test for that because of Culture since the error message is in a resx.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can check for
FullyQualifiedErrorIdthat is not localized.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adityapatwardhan in this case, I don't think you can because this is not an exception thrown, it's only an EXITCODE that is set along with some error string. It's pwsh.exe trying to start up... not some command that's failing to run.
an example: