diff --git a/xunit.runner.data/TestResultData.cs b/xunit.runner.data/TestResultData.cs
index c49cbe1..592962e 100644
--- a/xunit.runner.data/TestResultData.cs
+++ b/xunit.runner.data/TestResultData.cs
@@ -7,11 +7,17 @@ using System.Threading.Tasks;
namespace xunit.runner.data
{
+ ///
+ /// Note: More severe states are higher numbers.
+ ///
+ ///
public enum TestState
{
+ All = 0,
+ NotRun,
Passed,
+ Skipped,
Failed,
- Skipped
}
public sealed class TestResultData
diff --git a/xunit.runner.worker/DiscoverUtil.cs b/xunit.runner.worker/DiscoverUtil.cs
index e442e4f..4203700 100644
--- a/xunit.runner.worker/DiscoverUtil.cs
+++ b/xunit.runner.worker/DiscoverUtil.cs
@@ -44,7 +44,7 @@ namespace xunit.runner.worker
assemblyFileName: fileName,
diagnosticMessageSink: new MessageVisitor(),
shadowCopy: false))
- using (var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true))
+ using (var writer = new BinaryWriter(stream, Constants.Encoding, leaveOpen: true))
using (var impl = new Impl(xunit, writer))
{
xunit.Find(includeSourceInformation: false, messageSink: impl, discoveryOptions: TestFrameworkOptions.ForDiscovery());
diff --git a/xunit.runner.worker/RunUtil.cs b/xunit.runner.worker/RunUtil.cs
index 61ef259..2457084 100644
--- a/xunit.runner.worker/RunUtil.cs
+++ b/xunit.runner.worker/RunUtil.cs
@@ -23,6 +23,7 @@ namespace xunit.runner.worker
private void Process(string displayName, TestState state)
{
+ Console.WriteLine($"{state} - {displayName}");
var result = new TestResultData(displayName, state);
result.WriteTo(_writer);
}
diff --git a/xunit.runner.wpf/Converters/TestStateConverter.cs b/xunit.runner.wpf/Converters/TestStateConverter.cs
index d267326..6727212 100644
--- a/xunit.runner.wpf/Converters/TestStateConverter.cs
+++ b/xunit.runner.wpf/Converters/TestStateConverter.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
+using xunit.runner.data;
using xunit.runner.wpf.ViewModel;
namespace xunit.runner.wpf.Converters
diff --git a/xunit.runner.wpf/ITestUtil.cs b/xunit.runner.wpf/ITestUtil.cs
index 82b0639..eaca6a5 100644
--- a/xunit.runner.wpf/ITestUtil.cs
+++ b/xunit.runner.wpf/ITestUtil.cs
@@ -4,6 +4,8 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Windows.Threading;
+using xunit.runner.data;
using xunit.runner.wpf.ViewModel;
namespace xunit.runner.wpf
@@ -14,5 +16,27 @@ namespace xunit.runner.wpf
/// Discover the list of test cases which are available in the specified assembly.
///
List Discover(string assemblyPath);
+
+ ///
+ /// Begin a run of a unit test for the given assembly.
+ ///
+ ITestRunSession Run(Dispatcher dispatcher, string assemblyPath);
+ }
+
+ internal sealed class TestResultEventArgs : EventArgs
+ {
+ internal readonly string TestCaseDisplayName;
+ internal readonly TestState TestState;
+
+ internal TestResultEventArgs(string displayName, TestState state)
+ {
+ TestCaseDisplayName = displayName;
+ TestState = state;
+ }
+ }
+
+ internal interface ITestRunSession
+ {
+ event EventHandler TestFinished;
}
}
diff --git a/xunit.runner.wpf/Impl/RemoteTestUtil .BackgroundRunner.cs b/xunit.runner.wpf/Impl/RemoteTestUtil .BackgroundRunner.cs
new file mode 100644
index 0000000..9ac17ec
--- /dev/null
+++ b/xunit.runner.wpf/Impl/RemoteTestUtil .BackgroundRunner.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Pipes;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using xunit.runner.data;
+using xunit.runner.wpf.ViewModel;
+
+namespace xunit.runner.wpf.Impl
+{
+ internal partial class RemoteTestUtil
+ {
+ private sealed class BackgroundRunner
+ {
+ private readonly ConcurrentQueue _resultQueue;
+ private readonly BinaryReader _reader;
+
+ internal BackgroundRunner(ConcurrentQueue resultQueue, BinaryReader reader)
+ {
+ _resultQueue = resultQueue;
+ _reader = reader;
+ }
+
+ ///
+ /// This will be called on a background thread to read the results of the test from the
+ /// named pipe client stream.
+ ///
+ ///
+ internal Task GoOnBackground()
+ {
+ while (true)
+ {
+ TestResultData result;
+ try
+ {
+ result = TestResultData.ReadFrom(_reader);
+ }
+ catch
+ {
+ // Hacky way of detecting the stream being closed
+ break;
+ }
+
+ _resultQueue.Enqueue(result);
+ }
+
+ // Signal we are done
+ _resultQueue.Enqueue(null);
+
+ return Task.FromResult(true);
+ }
+ }
+ }
+}
diff --git a/xunit.runner.wpf/Impl/RemoteTestUtil .RunSession.cs b/xunit.runner.wpf/Impl/RemoteTestUtil .RunSession.cs
new file mode 100644
index 0000000..cc0ca7d
--- /dev/null
+++ b/xunit.runner.wpf/Impl/RemoteTestUtil .RunSession.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Pipes;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Threading;
+using xunit.runner.data;
+using xunit.runner.wpf.ViewModel;
+
+namespace xunit.runner.wpf.Impl
+{
+ internal partial class RemoteTestUtil
+ {
+ private sealed class RunSession : ITestRunSession
+ {
+ private const int MaxResultPerTick = 100;
+
+ private readonly Connection _connection;
+ private readonly ConcurrentQueue _resultQueue;
+ private readonly DispatcherTimer _timer;
+ private bool _closed;
+ private event EventHandler _testFinished;
+
+ internal RunSession(Connection connection, Dispatcher dispatcher, ConcurrentQueue resultQueue)
+ {
+ _connection = connection;
+ _resultQueue = resultQueue;
+ _timer = new DispatcherTimer(TimeSpan.FromMilliseconds(100), DispatcherPriority.Normal, OnTimerTick, dispatcher);
+ }
+
+ private void OnTimerTick(object sender, EventArgs e)
+ {
+ var i = 0;
+ TestResultData data;
+ while (i < MaxResultPerTick && _resultQueue.TryDequeue(out data))
+ {
+ if (data == null)
+ {
+ Close();
+ break;
+ }
+
+ _testFinished?.Invoke(this, new TestResultEventArgs(data.TestCaseDisplayName, data.TestState));
+ }
+ }
+
+ internal void Close()
+ {
+ if (_closed)
+ {
+ return;
+ }
+
+ _closed = true;
+ _timer.Stop();
+ ((IDisposable)_connection).Dispose();
+ }
+
+ #region ITestRunSession
+
+ event EventHandler ITestRunSession.TestFinished
+ {
+ add { _testFinished += value; }
+ remove { _testFinished -= value; }
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/xunit.runner.wpf/Impl/RemoteTestUtil.cs b/xunit.runner.wpf/Impl/RemoteTestUtil.cs
index 19afc5e..264fb71 100644
--- a/xunit.runner.wpf/Impl/RemoteTestUtil.cs
+++ b/xunit.runner.wpf/Impl/RemoteTestUtil.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -6,12 +7,13 @@ using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Windows.Threading;
using xunit.runner.data;
using xunit.runner.wpf.ViewModel;
namespace xunit.runner.wpf.Impl
{
- internal sealed class RemoteTestUtil : ITestUtil
+ internal sealed partial class RemoteTestUtil : ITestUtil
{
private sealed class Connection : IDisposable
{
@@ -78,7 +80,7 @@ namespace xunit.runner.wpf.Impl
var list = new List();
using (var connection = StartWorkerProcess(Constants.ActionDiscover, assemblyPath))
- using (var reader = new BinaryReader(connection.Stream, Encoding.UTF8, leaveOpen: true))
+ using (var reader = new BinaryReader(connection.Stream, Constants.Encoding, leaveOpen: true))
{
try
{
@@ -97,6 +99,16 @@ namespace xunit.runner.wpf.Impl
return list;
}
+ private RunSession Run(Dispatcher dispatcher, string assemblyPath)
+ {
+ var connection = StartWorkerProcess(Constants.ActionRun, assemblyPath);
+ var queue = new ConcurrentQueue();
+ var backgroundRunner = new BackgroundRunner(queue, new BinaryReader(connection.Stream, Constants.Encoding, leaveOpen: true));
+ Task.Run(backgroundRunner.GoOnBackground);
+
+ return new RunSession(connection, dispatcher, queue);
+ }
+
#region ITestUtil
List ITestUtil.Discover(string assemblyPath)
@@ -104,6 +116,11 @@ namespace xunit.runner.wpf.Impl
return Discover(assemblyPath);
}
+ ITestRunSession ITestUtil.Run(Dispatcher dispatcher, string assemblyPath)
+ {
+ return Run(dispatcher, assemblyPath);
+ }
+
#endregion
}
}
diff --git a/xunit.runner.wpf/ViewModel/MainViewModel.cs b/xunit.runner.wpf/ViewModel/MainViewModel.cs
index 4c16a98..e4bc08a 100644
--- a/xunit.runner.wpf/ViewModel/MainViewModel.cs
+++ b/xunit.runner.wpf/ViewModel/MainViewModel.cs
@@ -17,6 +17,7 @@ using System.IO.Pipes;
using System.IO;
using System.Text;
using xunit.runner.data;
+using System.Windows.Threading;
namespace xunit.runner.wpf.ViewModel
{
@@ -203,44 +204,6 @@ namespace xunit.runner.wpf.ViewModel
}
}
- private class TestRunVisitor : TestMessageVisitor
- {
- private readonly Func isCancelRequested;
- private readonly IEnumerable testCases;
-
- public event EventHandler TestFinished;
-
- public TestRunVisitor(IEnumerable testCases, Func isCancelRequested)
- {
- this.testCases = testCases;
- this.isCancelRequested = isCancelRequested;
- }
-
- protected override bool Visit(ITestFailed testFailed)
- {
- var testCase = testCases.Single(tc => tc.DisplayName == testFailed.TestCase.DisplayName);
- testCase.State = TestState.Failed;
- TestFinished?.Invoke(this, TestStateEventArgs.Failed);
- return !isCancelRequested();
- }
-
- protected override bool Visit(ITestPassed testPassed)
- {
- var testCase = testCases.Single(tc => tc.DisplayName == testPassed.TestCase.DisplayName);
- testCase.State = TestState.Passed;
- TestFinished?.Invoke(this, TestStateEventArgs.Passed);
- return !isCancelRequested();
- }
-
- protected override bool Visit(ITestSkipped testSkipped)
- {
- var testCase = testCases.Single(tc => tc.DisplayName == testSkipped.TestCase.DisplayName);
- testCase.State = TestState.Skipped;
- TestFinished?.Invoke(this, TestStateEventArgs.Skipped);
- return !isCancelRequested();
- }
- }
-
private bool IsBusy
{
get { return isBusy; }
@@ -270,7 +233,7 @@ namespace xunit.runner.wpf.ViewModel
private bool CanExecuteRun()
=> !IsBusy && TestCases.Any();
- private async void OnExecuteRun()
+ private void OnExecuteRun()
{
try
{
@@ -280,7 +243,7 @@ namespace xunit.runner.wpf.ViewModel
TestsFailed = 0;
TestsSkipped = 0;
CurrentRunState = TestState.NotRun;
- await Task.Run(() => RunTestsInBackground());
+ RunTests();
}
catch (Exception ex)
{
@@ -293,37 +256,26 @@ namespace xunit.runner.wpf.ViewModel
}
}
- private void RunTestsInBackground()
+ private void RunTests()
{
foreach (var tc in TestCases)
{
tc.State = TestState.NotRun;
}
- var selectedAssemblies = TestCases.ToLookup(tc => tc.AssemblyFileName);
- using (AssemblyHelper.SubscribeResolve())
- {
- foreach (var assembly in selectedAssemblies)
- {
- using (var xunit = new XunitFrontController(
- assemblyFileName: assembly.Key,
- useAppDomain: true,
- shadowCopy: false,
- diagnosticMessageSink: new DiagnosticMessageVisitor()))
- using (var testRunVisitor = new TestRunVisitor(allTestCases, () => IsCancelRequested))
- {
- testRunVisitor.TestFinished += TestRunVisitor_TestFinished;
- xunit.RunTests(assembly.Select(tcvm => xunit.Deserialize(tcvm.TestCase)).ToArray(), testRunVisitor, TestFrameworkOptions.ForExecution());
- testRunVisitor.Finished.WaitOne();
- }
- }
- }
+ // Hacky way of using one assembly for now. Will expand later.
+ var assemblyPath = TestCases.Select(x => x.AssemblyFileName).First();
+ var session = this.testUtil.Run(Dispatcher.CurrentDispatcher, assemblyPath);
+ session.TestFinished += OnTestFinished;
}
- private void TestRunVisitor_TestFinished(object sender, TestStateEventArgs e)
+ private void OnTestFinished(object sender, TestResultEventArgs e)
{
+ var testCase = TestCases.Single(x => x.DisplayName == e.TestCaseDisplayName);
+ testCase.State = e.TestState;
+
TestsCompleted++;
- switch (e.State)
+ switch (e.TestState)
{
case TestState.Passed:
TestsPassed++;
@@ -336,9 +288,9 @@ namespace xunit.runner.wpf.ViewModel
break;
}
- if (e.State > CurrentRunState)
+ if (e.TestState > CurrentRunState)
{
- CurrentRunState = e.State;
+ CurrentRunState = e.TestState;
}
}
@@ -379,19 +331,6 @@ namespace xunit.runner.wpf.ViewModel
}
}
- ///
- /// Note: More severe states are higher numbers.
- ///
- ///
- public enum TestState
- {
- All = 0,
- NotRun,
- Passed,
- Skipped,
- Failed,
- }
-
public class TestComparer : IComparer
{
public static TestComparer Instance { get; } = new TestComparer();
diff --git a/xunit.runner.wpf/ViewModel/TestCaseViewModel.cs b/xunit.runner.wpf/ViewModel/TestCaseViewModel.cs
index 5a3e9c7..b83724f 100644
--- a/xunit.runner.wpf/ViewModel/TestCaseViewModel.cs
+++ b/xunit.runner.wpf/ViewModel/TestCaseViewModel.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using xunit.runner.data;
using Xunit.Abstractions;
namespace xunit.runner.wpf.ViewModel
diff --git a/xunit.runner.wpf/ViewModel/TestStateEventArgs.cs b/xunit.runner.wpf/ViewModel/TestStateEventArgs.cs
deleted file mode 100644
index aaf1949..0000000
--- a/xunit.runner.wpf/ViewModel/TestStateEventArgs.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-
-namespace xunit.runner.wpf.ViewModel
-{
- public class TestStateEventArgs : EventArgs
- {
- public static TestStateEventArgs Failed { get; } = new TestStateEventArgs(TestState.Failed);
- public static TestStateEventArgs Passed { get; } = new TestStateEventArgs(TestState.Passed);
- public static TestStateEventArgs Skipped { get; } = new TestStateEventArgs(TestState.Skipped);
- private TestStateEventArgs(TestState state)
- {
- this.State = state;
- }
-
- public TestState State { get; }
- }
-}
\ No newline at end of file
diff --git a/xunit.runner.wpf/xunit.runner.wpf.csproj b/xunit.runner.wpf/xunit.runner.wpf.csproj
index ab60505..c0d145d 100644
--- a/xunit.runner.wpf/xunit.runner.wpf.csproj
+++ b/xunit.runner.wpf/xunit.runner.wpf.csproj
@@ -89,12 +89,13 @@
+
+
-
MSBuild:Compile