Cancellation for unit test runs re-implemented

This commit is contained in:
Jared Parsons
2015-08-19 13:34:10 -07:00
parent 0d3374d149
commit 6c3a38c8d0
5 changed files with 76 additions and 30 deletions
+12 -1
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using xunit.runner.data;
@@ -20,7 +21,7 @@ namespace xunit.runner.wpf
/// <summary>
/// Begin a run of a unit test for the given assembly.
/// </summary>
ITestRunSession Run(Dispatcher dispatcher, string assemblyPath);
ITestRunSession Run(Dispatcher dispatcher, string assemblyPath, CancellationToken cancellationToken = default(CancellationToken));
}
internal sealed class TestResultEventArgs : EventArgs
@@ -37,6 +38,16 @@ namespace xunit.runner.wpf
internal interface ITestRunSession
{
bool IsRunning { get; }
/// <summary>
/// Raised when an idividual test is finished running.
/// </summary>
event EventHandler<TestResultEventArgs> TestFinished;
/// <summary>
/// Raised when the session has finished executing all of the specified tests.
/// </summary>
event EventHandler SessionFinished;
}
}
@@ -6,6 +6,7 @@ using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using xunit.runner.data;
using xunit.runner.wpf.ViewModel;
@@ -18,11 +19,13 @@ namespace xunit.runner.wpf.Impl
{
private readonly ConcurrentQueue<TestResultData> _resultQueue;
private readonly BinaryReader _reader;
private readonly CancellationToken _cancellationToken;
internal BackgroundRunner(ConcurrentQueue<TestResultData> resultQueue, BinaryReader reader)
internal BackgroundRunner(ConcurrentQueue<TestResultData> resultQueue, BinaryReader reader, CancellationToken cancellationToken)
{
_resultQueue = resultQueue;
_reader = reader;
_cancellationToken = cancellationToken;
}
/// <summary>
@@ -32,7 +35,7 @@ namespace xunit.runner.wpf.Impl
/// <returns></returns>
internal Task GoOnBackground()
{
while (true)
while (!_cancellationToken.IsCancellationRequested)
{
TestResultData result;
try
@@ -24,6 +24,7 @@ namespace xunit.runner.wpf.Impl
private readonly DispatcherTimer _timer;
private bool _closed;
private event EventHandler<TestResultEventArgs> _testFinished;
private event EventHandler _sessionFinished;
internal RunSession(Connection connection, Dispatcher dispatcher, ConcurrentQueue<TestResultData> resultQueue)
{
@@ -58,16 +59,26 @@ namespace xunit.runner.wpf.Impl
_closed = true;
_timer.Stop();
((IDisposable)_connection).Dispose();
_sessionFinished?.Invoke(this, EventArgs.Empty);
}
#region ITestRunSession
bool ITestRunSession.IsRunning => !_closed;
event EventHandler<TestResultEventArgs> ITestRunSession.TestFinished
{
add { _testFinished += value; }
remove { _testFinished -= value; }
}
event EventHandler ITestRunSession.SessionFinished
{
add { _sessionFinished += value; }
remove { _sessionFinished -= value; }
}
#endregion
}
}
+7 -4
View File
@@ -6,6 +6,7 @@ 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;
@@ -53,6 +54,8 @@ namespace xunit.runner.wpf.Impl
var processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = typeof(xunit.runner.worker.Program).Assembly.Location;
processStartInfo.Arguments = $"{action} {argument}";
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
var process = Process.Start(processStartInfo);
try
{
@@ -99,11 +102,11 @@ namespace xunit.runner.wpf.Impl
return list;
}
private RunSession Run(Dispatcher dispatcher, string assemblyPath)
private RunSession Run(Dispatcher dispatcher, string assemblyPath, CancellationToken cancellationToken)
{
var connection = StartWorkerProcess(Constants.ActionRun, assemblyPath);
var queue = new ConcurrentQueue<TestResultData>();
var backgroundRunner = new BackgroundRunner(queue, new BinaryReader(connection.Stream, Constants.Encoding, leaveOpen: true));
var backgroundRunner = new BackgroundRunner(queue, new BinaryReader(connection.Stream, Constants.Encoding, leaveOpen: true), cancellationToken);
Task.Run(backgroundRunner.GoOnBackground);
return new RunSession(connection, dispatcher, queue);
@@ -116,9 +119,9 @@ namespace xunit.runner.wpf.Impl
return Discover(assemblyPath);
}
ITestRunSession ITestUtil.Run(Dispatcher dispatcher, string assemblyPath)
ITestRunSession ITestUtil.Run(Dispatcher dispatcher, string assemblyPath, CancellationToken cancellationToken)
{
return Run(dispatcher, assemblyPath);
return Run(dispatcher, assemblyPath, cancellationToken);
}
#endregion
+41 -23
View File
@@ -25,8 +25,10 @@ namespace xunit.runner.wpf.ViewModel
private readonly ObservableCollection<TestCaseViewModel> allTestCases = new ObservableCollection<TestCaseViewModel>();
private CancellationTokenSource filterCancellationTokenSource = new CancellationTokenSource();
private List<ITestRunSession> testRunSessionList = new List<ITestRunSession>();
private CancellationTokenSource cancellationTokenSource;
private bool isBusy;
private bool isCancelRequested;
public MainViewModel()
{
if (IsInDesignMode)
@@ -205,16 +207,6 @@ namespace xunit.runner.wpf.ViewModel
}
}
private bool IsCancelRequested
{
get { return isCancelRequested; }
set
{
isCancelRequested = value;
CancelCommand.RaiseCanExecuteChanged();
}
}
private static void OnExecuteExit()
{
Application.Current.Shutdown();
@@ -227,7 +219,6 @@ namespace xunit.runner.wpf.ViewModel
{
try
{
IsBusy = true;
TestsCompleted = 0;
TestsPassed = 0;
TestsFailed = 0;
@@ -239,24 +230,47 @@ namespace xunit.runner.wpf.ViewModel
{
MessageBox.Show(ex.ToString());
}
finally
{
IsBusy = false;
IsCancelRequested = false;
}
}
private void RunTests()
{
Debug.Assert(this.cancellationTokenSource == null);
Debug.Assert(this.testRunSessionList.Count == 0);
foreach (var tc in TestCases)
{
tc.State = TestState.NotRun;
}
// 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;
// TODO: Need a way to filter based on traits, selected test cases, etc ... For now we just run
// everything.
this.cancellationTokenSource = new CancellationTokenSource();
foreach (var assemblyPath in TestCases.Select(x => x.AssemblyFileName).Distinct())
{
var session = this.testUtil.Run(Dispatcher.CurrentDispatcher, assemblyPath, this.cancellationTokenSource.Token);
session.TestFinished += OnTestFinished;
session.SessionFinished += delegate { OnTestRunSessionFinished(session); };
this.testRunSessionList.Add(session);
}
this.IsBusy = true;
this.RunCommand.RaiseCanExecuteChanged();
this.CancelCommand.RaiseCanExecuteChanged();
}
private void OnTestRunSessionFinished(ITestRunSession session)
{
Debug.Assert(this.testRunSessionList.Contains(session));
this.testRunSessionList.Remove(session);
if (this.testRunSessionList.Count == 0)
{
this.cancellationTokenSource = null;
this.IsBusy = false;
this.RunCommand.RaiseCanExecuteChanged();
this.CancelCommand.RaiseCanExecuteChanged();
}
}
private void OnTestFinished(object sender, TestResultEventArgs e)
@@ -284,11 +298,15 @@ namespace xunit.runner.wpf.ViewModel
}
}
private bool CanExecuteCancel() => IsBusy && !IsCancelRequested;
private bool CanExecuteCancel()
{
return this.cancellationTokenSource != null && !this.cancellationTokenSource.IsCancellationRequested;
}
private void OnExecuteCancel()
{
this.IsCancelRequested = true;
Debug.Assert(CanExecuteCancel());
this.cancellationTokenSource.Cancel();
}
public bool IsPassedFilterChecked