diff --git a/xunit.runner.data/TestResultData.cs b/xunit.runner.data/TestResultData.cs
index 7e06f27..fefd0eb 100644
--- a/xunit.runner.data/TestResultData.cs
+++ b/xunit.runner.data/TestResultData.cs
@@ -10,6 +10,7 @@ public enum TestState
{
All = 0,
NotRun,
+ Running,
Passed,
Skipped,
Failed,
diff --git a/xunit.runner.worker/MessageSinks/BaseTestRunSink.cs b/xunit.runner.worker/MessageSinks/BaseTestRunSink.cs
index a99063e..3f3d5a6 100644
--- a/xunit.runner.worker/MessageSinks/BaseTestRunSink.cs
+++ b/xunit.runner.worker/MessageSinks/BaseTestRunSink.cs
@@ -19,6 +19,12 @@ protected override void DisposeCore(bool disposing)
protected override bool OnMessage(IMessageSinkMessage message)
{
+ var testStarted = message as ITestStarting;
+ if (testStarted != null)
+ {
+ OnTestStarted(testStarted);
+ }
+
var testFailed = message as ITestFailed;
if (testFailed != null)
{
@@ -47,6 +53,7 @@ protected override bool OnMessage(IMessageSinkMessage message)
protected virtual bool ShouldContinue => true;
+ protected abstract void OnTestStarted(ITestStarting testStarted);
protected abstract void OnTestFailed(ITestFailed testFailed);
protected abstract void OnTestPassed(ITestPassed testPassed);
protected abstract void OnTestSkipped(ITestSkipped testSkipped);
diff --git a/xunit.runner.worker/RunUtil.cs b/xunit.runner.worker/RunUtil.cs
index 961b292..aab3b60 100644
--- a/xunit.runner.worker/RunUtil.cs
+++ b/xunit.runner.worker/RunUtil.cs
@@ -30,6 +30,11 @@ private void Process(string displayName, string uniqueID, TestState state, strin
_writer.Write(result);
}
+ protected override void OnTestStarted(ITestStarting testStarted)
+ {
+ Process(testStarted.TestCase.DisplayName, testStarted.TestCase.UniqueID, TestState.Running);
+ }
+
protected override void OnTestFailed(ITestFailed testFailed)
{
var displayName = testFailed.TestCase.DisplayName;
diff --git a/xunit.runner.wpf/Artwork/Running_large.png b/xunit.runner.wpf/Artwork/Running_large.png
new file mode 100644
index 0000000..69ccfbc
Binary files /dev/null and b/xunit.runner.wpf/Artwork/Running_large.png differ
diff --git a/xunit.runner.wpf/Artwork/Running_small.png b/xunit.runner.wpf/Artwork/Running_small.png
new file mode 100644
index 0000000..fa7a2da
Binary files /dev/null and b/xunit.runner.wpf/Artwork/Running_small.png differ
diff --git a/xunit.runner.wpf/Converters/TestStateConverter.cs b/xunit.runner.wpf/Converters/TestStateConverter.cs
index 2b0af42..46f1cc3 100644
--- a/xunit.runner.wpf/Converters/TestStateConverter.cs
+++ b/xunit.runner.wpf/Converters/TestStateConverter.cs
@@ -9,6 +9,7 @@ namespace Xunit.Runner.Wpf.Converters
{
public class TestStateConverter : IValueConverter
{
+ private static ImageSource runningSource;
private static ImageSource failedSource;
private static ImageSource passedSource;
private static ImageSource skippedSource;
@@ -17,6 +18,7 @@ public class TestStateConverter : IValueConverter
static TestStateConverter()
{
+ runningSource = LoadResourceImage("Running_small.png");
failedSource = LoadResourceImage("Failed_small.png");
passedSource = LoadResourceImage("Passed_small.png");
skippedSource = LoadResourceImage("Skipped_small.png");
@@ -38,6 +40,8 @@ public object Convert(object value, Type targetType, object parameter, CultureIn
{
switch (state)
{
+ case TestState.Running:
+ return Brushes.Blue;
case TestState.Failed:
return Brushes.Red;
case TestState.Passed:
@@ -52,6 +56,8 @@ public object Convert(object value, Type targetType, object parameter, CultureIn
{
switch (state)
{
+ case TestState.Running:
+ return runningSource;
case TestState.Failed:
return failedSource;
case TestState.Passed:
diff --git a/xunit.runner.wpf/MainWindow.xaml b/xunit.runner.wpf/MainWindow.xaml
index 9b44790..e85a916 100644
--- a/xunit.runner.wpf/MainWindow.xaml
+++ b/xunit.runner.wpf/MainWindow.xaml
@@ -277,6 +277,21 @@
VerticalAlignment="Center" />
+
+
+
+
+
+
+
allTestCaseUniqueIDs = new HashSet();
private readonly ObservableCollection allTestCases = new ObservableCollection();
private readonly TraitCollectionView traitCollectionView = new TraitCollectionView();
+ private readonly HashSet runningTestSet = new HashSet();
+
private CancellationTokenSource filterCancellationTokenSource = new CancellationTokenSource();
private CancellationTokenSource cancellationTokenSource;
@@ -52,7 +54,7 @@ public bool AutoReloadAssemblies
public ObservableCollection RecentAssemblies { get; } = new ObservableCollection();
- private ImmutableList runningTests;
+ private ImmutableList testsToRun;
public ICommand ExitCommand { get; }
public ICommand WindowLoadedCommand { get; }
@@ -153,10 +155,13 @@ private static bool TestCaseMatches(TestCaseViewModel testCase, SearchQuery sear
}
}
- var noFilter = !(searchQuery.FilterFailedTests | searchQuery.FilterPassedTests | searchQuery.FilterSkippedTests);
+ var noFilter = !(searchQuery.FilterRunningTests | searchQuery.FilterFailedTests | searchQuery.FilterPassedTests | searchQuery.FilterSkippedTests);
switch (testCase.State)
{
+ case TestState.Running:
+ return noFilter || searchQuery.FilterRunningTests;
+
case TestState.Passed:
return noFilter || searchQuery.FilterPassedTests;
@@ -263,6 +268,13 @@ private TaskbarProgressBarState GetTaskBarState()
}
}
+ private int testsRunning = 0;
+ public int TestsRunning
+ {
+ get { return testsRunning; }
+ set { Set(ref testsRunning, value); }
+ }
+
private int testsPassed = 0;
public int TestsPassed
{
@@ -560,12 +572,12 @@ private bool CanExecuteRunSelected()
private async void OnExecuteRunAll()
{
- Debug.Assert(this.runningTests == null);
+ Debug.Assert(this.testsToRun == null);
UpdateTestCaseInfo(useSelected: false);
await ExecuteTestSessionOperation(RunFilteredTests);
- this.runningTests = null;
+ this.testsToRun = null;
}
private List RunFilteredTests()
@@ -575,13 +587,13 @@ private List RunFilteredTests()
private async void OnExecuteRunSelected()
{
- Debug.Assert(this.runningTests == null);
+ Debug.Assert(this.testsToRun == null);
Debug.Assert(this.SelectedTestCase != null);
UpdateTestCaseInfo(useSelected: true);
await ExecuteTestSessionOperation(RunSelectedTests);
- this.runningTests = null;
+ this.testsToRun = null;
}
private List RunSelectedTests()
@@ -593,37 +605,38 @@ private List RunTests(ImmutableList tests)
{
Debug.Assert(this.isBusy);
Debug.Assert(this.cancellationTokenSource != null);
- Debug.Assert(this.runningTests == null);
+ Debug.Assert(this.testsToRun == null);
TestsCompleted = 0;
+ TestsRunning = 0;
TestsPassed = 0;
TestsFailed = 0;
TestsSkipped = 0;
CurrentRunState = TestState.NotRun;
Output = string.Empty;
- this.runningTests = tests;
+ this.testsToRun = tests;
- foreach (var tc in this.runningTests)
+ foreach (var tc in this.testsToRun)
{
tc.State = TestState.NotRun;
}
- var runAll = this.runningTests.Count == this.allTestCases.Count;
+ var runAll = this.testsToRun.Count == this.allTestCases.Count;
var testSessionList = new List();
- foreach (var assemblyFileName in this.runningTests.Select(x => x.AssemblyFileName).Distinct())
+ foreach (var assemblyFileName in this.testsToRun.Select(x => x.AssemblyFileName).Distinct())
{
Task task;
if (runAll)
{
- task = this.testUtil.RunAll(assemblyFileName, OnTestsFinished, this.cancellationTokenSource.Token);
+ task = this.testUtil.RunAll(assemblyFileName, OnTestStateChange, this.cancellationTokenSource.Token);
}
else
{
var builder = ImmutableArray.CreateBuilder();
- foreach (var testCase in this.runningTests)
+ foreach (var testCase in this.testsToRun)
{
if (testCase.AssemblyFileName == assemblyFileName)
{
@@ -631,7 +644,7 @@ private List RunTests(ImmutableList tests)
}
}
- task = this.testUtil.RunSpecific(assemblyFileName, builder.ToImmutable(), OnTestsFinished, this.cancellationTokenSource.Token);
+ task = this.testUtil.RunSpecific(assemblyFileName, builder.ToImmutable(), OnTestStateChange, this.cancellationTokenSource.Token);
}
testSessionList.Add(task);
@@ -711,28 +724,43 @@ private void OnTestsDiscovered(IEnumerable testCases)
}
}
- private void OnTestsFinished(IEnumerable testResultData)
+ private void OnTestStateChange(IEnumerable testResultData)
{
- Debug.Assert(this.runningTests != null);
+ Debug.Assert(this.testsToRun != null);
foreach (var result in testResultData)
{
- var testCase = this.runningTests.Single(x => x.UniqueID == result.TestCaseUniqueID);
+ var testCase = this.testsToRun.Single(x => x.UniqueID == result.TestCaseUniqueID);
testCase.State = result.TestState;
- TestsCompleted++;
- switch (result.TestState)
+ if (result.TestState == TestState.Running)
{
- case TestState.Passed:
- TestsPassed++;
- break;
- case TestState.Failed:
- TestsFailed++;
- Output = Output + result.Output;
- break;
- case TestState.Skipped:
- TestsSkipped++;
- break;
+ if (runningTestSet.Add(result.TestCaseUniqueID))
+ {
+ TestsRunning++;
+ }
+ }
+ else
+ {
+ if (runningTestSet.Remove(result.TestCaseUniqueID))
+ {
+ TestsRunning--;
+ }
+
+ TestsCompleted++;
+ switch (result.TestState)
+ {
+ case TestState.Passed:
+ TestsPassed++;
+ break;
+ case TestState.Failed:
+ TestsFailed++;
+ Output = Output + result.Output;
+ break;
+ case TestState.Skipped:
+ TestsSkipped++;
+ break;
+ }
}
if (result.TestState > CurrentRunState)
@@ -820,6 +848,18 @@ private void UpdateAutoReloadStatus()
}
}
+ public bool FilterRunningTests
+ {
+ get { return searchQuery.FilterRunningTests; }
+ set
+ {
+ if (Set(ref searchQuery.FilterRunningTests, value))
+ {
+ FilterAfterDelay();
+ }
+ }
+ }
+
public bool FilterPassedTests
{
get { return searchQuery.FilterPassedTests; }
diff --git a/xunit.runner.wpf/ViewModel/SearchQuery.cs b/xunit.runner.wpf/ViewModel/SearchQuery.cs
index 2105fce..84570c5 100644
--- a/xunit.runner.wpf/ViewModel/SearchQuery.cs
+++ b/xunit.runner.wpf/ViewModel/SearchQuery.cs
@@ -4,6 +4,7 @@ namespace Xunit.Runner.Wpf.ViewModel
{
public class SearchQuery
{
+ public bool FilterRunningTests = false;
public bool FilterFailedTests = false;
public bool FilterPassedTests = false;
public bool FilterSkippedTests = false;
diff --git a/xunit.runner.wpf/xunit.runner.wpf.csproj b/xunit.runner.wpf/xunit.runner.wpf.csproj
index 28ef644..d902b82 100644
--- a/xunit.runner.wpf/xunit.runner.wpf.csproj
+++ b/xunit.runner.wpf/xunit.runner.wpf.csproj
@@ -203,6 +203,12 @@
+
+
+
+
+
+