Private/Invoke-RaidinessBrowserProcess.ps1
|
<# Running the browser once, and the single seam the tests mock. Deliberately System.Diagnostics.Process and not Start-Process: this needs stdout and a hard deadline in one place, and Start-Process gives neither cleanly. It is also the read-only lint's least favourite cmdlet, and a local browser does not need an allow marker to justify itself. Standard output is read asynchronously *before* WaitForExit. A megabyte of DOM will fill the pipe buffer and deadlock a process that is only waited on -- which is the classic way this kind of bridge hangs forever. #> function Invoke-RaidinessBrowserProcess { [CmdletBinding()] [OutputType([pscustomobject])] param( [Parameter(Mandatory = $true)] [string] $Browser, [Parameter(Mandatory = $true)] [string[]] $ArgumentList, [int] $TimeoutSeconds = 180 ) $info = [System.Diagnostics.ProcessStartInfo]::new() $info.FileName = $Browser foreach ($argument in $ArgumentList) { $info.ArgumentList.Add($argument) } $info.RedirectStandardOutput = $true $info.RedirectStandardError = $true $info.UseShellExecute = $false $info.CreateNoWindow = $true $process = [System.Diagnostics.Process]::Start($info) $stdout = $process.StandardOutput.ReadToEndAsync() $stderr = $process.StandardError.ReadToEndAsync() $finished = $process.WaitForExit($TimeoutSeconds * 1000) if (-not $finished) { try { $process.Kill($true) } catch { Write-Verbose "browser did not stop: $($_.Exception.Message)" } return [pscustomobject]@{ TimedOut = $true; ExitCode = -1; Output = ''; Error = '' } } [pscustomobject]@{ TimedOut = $false ExitCode = $process.ExitCode Output = $stdout.GetAwaiter().GetResult() Error = $stderr.GetAwaiter().GetResult() } } |