microsoft/TypeAgent

Public

mirrored fromhttps://github.com/microsoft/TypeAgentAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/remove-deprecated-dependencies

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

dotnet/autoShell.Tests/AutoShellProcess.cs

149lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Diagnostics;
5
6namespace autoShell.Tests;
7
8/// <summary>
9/// Manages an autoShell.exe child process with redirected stdin/stdout
10/// for end-to-end testing of the JSON command protocol.
11/// </summary>
12internal sealed class AutoShellProcess : IDisposable
13{
14 private static readonly string s_exePath = Path.Combine(AppContext.BaseDirectory, "autoShell.exe");
15
16 private readonly Process _process;
17
18 private AutoShellProcess(Process process)
19 {
20 _process = process;
21 }
22
23 /// <summary>
24 /// Starts autoShell.exe in interactive (stdin) mode with redirected I/O.
25 /// </summary>
26 public static AutoShellProcess StartInteractive()
27 {
28 var psi = new ProcessStartInfo
29 {
30 FileName = s_exePath,
31 RedirectStandardInput = true,
32 RedirectStandardOutput = true,
33 RedirectStandardError = true,
34 UseShellExecute = false,
35 CreateNoWindow = true,
36 };
37
38 var process = Process.Start(psi)
39 ?? throw new InvalidOperationException("Failed to start autoShell.exe");
40
41 return new AutoShellProcess(process);
42 }
43
44 /// <summary>
45 /// Starts autoShell.exe with command-line arguments (non-interactive mode).
46 /// Returns stdout content and exit code after the process completes.
47 /// </summary>
48 public static (string Output, int ExitCode) RunWithArgs(string args, int timeoutMs = 10000)
49 {
50 var psi = new ProcessStartInfo
51 {
52 FileName = s_exePath,
53 Arguments = args,
54 RedirectStandardOutput = true,
55 RedirectStandardError = true,
56 UseShellExecute = false,
57 CreateNoWindow = true,
58 };
59
60 using var process = Process.Start(psi)!;
61 string output = process.StandardOutput.ReadToEnd();
62 bool exited = process.WaitForExit(timeoutMs);
63
64 if (!exited)
65 {
66 process.Kill();
67 throw new TimeoutException($"autoShell.exe did not exit within {timeoutMs}ms");
68 }
69
70 return (output, process.ExitCode);
71 }
72
73 /// <summary>
74 /// Sends a JSON command string to stdin, terminated with \r\n.
75 /// </summary>
76 public void SendCommand(string json)
77 {
78 _process.StandardInput.WriteLine(json);
79 _process.StandardInput.Flush();
80 }
81
82 /// <summary>
83 /// Reads a single line of stdout with a timeout.
84 /// Returns null if the timeout expires before a line is available.
85 /// </summary>
86 public async Task<string?> ReadLineAsync(int timeoutMs = 5000)
87 {
88 using var cts = new CancellationTokenSource(timeoutMs);
89 try
90 {
91 var lineTask = _process.StandardOutput.ReadLineAsync(cts.Token).AsTask();
92 var completedTask = await Task.WhenAny(lineTask, Task.Delay(timeoutMs, cts.Token));
93 if (completedTask == lineTask)
94 {
95 await cts.CancelAsync();
96 return await lineTask;
97 }
98 return null;
99 }
100 catch (OperationCanceledException)
101 {
102 return null;
103 }
104 }
105
106 /// <summary>
107 /// Sends {"quit":""} and waits for the process to exit.
108 /// </summary>
109 public void SendQuit(int timeoutMs = 5000)
110 {
111 SendCommand("""{"quit":""}""");
112 _process.WaitForExit(timeoutMs);
113 }
114
115 /// <summary>
116 /// Returns true if the process has exited.
117 /// </summary>
118 public bool HasExited => _process.HasExited;
119
120 /// <summary>
121 /// Closes the stdin stream (sends EOF).
122 /// </summary>
123 public void CloseStdin()
124 {
125 _process.StandardInput.Close();
126 }
127
128 /// <summary>
129 /// Waits for the process to exit within the given timeout.
130 /// </summary>
131 public bool WaitForExit(int timeoutMs)
132 {
133 return _process.WaitForExit(timeoutMs);
134 }
135
136 public void Dispose()
137 {
138 try
139 {
140 if (!_process.HasExited)
141 {
142 _process.Kill();
143 _process.WaitForExit(3000);
144 }
145 }
146 catch { }
147 _process.Dispose();
148 }
149}
150