// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; using Microsoft.Extensions.Internal; using Xunit.Abstractions; using Microsoft.Extensions.Tools.Internal; namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests { public class AwaitableProcess : IDisposable { private Process _process; private readonly ProcessSpec _spec; private BufferBlock _source; private ITestOutputHelper _logger; private int _reading; public AwaitableProcess(ProcessSpec spec, ITestOutputHelper logger) { _spec = spec; _logger = logger; } public void Start() { if (_process != null) { throw new InvalidOperationException("Already started"); } var psi = new ProcessStartInfo { UseShellExecute = false, FileName = _spec.Executable, WorkingDirectory = _spec.WorkingDirectory, Arguments = ArgumentEscaper.EscapeAndConcatenate(_spec.Arguments), RedirectStandardOutput = true, RedirectStandardError = true }; _process = Process.Start(psi); _logger.WriteLine($"{DateTime.Now}: process start: '{psi.FileName} {psi.Arguments}'"); StartProcessingOutput(_process.StandardOutput); StartProcessingOutput(_process.StandardError);; } public Task GetOutputLineAsync(string message) => GetOutputLineAsync(m => message == m); public async Task GetOutputLineAsync(Predicate predicate) { while (!_source.Completion.IsCompleted) { while (await _source.OutputAvailableAsync()) { var next = await _source.ReceiveAsync(); _logger.WriteLine($"{DateTime.Now}: recv: '{next}'"); if (predicate(next)) { return next; } } } return null; } private void StartProcessingOutput(StreamReader streamReader) { _source = _source ?? new BufferBlock(); Interlocked.Increment(ref _reading); Task.Run(() => { string line; while ((line = streamReader.ReadLine()) != null) { _logger.WriteLine($"{DateTime.Now} post: {line}"); _source.Post(line); } if (Interlocked.Decrement(ref _reading) <= 0) { _source.Complete(); } }).ConfigureAwait(false); } public void Dispose() { if (_process != null && !_process.HasExited) { _process.KillTree(); } } } }