@@ -146,6 +146,85 @@ public async Task RunAndCaptureText_EmptyOutput(bool useAsync)
146146 Assert . Equal ( string . Empty , result . StandardError ) ;
147147 }
148148
149+ [ ConditionalTheory ( typeof ( RemoteExecutor ) , nameof ( RemoteExecutor . IsSupported ) ) ]
150+ [ InlineData ( false ) ]
151+ [ InlineData ( true ) ]
152+ public async Task RunAndCaptureText_WithTimeoutOrCancellation_CapturesOutput ( bool useAsync )
153+ {
154+ using Process template = CreateProcess ( static ( ) =>
155+ {
156+ Console . Write ( "captured" ) ;
157+ Console . Error . Write ( "errors" ) ;
158+ return RemoteExecutor . SuccessExitCode ;
159+ } ) ;
160+
161+ template . StartInfo . RedirectStandardOutput = true ;
162+ template . StartInfo . RedirectStandardError = true ;
163+
164+ ProcessTextOutput result ;
165+ if ( useAsync )
166+ {
167+ using var cts = new CancellationTokenSource ( TimeSpan . FromMinutes ( 1 ) ) ;
168+ result = await Process . RunAndCaptureTextAsync ( template . StartInfo , cts . Token ) ;
169+ }
170+ else
171+ {
172+ result = Process . RunAndCaptureText ( template . StartInfo , TimeSpan . FromMinutes ( 1 ) ) ;
173+ }
174+
175+ Assert . Equal ( RemoteExecutor . SuccessExitCode , result . ExitStatus . ExitCode ) ;
176+ Assert . False ( result . ExitStatus . Canceled ) ;
177+ Assert . Equal ( "captured" , result . StandardOutput ) ;
178+ Assert . Equal ( "errors" , result . StandardError ) ;
179+ }
180+
181+ [ ConditionalTheory ( typeof ( RemoteExecutor ) , nameof ( RemoteExecutor . IsSupported ) ) ]
182+ [ InlineData ( false ) ]
183+ [ InlineData ( true ) ]
184+ public async Task RunAndCaptureText_WithTimeoutOrCancellation_KillsLongRunningProcess ( bool useAsync )
185+ {
186+ using Process template = CreateSleepProcess ( ( int ) TimeSpan . FromHours ( 1 ) . TotalMilliseconds ) ;
187+
188+ template . StartInfo . RedirectStandardOutput = true ;
189+ template . StartInfo . RedirectStandardError = true ;
190+
191+ if ( useAsync )
192+ {
193+ await Assert . ThrowsAnyAsync < OperationCanceledException > ( async ( ) =>
194+ await Process . RunAndCaptureTextAsync ( template . StartInfo , new CancellationTokenSource ( TimeSpan . FromMilliseconds ( 100 ) ) . Token ) ) ;
195+ }
196+ else
197+ {
198+ Assert . ThrowsAny < TimeoutException > ( ( ) =>
199+ Process . RunAndCaptureText ( template . StartInfo , TimeSpan . FromMilliseconds ( 100 ) ) ) ;
200+ }
201+ }
202+
203+ [ Theory ]
204+ [ InlineData ( false , false , true ) ]
205+ [ InlineData ( false , true , false ) ]
206+ [ InlineData ( false , false , false ) ]
207+ [ InlineData ( true , false , true ) ]
208+ [ InlineData ( true , true , false ) ]
209+ [ InlineData ( true , false , false ) ]
210+ public async Task RunAndCaptureText_NotRedirected_ThrowsInvalidOperationException ( bool useAsync , bool redirectOutput , bool redirectError )
211+ {
212+ ProcessStartInfo startInfo = new ( "someprocess" )
213+ {
214+ RedirectStandardOutput = redirectOutput ,
215+ RedirectStandardError = redirectError
216+ } ;
217+
218+ if ( useAsync )
219+ {
220+ await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => Process . RunAndCaptureTextAsync ( startInfo ) ) ;
221+ }
222+ else
223+ {
224+ Assert . Throws < InvalidOperationException > ( ( ) => Process . RunAndCaptureText ( startInfo ) ) ;
225+ }
226+ }
227+
149228 [ Theory ]
150229 [ InlineData ( false ) ]
151230 [ InlineData ( true ) ]
0 commit comments