public async Task ConnectToTerminal() { const uint CtrlCExitCode = 0xC000013A; var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); const string Data = "abc✓ЖЖЖ①Ⅻㄨㄩ 啊阿鼾齄丂丄狚狛狜狝﨨﨩ˊˋ˙– ⿻〇㐀㐁䶴䶵"; string app = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Path.Combine(Environment.SystemDirectory, "cmd.exe") : "sh"; var options = new PtyOptions { Name = "Custom terminal", Cols = Data.Length + Environment.CurrentDirectory.Length + 50, Rows = 25, Cwd = Environment.CurrentDirectory, App = app, Environment = new Dictionary <string, string>() { { "FOO", "bar" }, { "Bazz", string.Empty }, }, }; IPtyConnection terminal = await PtyProvider.SpawnAsync(options, this.TimeoutToken); var processExitedTcs = new TaskCompletionSource <uint>(); terminal.ProcessExited += (sender, e) => processExitedTcs.TrySetResult((uint)terminal.ExitCode); string GetTerminalExitCode() => processExitedTcs.Task.IsCompleted ? $". Terminal process has exited with exit code {processExitedTcs.Task.GetAwaiter().GetResult()}." : string.Empty; var firstOutput = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); var firstDataFound = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); var output = string.Empty; var checkTerminalOutputAsync = Task.Run(async() => { var buffer = new byte[4096]; var ansiRegex = new Regex( @"[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PRZcf-ntqry=><~]))"); while (!this.TimeoutToken.IsCancellationRequested && !processExitedTcs.Task.IsCompleted) { int count = await terminal.ReaderStream.ReadAsync(buffer, 0, buffer.Length, this.TimeoutToken); if (count == 0) { break; } firstOutput.TrySetResult(null); output += encoding.GetString(buffer, 0, count); output = output.Replace("\r", string.Empty).Replace("\n", string.Empty); output = ansiRegex.Replace(output, string.Empty); var index = output.IndexOf(Data); if (index >= 0) { firstDataFound.TrySetResult(null); if (index <= output.Length - (2 * Data.Length) && output.IndexOf(Data, index + Data.Length) >= 0) { return(true); } } } firstOutput.TrySetCanceled(); firstDataFound.TrySetCanceled(); return(false); }); try { await firstOutput.Task; } catch (OperationCanceledException exception) { throw new InvalidOperationException( $"Could not get any output from terminal{GetTerminalExitCode()}", exception); } try { byte[] commandBuffer = encoding.GetBytes("echo " + Data); await terminal.WriterStream.WriteAsync(commandBuffer, 0, commandBuffer.Length, this.TimeoutToken); await terminal.WriterStream.FlushAsync(); await firstDataFound.Task; await terminal.WriterStream.WriteAsync(new byte[] { 0x0D }, 0, 1, this.TimeoutToken); // Enter await terminal.WriterStream.FlushAsync(); Assert.True(await checkTerminalOutputAsync); } catch (Exception exception) { throw new InvalidOperationException( $"Could not get expected data from terminal.{GetTerminalExitCode()} Actual terminal output:\n{output}", exception); } terminal.Resize(40, 10); terminal.Dispose(); using (this.TimeoutToken.Register(() => processExitedTcs.TrySetCanceled(this.TimeoutToken))) { uint exitCode = await processExitedTcs.Task; Assert.True( exitCode == CtrlCExitCode || // WinPty terminal exit code. exitCode == 1 || // Pseudo Console exit code on Win 10. exitCode == 0); // pty exit code on *nix. } Assert.True(terminal.WaitForExit(TestTimeoutMs)); }
public async Task <IPtyConnection> CreateSteamCMD(string strparams) { //if (activeSteamCMD != null && !activeSteamCMD.HasExited) if (activeSteamCMD != null && activeSteamCMD.ExitCode == default(int)) { throw new Exception("Can't start a SteamCMD when one is already open!"); } //string app = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Path.Combine(Environment.SystemDirectory, "cmd.exe") : "sh"; string app = SteamCMDExe.FullName; string cwd = SteamCMDFolder.FullName; var options = new PtyOptions() { Name = "SteamCMD terminal", App = app, CommandLine = strparams.Split(" "), VerbatimCommandLine = true, Cwd = cwd, Cols = 100, Rows = 80, Environment = new Dictionary <string, string>() }; IPtyConnection terminal = await PtyProvider.SpawnAsync(options, new System.Threading.CancellationToken()); terminal.ProcessExited += (sender, e) => { activeSteamCMD = null; }; activeSteamCMD = terminal; /* * * var steamCMDProc = new Process(); * steamCMDProc.StartInfo.FileName = SteamCMDExe.FullName; * steamCMDProc.StartInfo.WorkingDirectory = SteamCMDFolder.FullName; * steamCMDProc.StartInfo.Arguments = strparams; * * steamCMDProc.StartInfo.RedirectStandardInput = true; * steamCMDProc.StartInfo.RedirectStandardError = true; * steamCMDProc.StartInfo.RedirectStandardOutput = true; * //steamCMDProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; * steamCMDProc.StartInfo.CreateNoWindow = true; * steamCMDProc.StartInfo.UseShellExecute = false; * steamCMDProc.EnableRaisingEvents = true; // Investigate? * //activeSteamCMD.EnableRaisingEvents = false; * // activeSteamCMD.OutputDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data); * // activeSteamCMD.ErrorDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data); * * steamCMDProc.Exited += (sender, e) => * { * //activeSteamCMD.Close(); * activeSteamCMD = null; * }; * * activeSteamCMD = steamCMDProc; */ return(activeSteamCMD); }