Skip to content

Commit

Permalink
made wpf xunit runner to support showing/filtering running tests so t…
Browse files Browse the repository at this point in the history
…hat if we get deadlock or hang tests, we can see what the test it was running easily.
  • Loading branch information
heejaechang committed Nov 1, 2017
1 parent 761f801 commit 8c3c86d
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 29 deletions.
1 change: 1 addition & 0 deletions xunit.runner.data/TestResultData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public enum TestState
{
All = 0,
NotRun,
Running,
Passed,
Skipped,
Failed,
Expand Down
7 changes: 7 additions & 0 deletions xunit.runner.worker/MessageSinks/BaseTestRunSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions xunit.runner.worker/RunUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Binary file added xunit.runner.wpf/Artwork/Running_large.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added xunit.runner.wpf/Artwork/Running_small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions xunit.runner.wpf/Converters/TestStateConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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");
Expand All @@ -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:
Expand All @@ -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:
Expand Down
15 changes: 15 additions & 0 deletions xunit.runner.wpf/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,21 @@
VerticalAlignment="Center" />
</StackPanel>
</ToggleButton>

<ToggleButton IsChecked="{Binding FilterRunningTests}"
BorderThickness="0"
Background="Transparent"
Margin="2,4,0,4"
Grid.Column="2"
Command="{Binding TestFilterChanged}">
<StackPanel Orientation="Horizontal">
<Image Source="Artwork\Running_large.png" />
<TextBlock Margin="4,0"
FontSize="16"
Text="{Binding TestsRunning, StringFormat={}{0:#\,0}}"
VerticalAlignment="Center" />
</StackPanel>
</ToggleButton>
</StackPanel>

<ListBox x:Name="TestCases" ItemsSource="{Binding FilteredTestCases}"
Expand Down
98 changes: 69 additions & 29 deletions xunit.runner.wpf/ViewModel/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class MainViewModel : ViewModelBase
private readonly HashSet<string> allTestCaseUniqueIDs = new HashSet<string>();
private readonly ObservableCollection<TestCaseViewModel> allTestCases = new ObservableCollection<TestCaseViewModel>();
private readonly TraitCollectionView traitCollectionView = new TraitCollectionView();
private readonly HashSet<string> runningTestSet = new HashSet<string>();

private CancellationTokenSource filterCancellationTokenSource = new CancellationTokenSource();

private CancellationTokenSource cancellationTokenSource;
Expand All @@ -52,7 +54,7 @@ public bool AutoReloadAssemblies

public ObservableCollection<RecentAssemblyViewModel> RecentAssemblies { get; } = new ObservableCollection<RecentAssemblyViewModel>();

private ImmutableList<TestCaseViewModel> runningTests;
private ImmutableList<TestCaseViewModel> testsToRun;

public ICommand ExitCommand { get; }
public ICommand WindowLoadedCommand { get; }
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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<Task> RunFilteredTests()
Expand All @@ -575,13 +587,13 @@ private List<Task> 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<Task> RunSelectedTests()
Expand All @@ -593,45 +605,46 @@ private List<Task> RunTests(ImmutableList<TestCaseViewModel> 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<Task>();

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<string>();

foreach (var testCase in this.runningTests)
foreach (var testCase in this.testsToRun)
{
if (testCase.AssemblyFileName == assemblyFileName)
{
builder.Add(testCase.UniqueID);
}
}

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);
Expand Down Expand Up @@ -711,28 +724,43 @@ private void OnTestsDiscovered(IEnumerable<TestCaseData> testCases)
}
}

private void OnTestsFinished(IEnumerable<TestResultData> testResultData)
private void OnTestStateChange(IEnumerable<TestResultData> 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)
Expand Down Expand Up @@ -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; }
Expand Down
1 change: 1 addition & 0 deletions xunit.runner.wpf/ViewModel/SearchQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions xunit.runner.wpf/xunit.runner.wpf.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@
<ItemGroup>
<Resource Include="Artwork\Application.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Artwork\Running_large.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Artwork\Running_small.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
Expand Down

0 comments on commit 8c3c86d

Please sign in to comment.