using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Threading; using xunit.runner.data; using xunit.runner.wpf.ViewModel; namespace xunit.runner.wpf.Impl { internal sealed partial class RemoteTestUtil : ITestUtil { private readonly Dispatcher _dispatcher; internal RemoteTestUtil(Dispatcher dispatcher) { _dispatcher = dispatcher; } private static Connection StartWorkerProcess(string action, string argument) { var pipeName = $"xunit.runner.wpf.pipe.{Guid.NewGuid()}"; var processStartInfo = new ProcessStartInfo(); processStartInfo.FileName = typeof(xunit.runner.worker.Program).Assembly.Location; processStartInfo.Arguments = $"{pipeName} {action} {argument}"; processStartInfo.WindowStyle = ProcessWindowStyle.Hidden; var process = Process.Start(processStartInfo); try { var stream = new NamedPipeClientStream(pipeName); stream.Connect(); return new Connection(stream, process); } catch { process.Kill(); throw; } } private Task Discover(string assemblyPath, Action callback, CancellationToken cancellationToken) { var connection = StartWorkerProcess(Constants.ActionDiscover, assemblyPath); var queue = new ConcurrentQueue(); var backgroundReader = new BackgroundReader(queue, new ClientReader(connection.Stream), r => r.ReadTestCaseData(), cancellationToken); backgroundReader.ReadAsync(); var backgroundProducer = new BackgroundProducer(connection, _dispatcher, queue, callback); return backgroundProducer.Task; } private Task RunCore(string actionName, string assemblyPath, ImmutableArray testCaseDisplayNames, Action callback, CancellationToken cancellationToken) { var connection = StartWorkerProcess(actionName, assemblyPath); var queue = CreateRunQueue(connection, testCaseDisplayNames, cancellationToken); var backgroundProducer = new BackgroundProducer(connection, _dispatcher, queue, callback); return backgroundProducer.Task; } /// /// Create the which will be populated with the /// as it arrives from the worker. /// private static ConcurrentQueue CreateRunQueue(Connection connection, ImmutableArray testCaseDisplayNames, CancellationToken cancellationToken) { var queue = new ConcurrentQueue(); var unused = CreateRunQueueCore(queue, connection, testCaseDisplayNames, cancellationToken); return queue; } private static async Task CreateRunQueueCore(ConcurrentQueue queue, Connection connection, ImmutableArray testCaseDisplayNames, CancellationToken cancellationToken) { try { if (!testCaseDisplayNames.IsDefaultOrEmpty) { var backgroundWriter = new BackgroundWriter(new ClientWriter(connection.Stream), testCaseDisplayNames, (w, s) => w.Write(s), cancellationToken); await backgroundWriter.WriteAsync(); } var backgroundReader = new BackgroundReader(queue, new ClientReader(connection.Stream), r => r.ReadTestResultData(), cancellationToken); await backgroundReader.ReadAsync(); } catch (Exception ex) { Debug.Fail(ex.Message); // Signal data completed queue.Enqueue(null); } } #region ITestUtil Task ITestUtil.Discover(string assemblyPath, Action callback, CancellationToken cancellationToken) { return Discover(assemblyPath, callback, cancellationToken); } Task ITestUtil.RunAll(string assemblyPath, Action callback, CancellationToken cancellationToken) { return RunCore(Constants.ActionRunAll, assemblyPath, ImmutableArray.Empty, callback, cancellationToken); } Task ITestUtil.RunSpecific(string assemblyPath, ImmutableArray testCaseDisplayNames, Action callback, CancellationToken cancellationToken) { return RunCore(Constants.ActionRunSpecific, assemblyPath, testCaseDisplayNames, callback, cancellationToken); } #endregion } }