@@ -15,21 +15,24 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
1515{
1616 public class AwaitableProcess : IDisposable
1717 {
18+ private readonly object _testOutputLock = new object ( ) ;
19+
1820 private Process _process ;
1921 private readonly ProcessSpec _spec ;
2022 private readonly List < string > _lines ;
2123 private BufferBlock < string > _source ;
2224 private ITestOutputHelper _logger ;
2325 private TaskCompletionSource < int > _exited ;
2426 private bool _started ;
27+ private bool _disposed ;
2528
2629 public AwaitableProcess ( ProcessSpec spec , ITestOutputHelper logger )
2730 {
2831 _spec = spec ;
2932 _logger = logger ;
3033 _source = new BufferBlock < string > ( ) ;
3134 _lines = new List < string > ( ) ;
32- _exited = new TaskCompletionSource < int > ( ) ;
35+ _exited = new TaskCompletionSource < int > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
3336 }
3437
3538 public IEnumerable < string > Output => _lines ;
@@ -72,25 +75,25 @@ public void Start()
7275 _process . ErrorDataReceived += OnData ;
7376 _process . Exited += OnExit ;
7477
75- _logger . WriteLine ( $ "{ DateTime . Now } : starting process: '{ _process . StartInfo . FileName } { _process . StartInfo . Arguments } '") ;
78+ WriteTestOutput ( $ "{ DateTime . Now } : starting process: '{ _process . StartInfo . FileName } { _process . StartInfo . Arguments } '") ;
7679 _process . Start ( ) ;
7780 _started = true ;
7881 _process . BeginErrorReadLine ( ) ;
7982 _process . BeginOutputReadLine ( ) ;
80- _logger . WriteLine ( $ "{ DateTime . Now } : process started: '{ _process . StartInfo . FileName } { _process . StartInfo . Arguments } '") ;
83+ WriteTestOutput ( $ "{ DateTime . Now } : process started: '{ _process . StartInfo . FileName } { _process . StartInfo . Arguments } '") ;
8184 }
8285
8386 public async Task < string > GetOutputLineAsync ( string message , TimeSpan timeout )
8487 {
85- _logger . WriteLine ( $ "Waiting for output line [msg == '{ message } ']. Will wait for { timeout . TotalSeconds } sec.") ;
88+ WriteTestOutput ( $ "Waiting for output line [msg == '{ message } ']. Will wait for { timeout . TotalSeconds } sec.") ;
8689 var cts = new CancellationTokenSource ( ) ;
8790 cts . CancelAfter ( timeout ) ;
8891 return await GetOutputLineAsync ( $ "[msg == '{ message } ']", m => string . Equals ( m , message , StringComparison . Ordinal ) , cts . Token ) ;
8992 }
9093
9194 public async Task < string > GetOutputLineStartsWithAsync ( string message , TimeSpan timeout )
9295 {
93- _logger . WriteLine ( $ "Waiting for output line [msg.StartsWith('{ message } ')]. Will wait for { timeout . TotalSeconds } sec.") ;
96+ WriteTestOutput ( $ "Waiting for output line [msg.StartsWith('{ message } ')]. Will wait for { timeout . TotalSeconds } sec.") ;
9497 var cts = new CancellationTokenSource ( ) ;
9598 cts . CancelAfter ( timeout ) ;
9699 return await GetOutputLineAsync ( $ "[msg.StartsWith('{ message } ')]", m => m != null && m . StartsWith ( message , StringComparison . Ordinal ) , cts . Token ) ;
@@ -105,7 +108,7 @@ private async Task<string> GetOutputLineAsync(string predicateName, Predicate<st
105108 var next = await _source . ReceiveAsync ( cancellationToken ) ;
106109 _lines . Add ( next ) ;
107110 var match = predicate ( next ) ;
108- _logger . WriteLine ( $ "{ DateTime . Now } : recv: '{ next } '. { ( match ? "Matches" : "Does not match" ) } condition '{ predicateName } '.") ;
111+ WriteTestOutput ( $ "{ DateTime . Now } : recv: '{ next } '. { ( match ? "Matches" : "Does not match" ) } condition '{ predicateName } '.") ;
109112 if ( match )
110113 {
111114 return next ;
@@ -124,7 +127,7 @@ public async Task<IList<string>> GetAllOutputLinesAsync(CancellationToken cancel
124127 while ( await _source . OutputAvailableAsync ( cancellationToken ) )
125128 {
126129 var next = await _source . ReceiveAsync ( cancellationToken ) ;
127- _logger . WriteLine ( $ "{ DateTime . Now } : recv: '{ next } '") ;
130+ WriteTestOutput ( $ "{ DateTime . Now } : recv: '{ next } '") ;
128131 lines . Add ( next ) ;
129132 }
130133 }
@@ -134,30 +137,50 @@ public async Task<IList<string>> GetAllOutputLinesAsync(CancellationToken cancel
134137 private void OnData ( object sender , DataReceivedEventArgs args )
135138 {
136139 var line = args . Data ?? string . Empty ;
137- _logger . WriteLine ( $ "{ DateTime . Now } : post: '{ line } '") ;
140+
141+ WriteTestOutput ( $ "{ DateTime . Now } : post: '{ line } '") ;
138142 _source . Post ( line ) ;
139143 }
140144
145+ private void WriteTestOutput ( string text )
146+ {
147+ lock ( _testOutputLock )
148+ {
149+ if ( ! _disposed )
150+ {
151+ _logger . WriteLine ( text ) ;
152+ }
153+ }
154+ }
155+
141156 private void OnExit ( object sender , EventArgs args )
142157 {
143158 // Wait to ensure the process has exited and all output consumed
144159 _process . WaitForExit ( ) ;
145160 _source . Complete ( ) ;
146161 _exited . TrySetResult ( _process . ExitCode ) ;
147- _logger . WriteLine ( $ "Process { _process . Id } has exited") ;
162+ WriteTestOutput ( $ "Process { _process . Id } has exited") ;
148163 }
149164
150165 public void Dispose ( )
151166 {
152167 _source . Complete ( ) ;
153168
169+ lock ( _testOutputLock )
170+ {
171+ _disposed = true ;
172+ }
173+
154174 if ( _process != null )
155175 {
156176 if ( _started && ! _process . HasExited )
157177 {
158178 _process . KillTree ( ) ;
159179 }
160180
181+ _process . CancelErrorRead ( ) ;
182+ _process . CancelOutputRead ( ) ;
183+
161184 _process . ErrorDataReceived -= OnData ;
162185 _process . OutputDataReceived -= OnData ;
163186 _process . Exited -= OnExit ;
0 commit comments