Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions src/Tools/dotnet-watch/test/AwaitableProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,24 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
{
public class AwaitableProcess : IDisposable
{
private readonly object _testOutputLock = new object();

private Process _process;
private readonly ProcessSpec _spec;
private readonly List<string> _lines;
private BufferBlock<string> _source;
private ITestOutputHelper _logger;
private TaskCompletionSource<int> _exited;
private bool _started;
private bool _disposed;

public AwaitableProcess(ProcessSpec spec, ITestOutputHelper logger)
{
_spec = spec;
_logger = logger;
_source = new BufferBlock<string>();
_lines = new List<string>();
_exited = new TaskCompletionSource<int>();
_exited = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
}

public IEnumerable<string> Output => _lines;
Expand Down Expand Up @@ -72,25 +75,25 @@ public void Start()
_process.ErrorDataReceived += OnData;
_process.Exited += OnExit;

_logger.WriteLine($"{DateTime.Now}: starting process: '{_process.StartInfo.FileName} {_process.StartInfo.Arguments}'");
WriteTestOutput($"{DateTime.Now}: starting process: '{_process.StartInfo.FileName} {_process.StartInfo.Arguments}'");
_process.Start();
_started = true;
_process.BeginErrorReadLine();
_process.BeginOutputReadLine();
_logger.WriteLine($"{DateTime.Now}: process started: '{_process.StartInfo.FileName} {_process.StartInfo.Arguments}'");
WriteTestOutput($"{DateTime.Now}: process started: '{_process.StartInfo.FileName} {_process.StartInfo.Arguments}'");
}

public async Task<string> GetOutputLineAsync(string message, TimeSpan timeout)
{
_logger.WriteLine($"Waiting for output line [msg == '{message}']. Will wait for {timeout.TotalSeconds} sec.");
WriteTestOutput($"Waiting for output line [msg == '{message}']. Will wait for {timeout.TotalSeconds} sec.");
var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);
return await GetOutputLineAsync($"[msg == '{message}']", m => string.Equals(m, message, StringComparison.Ordinal), cts.Token);
}

public async Task<string> GetOutputLineStartsWithAsync(string message, TimeSpan timeout)
{
_logger.WriteLine($"Waiting for output line [msg.StartsWith('{message}')]. Will wait for {timeout.TotalSeconds} sec.");
WriteTestOutput($"Waiting for output line [msg.StartsWith('{message}')]. Will wait for {timeout.TotalSeconds} sec.");
var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);
return await GetOutputLineAsync($"[msg.StartsWith('{message}')]", m => m != null && m.StartsWith(message, StringComparison.Ordinal), cts.Token);
Expand All @@ -105,7 +108,7 @@ private async Task<string> GetOutputLineAsync(string predicateName, Predicate<st
var next = await _source.ReceiveAsync(cancellationToken);
_lines.Add(next);
var match = predicate(next);
_logger.WriteLine($"{DateTime.Now}: recv: '{next}'. {(match ? "Matches" : "Does not match")} condition '{predicateName}'.");
WriteTestOutput($"{DateTime.Now}: recv: '{next}'. {(match ? "Matches" : "Does not match")} condition '{predicateName}'.");
if (match)
{
return next;
Expand All @@ -124,7 +127,7 @@ public async Task<IList<string>> GetAllOutputLinesAsync(CancellationToken cancel
while (await _source.OutputAvailableAsync(cancellationToken))
{
var next = await _source.ReceiveAsync(cancellationToken);
_logger.WriteLine($"{DateTime.Now}: recv: '{next}'");
WriteTestOutput($"{DateTime.Now}: recv: '{next}'");
lines.Add(next);
}
}
Expand All @@ -134,30 +137,50 @@ public async Task<IList<string>> GetAllOutputLinesAsync(CancellationToken cancel
private void OnData(object sender, DataReceivedEventArgs args)
{
var line = args.Data ?? string.Empty;
_logger.WriteLine($"{DateTime.Now}: post: '{line}'");

WriteTestOutput($"{DateTime.Now}: post: '{line}'");
_source.Post(line);
}

private void WriteTestOutput(string text)
{
lock (_testOutputLock)
{
if (!_disposed)
{
_logger.WriteLine(text);
}
}
}

private void OnExit(object sender, EventArgs args)
{
// Wait to ensure the process has exited and all output consumed
_process.WaitForExit();
_source.Complete();
_exited.TrySetResult(_process.ExitCode);
_logger.WriteLine($"Process {_process.Id} has exited");
WriteTestOutput($"Process {_process.Id} has exited");
}

public void Dispose()
{
_source.Complete();

lock (_testOutputLock)
{
_disposed = true;
}

if (_process != null)
{
if (_started && !_process.HasExited)
{
_process.KillTree();
}

_process.CancelErrorRead();
_process.CancelOutputRead();

_process.ErrorDataReceived -= OnData;
_process.OutputDataReceived -= OnData;
_process.Exited -= OnExit;
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

3 changes: 0 additions & 3 deletions src/Tools/dotnet-watch/test/dotnet-watch.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
-->
<UseAspNetCoreSharedRuntime>true</UseAspNetCoreSharedRuntime>
<DoNotApplyWorkaroundsToMicrosoftAspNetCoreApp>true</DoNotApplyWorkaroundsToMicrosoftAspNetCoreApp>

<!-- Skipped due to https://github.com/dotnet/aspnetcore/issues/26061 -->
<SkipTests Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</SkipTests>
</PropertyGroup>

<ItemGroup>
Expand Down