20 Commits

Author SHA1 Message Date
Kevin Pilch a96ce278d2 Fix nullability warnings 2018-05-05 16:21:00 -07:00
Kevin Pilch a10c13626c Remove compilers package 2018-05-05 15:22:08 -07:00
Kevin Pilch e9dfb23e18 Merge pull request #84 from Eawvv/gui-enhancements
Gui enhancements - Run selected on enter or double click
2018-02-16 09:05:30 -08:00
Edwin Roerdink f6e8918bef execute test on enter key 2018-02-04 01:42:25 +01:00
Edwin Roerdink 18275067e9 execute test on double click 2018-02-04 01:19:04 +01:00
Kevin Pilch 39f89587fc Merge pull request #81 from heejaechang/fixDiscovery
made discovery to use same setting as run so that same tests are disc…
2017-11-01 09:24:29 -07:00
Heejae Chang 8c3c86d918 made wpf xunit runner to support showing/filtering running tests so that if we get deadlock or hang tests, we can see what the test it was running easily. 2017-10-31 17:23:22 -07:00
Heejae Chang 761f801e43 made discovery to use same setting as run so that same tests are discovered. 2017-10-31 14:56:33 -07:00
Kevin Pilch 643d9061c8 Merge pull request #79 from 333fred/fix-autoreload
Fix first open and default autoreload case
2017-06-05 16:17:11 -07:00
Fredric Silberberg 6d4a249a97 Fix first open and default autoreload case 2017-06-05 15:49:35 -07:00
Kevin Pilch 5ed3579b30 Merge pull request #78 from 333fred/screenshot
Added screenshot to readme.
2017-06-05 11:22:21 -07:00
Fredric Silberberg 1f55afd2e9 Added screenshot to readme. 2017-06-05 10:49:13 -07:00
Kevin Pilch c6c48067a7 Merge pull request #77 from 333fred/update-readme
Add nuget to readme
2017-06-05 10:32:28 -07:00
Fredric Silberberg ab5b3afea2 Add nuget to readme 2017-06-05 10:30:00 -07:00
Kevin Pilch feb8588961 Merge pull request #76 from sharwell/open-2017
Open solution in Visual Studio 2017
2017-06-03 14:42:28 -07:00
Kevin Pilch 08907d707c Merge pull request #75 from sharwell/include-binaries
Include binaries in the NuGet package
2017-06-03 14:42:07 -07:00
Sam Harwell de354d8095 Open solution in Visual Studio 2017
Since the project is using features from C# 7, update the solution to open
by default in a version of Visual Studio that supports them.
2017-06-03 16:40:14 -05:00
Sam Harwell dd668f753b Move .targets into a framework-specific folder
Fixes installation via project.json.
2017-06-03 16:38:56 -05:00
Sam Harwell a5712e104a Disable NuGet package analysis since this is a non-standard package layout 2017-06-03 16:30:06 -05:00
Sam Harwell f2e1c0fb5e Include binaries in NuGet package 2017-06-03 16:27:16 -05:00
34 changed files with 355 additions and 245 deletions
+1
View File
@@ -214,3 +214,4 @@ FakesAssemblies/
**/*.Server/GeneratedArtifacts **/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml **/*.Server/ModelManifest.xml
_Pvt_Extensions _Pvt_Extensions
/Settings.XamlStyle
+4
View File
@@ -3,4 +3,8 @@ XUnit Gui written in WPF
A simple replacement for the old winforms xunit.gui with support for xunit 2.0. A simple replacement for the old winforms xunit.gui with support for xunit 2.0.
Find it on NuGet at [xunit.runner.wpf](https://www.nuget.org/packages/xunit.runner.wpf).
![Screenshot](docs/screenshot.png)
[![Build status](https://ci.appveyor.com/api/projects/status/13dshnyj592mwe9e/branch/master?svg=true)](https://ci.appveyor.com/project/Pilchie/xunit-runner-wpf/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/13dshnyj592mwe9e/branch/master?svg=true)](https://ci.appveyor.com/project/Pilchie/xunit-runner-wpf/branch/master)
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -66,12 +65,6 @@
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
-1
View File
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Microsoft.Net.Compilers" version="2.2.0" targetFramework="net452" developmentDependency="true" />
<package id="xunit" version="2.1.0" targetFramework="net452" /> <package id="xunit" version="2.1.0" targetFramework="net452" />
<package id="xunit.abstractions" version="2.0.0" targetFramework="net452" /> <package id="xunit.abstractions" version="2.0.0" targetFramework="net452" />
<package id="xunit.assert" version="2.1.0" targetFramework="net452" /> <package id="xunit.assert" version="2.1.0" targetFramework="net452" />
Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

+1 -1
View File
@@ -7,7 +7,7 @@ namespace Xunit.Runner.Data
{ {
private readonly BinaryReader _reader; private readonly BinaryReader _reader;
private bool _closed; private bool _closed;
private Exception _exception; private Exception? _exception;
public bool IsConnected => !_closed; public bool IsConnected => !_closed;
+1
View File
@@ -10,6 +10,7 @@ namespace Xunit.Runner.Data
{ {
All = 0, All = 0,
NotRun, NotRun,
Running,
Passed, Passed,
Skipped, Skipped,
Failed, Failed,
@@ -48,11 +48,6 @@
<Compile Include="TestDataKind.cs" /> <Compile Include="TestDataKind.cs" />
<Compile Include="TestResultData.cs" /> <Compile Include="TestResultData.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Net.Compilers">
<Version>2.2.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
+1 -1
View File
@@ -35,7 +35,7 @@ namespace Xunit.Runner.Worker
internal static void Go(string assemblyFileName, Stream stream) internal static void Go(string assemblyFileName, Stream stream)
{ {
Go(assemblyFileName, stream, AppDomainSupport.Denied, Go(assemblyFileName, stream, AppDomainSupport.IfAvailable,
(xunit, configuration, writer) => (xunit, configuration, writer) =>
{ {
using (var sink = new TestDiscoverySink(writer)) using (var sink = new TestDiscoverySink(writer))
@@ -19,6 +19,12 @@ namespace Xunit.Runner.Worker.MessageSinks
protected override bool OnMessage(IMessageSinkMessage message) protected override bool OnMessage(IMessageSinkMessage message)
{ {
var testStarted = message as ITestStarting;
if (testStarted != null)
{
OnTestStarted(testStarted);
}
var testFailed = message as ITestFailed; var testFailed = message as ITestFailed;
if (testFailed != null) if (testFailed != null)
{ {
@@ -47,6 +53,7 @@ namespace Xunit.Runner.Worker.MessageSinks
protected virtual bool ShouldContinue => true; protected virtual bool ShouldContinue => true;
protected abstract void OnTestStarted(ITestStarting testStarted);
protected abstract void OnTestFailed(ITestFailed testFailed); protected abstract void OnTestFailed(ITestFailed testFailed);
protected abstract void OnTestPassed(ITestPassed testPassed); protected abstract void OnTestPassed(ITestPassed testPassed);
protected abstract void OnTestSkipped(ITestSkipped testSkipped); protected abstract void OnTestSkipped(ITestSkipped testSkipped);
+6 -1
View File
@@ -23,13 +23,18 @@ namespace Xunit.Runner.Worker
private void Process(string displayName, string uniqueID, TestState state, string output = "") private void Process(string displayName, string uniqueID, TestState state, string output = "")
{ {
Console.WriteLine($"{state} - {displayName}"); System.Diagnostics.Trace.WriteLine($"{state} - {displayName}");
var result = new TestResultData(displayName, uniqueID, state, output); var result = new TestResultData(displayName, uniqueID, state, output);
_writer.Write(TestDataKind.Value); _writer.Write(TestDataKind.Value);
_writer.Write(result); _writer.Write(result);
} }
protected override void OnTestStarted(ITestStarting testStarted)
{
Process(testStarted.TestCase.DisplayName, testStarted.TestCase.UniqueID, TestState.Running);
}
protected override void OnTestFailed(ITestFailed testFailed) protected override void OnTestFailed(ITestFailed testFailed)
{ {
var displayName = testFailed.TestCase.DisplayName; var displayName = testFailed.TestCase.DisplayName;
-1
View File
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Microsoft.Net.Compilers" version="2.2.0" targetFramework="net46" developmentDependency="true" />
<package id="xunit.abstractions" version="2.0.0" targetFramework="net452" /> <package id="xunit.abstractions" version="2.0.0" targetFramework="net452" />
<package id="xunit.runner.utility" version="2.1.0" targetFramework="net46" /> <package id="xunit.runner.utility" version="2.1.0" targetFramework="net46" />
</packages> </packages>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -77,12 +76,6 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
+2 -2
View File
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14 # Visual Studio 15
VisualStudioVersion = 14.0.23107.0 VisualStudioVersion = 15.0.26430.4
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xunit.runner.wpf", "xunit.runner.wpf\xunit.runner.wpf.csproj", "{34FB519C-FB49-4B31-ACA2-7F7879311BCF}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xunit.runner.wpf", "xunit.runner.wpf\xunit.runner.wpf.csproj", "{34FB519C-FB49-4B31-ACA2-7F7879311BCF}"
EndProject EndProject
Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

+3 -5
View File
@@ -16,16 +16,14 @@ namespace Xunit.Runner.Wpf
} }
} }
public static CommandBindingCollection GetRegistration(UIElement element) public static CommandBindingCollection? GetRegistration(UIElement element)
=> (element != null ? (CommandBindingCollection)element.GetValue(Registration) : null); => (element != null ? (CommandBindingCollection)element.GetValue(Registration) : null);
private static void OnRegistrationChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) private static void OnRegistrationChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{ {
UIElement element = sender as UIElement; if (sender is UIElement element)
if (element != null)
{ {
CommandBindingCollection bindings = e.NewValue as CommandBindingCollection; if (e.NewValue is CommandBindingCollection bindings)
if (bindings != null)
{ {
element.CommandBindings.AddRange(bindings); element.CommandBindings.AddRange(bindings);
} }
@@ -9,6 +9,7 @@ namespace Xunit.Runner.Wpf.Converters
{ {
public class TestStateConverter : IValueConverter public class TestStateConverter : IValueConverter
{ {
private static ImageSource runningSource;
private static ImageSource failedSource; private static ImageSource failedSource;
private static ImageSource passedSource; private static ImageSource passedSource;
private static ImageSource skippedSource; private static ImageSource skippedSource;
@@ -17,6 +18,7 @@ namespace Xunit.Runner.Wpf.Converters
static TestStateConverter() static TestStateConverter()
{ {
runningSource = LoadResourceImage("Running_small.png");
failedSource = LoadResourceImage("Failed_small.png"); failedSource = LoadResourceImage("Failed_small.png");
passedSource = LoadResourceImage("Passed_small.png"); passedSource = LoadResourceImage("Passed_small.png");
skippedSource = LoadResourceImage("Skipped_small.png"); skippedSource = LoadResourceImage("Skipped_small.png");
@@ -31,13 +33,15 @@ namespace Xunit.Runner.Wpf.Converters
return image; return image;
} }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
{ {
var state = (TestState)value; var state = (TestState)value;
if (targetType == typeof(Brush)) if (targetType == typeof(Brush))
{ {
switch (state) switch (state)
{ {
case TestState.Running:
return Brushes.Blue;
case TestState.Failed: case TestState.Failed:
return Brushes.Red; return Brushes.Red;
case TestState.Passed: case TestState.Passed:
@@ -52,6 +56,8 @@ namespace Xunit.Runner.Wpf.Converters
{ {
switch (state) switch (state)
{ {
case TestState.Running:
return runningSource;
case TestState.Failed: case TestState.Failed:
return failedSource; return failedSource;
case TestState.Passed: case TestState.Passed:
+7 -1
View File
@@ -283,7 +283,13 @@ namespace Xunit.Runner.Wpf
object IList.this[int index] object IList.this[int index]
{ {
get { return this[index]; } get
{
// Can't figure out how to not get a warning here.
#pragma warning disable CS8603
return this[index];
#pragma warning restore
}
set { throw new NotSupportedException(); } set { throw new NotSupportedException(); }
} }
@@ -55,13 +55,13 @@ namespace Xunit.Runner.Wpf.Impl
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
private sealed class BackgroundReader<T> where T : class private sealed class BackgroundReader<T> where T : class
{ {
private readonly ConcurrentQueue<T> _queue; private readonly ConcurrentQueue<T?> _queue;
private readonly ClientReader _reader; private readonly ClientReader _reader;
private readonly Func<ClientReader, T> _readValue; private readonly Func<ClientReader, T> _readValue;
internal ClientReader Reader => _reader; internal ClientReader Reader => _reader;
internal BackgroundReader(ConcurrentQueue<T> queue, ClientReader reader, Func<ClientReader, T> readValue) internal BackgroundReader(ConcurrentQueue<T?> queue, ClientReader reader, Func<ClientReader, T> readValue)
{ {
_queue = queue; _queue = queue;
_reader = reader; _reader = reader;
@@ -111,7 +111,7 @@ namespace Xunit.Runner.Wpf.Impl
private const int MaxResultPerTick = 1000; private const int MaxResultPerTick = 1000;
private readonly Connection _connection; private readonly Connection _connection;
private readonly ConcurrentQueue<T> _queue; private readonly ConcurrentQueue<T?> _queue;
private readonly DispatcherTimer _timer; private readonly DispatcherTimer _timer;
private readonly Action<List<T>> _callback; private readonly Action<List<T>> _callback;
private readonly int _maxPerTick; private readonly int _maxPerTick;
@@ -122,7 +122,7 @@ namespace Xunit.Runner.Wpf.Impl
internal BackgroundProducer( internal BackgroundProducer(
Connection connection, Connection connection,
Dispatcher dispatcher, Dispatcher dispatcher,
ConcurrentQueue<T> queue, ConcurrentQueue<T?> queue,
Action<List<T>> callback, Action<List<T>> callback,
int maxResultPerTick = MaxResultPerTick, int maxResultPerTick = MaxResultPerTick,
TimeSpan? interval = null) TimeSpan? interval = null)
@@ -144,8 +144,7 @@ namespace Xunit.Runner.Wpf.Impl
var i = 0; var i = 0;
var list = new List<T>(); var list = new List<T>();
var isDone = false; var isDone = false;
T value; while (i < _maxPerTick && _queue.TryDequeue(out T? value))
while (i < _maxPerTick && _queue.TryDequeue(out value))
{ {
if (value == null) if (value == null)
{ {
@@ -8,10 +8,10 @@ namespace Xunit.Runner.Wpf.Impl
{ {
private sealed class Connection : IDisposable private sealed class Connection : IDisposable
{ {
private NamedPipeClientStream _stream; private NamedPipeClientStream? _stream;
private ClientReader _reader; private ClientReader _reader;
internal NamedPipeClientStream Stream => _stream; internal NamedPipeClientStream Stream => _stream ?? throw new ObjectDisposedException(nameof(Connection));
internal ClientReader Reader => _reader; internal ClientReader Reader => _reader;
+1 -1
View File
@@ -123,7 +123,7 @@ namespace Xunit.Runner.Wpf.Impl
private async Task ProcessResultsCore<T>(Connection connection, Func<ClientReader, T> readValue, Action<List<T>> callback, CancellationToken cancellationToken) private async Task ProcessResultsCore<T>(Connection connection, Func<ClientReader, T> readValue, Action<List<T>> callback, CancellationToken cancellationToken)
where T : class where T : class
{ {
var queue = new ConcurrentQueue<T>(); var queue = new ConcurrentQueue<T?>();
var backgroundReader = new BackgroundReader<T>(queue, new ClientReader(connection.Stream), readValue); var backgroundReader = new BackgroundReader<T>(queue, new ClientReader(connection.Stream), readValue);
var backgroundProducer = new BackgroundProducer<T>(connection, _dispatcher, queue, callback); var backgroundProducer = new BackgroundProducer<T>(connection, _dispatcher, queue, callback);
+1 -1
View File
@@ -14,7 +14,7 @@ namespace Xunit.Runner.Wpf.Impl
private readonly IDictionary<string, FileSystemWatcher> watchedAssemblies = new Dictionary<string, FileSystemWatcher>(); private readonly IDictionary<string, FileSystemWatcher> watchedAssemblies = new Dictionary<string, FileSystemWatcher>();
private readonly Dispatcher dispatcher; private readonly Dispatcher dispatcher;
private bool isEnabled = false; private bool isEnabled = false;
private ReloadDebouncer debouncer; private ReloadDebouncer? debouncer;
public TestAssemblyWatcher(Dispatcher dispatcher) public TestAssemblyWatcher(Dispatcher dispatcher)
{ {
+198 -151
View File
@@ -1,24 +1,25 @@
<Window x:Class="Xunit.Runner.Wpf.MainWindow" <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" x:Class="Xunit.Runner.Wpf.MainWindow"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform"
xmlns:local="clr-namespace:Xunit.Runner.Wpf" xmlns:converters="clr-namespace:Xunit.Runner.Wpf.Converters"
xmlns:converters="clr-namespace:Xunit.Runner.Wpf.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:Xunit.Runner.Wpf.ViewModel" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:Xunit.Runner.Wpf"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" xmlns:vm="clr-namespace:Xunit.Runner.Wpf.ViewModel"
DataContext="{Binding Main, Source={StaticResource Locator}}" Name="Main"
Title="xUnit.net Test Runner" Title="xUnit.net Test Runner"
Icon="Artwork\Application.ico" Width="525"
ResizeMode="CanResizeWithGrip" Height="600"
Height="600" MinWidth="425"
MinHeight="525" MinHeight="525"
Width="525" DataContext="{Binding Main, Source={StaticResource Locator}}"
MinWidth="425" Icon="Artwork\Application.ico"
Name="Main"> ResizeMode="CanResizeWithGrip"
mc:Ignorable="d">
<i:Interaction.Triggers> <i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded"> <i:EventTrigger EventName="Loaded">
@@ -43,12 +44,10 @@
<Menu Grid.Row="0"> <Menu Grid.Row="0">
<MenuItem Header="_File"> <MenuItem Header="_File">
<MenuItem Header="E_xit" <MenuItem Command="{Binding ExitCommand}" Header="E_xit" />
Command="{Binding ExitCommand}" />
</MenuItem> </MenuItem>
<MenuItem Header="_Assembly"> <MenuItem Header="_Assembly">
<MenuItem Header="_Open" <MenuItem Command="ApplicationCommands.Open" Header="_Open" />
Command="ApplicationCommands.Open" />
<MenuItem Header="R_ecent" ItemsSource="{Binding RecentAssemblies}"> <MenuItem Header="R_ecent" ItemsSource="{Binding RecentAssemblies}">
<MenuItem.ItemContainerStyle> <MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem"> <Style TargetType="MenuItem">
@@ -59,10 +58,14 @@
</MenuItem.ItemContainerStyle> </MenuItem.ItemContainerStyle>
</MenuItem> </MenuItem>
<Separator /> <Separator />
<MenuItem Header="_Unload" Command="{Binding AssemblyRemoveAllCommand}"/> <MenuItem Command="{Binding AssemblyRemoveAllCommand}" Header="_Unload" />
<MenuItem Header="_Reload" Command="{Binding AssemblyReloadAllCommand}" /> <MenuItem Command="{Binding AssemblyReloadAllCommand}" Header="_Reload" />
<Separator /> <Separator />
<MenuItem Header="_Auto Reload Test Assemblies" IsCheckable="True" IsChecked="{Binding AutoReloadAssemblies}" Command="{Binding AutoReloadAssembliesCommand}" /> <MenuItem
Command="{Binding AutoReloadAssembliesCommand}"
Header="_Auto Reload Test Assemblies"
IsCheckable="True"
IsChecked="{Binding AutoReloadAssemblies}" />
</MenuItem> </MenuItem>
<MenuItem Header="_Project"> <MenuItem Header="_Project">
<MenuItem Header="_Open" /> <MenuItem Header="_Open" />
@@ -86,10 +89,11 @@
<RowDefinition Height="auto" /> <RowDefinition Height="auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<GroupBox Header="Refinements" <GroupBox
Margin="3" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Grid.Row="0"> Margin="3"
Header="Refinements">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto" /> <RowDefinition Height="auto" />
@@ -100,25 +104,24 @@
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Label Content="Search:" <Label Grid.Row="0" Content="Search:" />
Grid.Row="0" /> <TextBox Grid.Row="1" Text="{Binding FilterString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Grid.Row="1"
Text="{Binding FilterString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Label Content="Assemblies:" <Label Grid.Row="2" Content="Assemblies:" />
Grid.Row="2" /> <ListBox
<ListBox Height="175" Grid.Row="3"
ItemsSource="{Binding Assemblies}" Height="175"
SelectionMode="Extended" ItemsSource="{Binding Assemblies}"
Grid.Row="3"> SelectionMode="Extended">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate DataType="vm:TestAssemblyViewModel"> <DataTemplate DataType="vm:TestAssemblyViewModel">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DisplayName}" /> <TextBlock Text="{Binding DisplayName}" />
<TextBlock Text=" Discovering tests..." <TextBlock
FontStyle="Italic" FontStyle="Italic"
Foreground="Gray"> Foreground="Gray"
Text=" Discovering tests...">
<TextBlock.Style> <TextBlock.Style>
<Style TargetType="TextBlock"> <Style TargetType="TextBlock">
@@ -138,41 +141,36 @@
<ListBox.ItemContainerStyle> <ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}"> <Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/> <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}" />
</Style> </Style>
</ListBox.ItemContainerStyle> </ListBox.ItemContainerStyle>
<ListBox.ContextMenu> <ListBox.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="Reload" Command="{Binding AssemblyReloadCommand}" /> <MenuItem Command="{Binding AssemblyReloadCommand}" Header="Reload" />
<MenuItem Header="Reload All" Command="{Binding AssemblyReloadAllCommand}" /> <MenuItem Command="{Binding AssemblyReloadAllCommand}" Header="Reload All" />
<Separator /> <Separator />
<MenuItem Header="Remove" Command="{Binding AssemblyRemoveCommand}" /> <MenuItem Command="{Binding AssemblyRemoveCommand}" Header="Remove" />
<MenuItem Header="Remove All" Command="{Binding AssemblyRemoveAllCommand}" /> <MenuItem Command="{Binding AssemblyRemoveAllCommand}" Header="Remove All" />
</ContextMenu> </ContextMenu>
</ListBox.ContextMenu> </ListBox.ContextMenu>
</ListBox> </ListBox>
<Label Content="Traits:" <Label Grid.Row="4" Content="Traits:" />
Grid.Row="4" />
<TreeView Grid.Row="5" <TreeView Grid.Row="5" ItemsSource="{Binding Traits}">
ItemsSource="{Binding Traits}">
<TreeView.Resources> <TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type vm:TraitViewModel}" <HierarchicalDataTemplate DataType="{x:Type vm:TraitViewModel}" ItemsSource="{Binding Children}">
ItemsSource="{Binding Children}">
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Text}" > <CheckBox Content="{Binding Text}" IsChecked="{Binding IsChecked}">
<i:Interaction.Triggers> <i:Interaction.Triggers>
<i:EventTrigger EventName="Checked"> <i:EventTrigger EventName="Checked">
<cmd:EventToCommand Command="{Binding DataContext.TraitCheckedChangedCommand, RelativeSource={RelativeSource AncestorType=Window}}" <cmd:EventToCommand Command="{Binding DataContext.TraitCheckedChangedCommand, RelativeSource={RelativeSource AncestorType=Window}}" CommandParameter="{Binding}" />
CommandParameter="{Binding}" />
</i:EventTrigger> </i:EventTrigger>
<i:EventTrigger EventName="Unchecked"> <i:EventTrigger EventName="Unchecked">
<cmd:EventToCommand Command="{Binding DataContext.TraitCheckedChangedCommand, RelativeSource={RelativeSource AncestorType=Window}}" <cmd:EventToCommand Command="{Binding DataContext.TraitCheckedChangedCommand, RelativeSource={RelativeSource AncestorType=Window}}" CommandParameter="{Binding}" />
CommandParameter="{Binding}" />
</i:EventTrigger> </i:EventTrigger>
</i:Interaction.Triggers> </i:Interaction.Triggers>
</CheckBox> </CheckBox>
@@ -188,7 +186,7 @@
<TreeView.ContextMenu> <TreeView.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="Clear" Command="{Binding TraitsClearCommand}" /> <MenuItem Command="{Binding TraitsClearCommand}" Header="Clear" />
</ContextMenu> </ContextMenu>
</TreeView.ContextMenu> </TreeView.ContextMenu>
@@ -196,36 +194,40 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<Grid Grid.Column="0" <Grid Grid.Row="1" Grid.Column="0">
Grid.Row="1">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Content="_Run All" <Button
Command="{Binding RunAllCommand}" Grid.Column="0"
Grid.Column="0" Margin="10,0,0,0"/> Margin="10,0,0,0"
<Button Content="Run _Selected" Command="{Binding RunAllCommand}"
Command="{Binding RunSelectedCommand}" Content="_Run All" />
Grid.Column="1"/> <Button
<Button Content="_Cancel" Grid.Column="1"
Command="{Binding CancelCommand}" Command="{Binding RunSelectedCommand}"
Grid.Column="2" Margin="0,0,10,0" /> Content="Run _Selected" />
<Button
Grid.Column="2"
Margin="0,0,10,0"
Command="{Binding CancelCommand}"
Content="_Cancel" />
</Grid> </Grid>
<Grid Grid.Column="1" <Grid Grid.Row="0" Grid.Column="1">
Grid.Row="0">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="200px" /> <RowDefinition Height="*" MinHeight="200px" />
<RowDefinition Height="auto" /> <RowDefinition Height="auto" />
<RowDefinition Height="*" MinHeight="200px"/> <RowDefinition Height="*" MinHeight="200px" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<GroupBox Header="{Binding TestCasesCaption}" <GroupBox
Margin="3" Grid.Row="0"
Grid.Row="0"> Margin="3"
Header="{Binding TestCasesCaption}">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto" /> <RowDefinition Height="auto" />
@@ -233,56 +235,82 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<ToggleButton IsChecked="{Binding FilterPassedTests}" <ToggleButton
BorderThickness="0" Grid.Column="0"
Background="Transparent" Margin="0,4,2,4"
Margin="0,4,2,4" Background="Transparent"
Grid.Column="0" BorderThickness="0"
Command="{Binding TestFilterChanged}"> Command="{Binding TestFilterChanged}"
IsChecked="{Binding FilterPassedTests}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="Artwork\Passed_large.png" /> <Image Source="Artwork\Passed_large.png" />
<TextBlock Margin="4,0" <TextBlock
FontSize="16" Margin="4,0"
Text="{Binding TestsPassed, StringFormat={}{0:#\,0}}" VerticalAlignment="Center"
VerticalAlignment="Center" /> FontSize="16"
Text="{Binding TestsPassed, StringFormat={}{0:#\,0}}" />
</StackPanel> </StackPanel>
</ToggleButton> </ToggleButton>
<ToggleButton IsChecked="{Binding FilterFailedTests}" <ToggleButton
BorderThickness="0" Grid.Column="1"
Background="Transparent" Margin="2,4"
Margin="2,4" Background="Transparent"
Grid.Column="1" BorderThickness="0"
Command="{Binding TestFilterChanged}"> Command="{Binding TestFilterChanged}"
IsChecked="{Binding FilterFailedTests}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="Artwork\Failed_large.png" /> <Image Source="Artwork\Failed_large.png" />
<TextBlock Margin="4,0" <TextBlock
FontSize="16" Margin="4,0"
Text="{Binding TestsFailed, StringFormat={}{0:#\,0}}" VerticalAlignment="Center"
VerticalAlignment="Center" /> FontSize="16"
Text="{Binding TestsFailed, StringFormat={}{0:#\,0}}" />
</StackPanel> </StackPanel>
</ToggleButton> </ToggleButton>
<ToggleButton IsChecked="{Binding FilterSkippedTests}" <ToggleButton
BorderThickness="0" Grid.Column="2"
Background="Transparent" Margin="2,4,0,4"
Margin="2,4,0,4" Background="Transparent"
Grid.Column="2" BorderThickness="0"
Command="{Binding TestFilterChanged}"> Command="{Binding TestFilterChanged}"
IsChecked="{Binding FilterSkippedTests}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="Artwork\Skipped_large.png" /> <Image Source="Artwork\Skipped_large.png" />
<TextBlock Margin="4,0" <TextBlock
FontSize="16" Margin="4,0"
Text="{Binding TestsSkipped, StringFormat={}{0:#\,0}}" VerticalAlignment="Center"
VerticalAlignment="Center" /> FontSize="16"
Text="{Binding TestsSkipped, StringFormat={}{0:#\,0}}" />
</StackPanel>
</ToggleButton>
<ToggleButton
Grid.Column="2"
Margin="2,4,0,4"
Background="Transparent"
BorderThickness="0"
Command="{Binding TestFilterChanged}"
IsChecked="{Binding FilterRunningTests}">
<StackPanel Orientation="Horizontal">
<Image Source="Artwork\Running_large.png" />
<TextBlock
Margin="4,0"
VerticalAlignment="Center"
FontSize="16"
Text="{Binding TestsRunning, StringFormat={}{0:#\,0}}" />
</StackPanel> </StackPanel>
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<ListBox x:Name="TestCases" ItemsSource="{Binding FilteredTestCases}" <ListBox
SelectionMode="Extended" x:Name="TestCases"
SelectedItem="{Binding SelectedTestCase, Mode=TwoWay}" Grid.Row="1"
Grid.Row="1" SelectionChanged="TestCases_SelectionChanged"> ItemsSource="{Binding FilteredTestCases}"
SelectedItem="{Binding SelectedTestCase, Mode=TwoWay}"
SelectionChanged="TestCases_SelectionChanged"
SelectionMode="Extended">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate DataType="vm:TestCaseViewModel"> <DataTemplate DataType="vm:TestCaseViewModel">
<Grid> <Grid>
@@ -291,49 +319,71 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image Width="16" <Image
Margin="0,0,2,0" Grid.Column="0"
Source="{Binding Path=State, Mode=OneWay, Converter={StaticResource TestStateConverter}}" Width="16"
Grid.Column="0" /> Margin="0,0,2,0"
Source="{Binding Path=State, Mode=OneWay, Converter={StaticResource TestStateConverter}}" />
<TextBlock Text="{Binding DisplayName}" <TextBlock
VerticalAlignment="Center" Grid.Column="1"
Grid.Column="1" /> VerticalAlignment="Center"
Text="{Binding DisplayName}" />
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
<ListBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding RunSelectedCommand}" />
</ListBox.InputBindings>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<cmd:EventToCommand Command="{Binding Path=RunSelectedCommand}" PassEventArgsToCommand="False" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox> </ListBox>
</Grid> </Grid>
</GroupBox> </GroupBox>
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" Height="3" Background="White"/> <GridSplitter
Grid.Row="1"
Height="3"
HorizontalAlignment="Stretch"
Background="White" />
<GroupBox Header="Output" <GroupBox
Margin="3" Grid.Row="2"
Grid.Row="2"> Margin="3"
<TextBox IsReadOnly="True" Header="Output">
HorizontalScrollBarVisibility="Auto" <TextBox
VerticalScrollBarVisibility="Visible" FontFamily="Consolas"
FontFamily="Consolas" HorizontalScrollBarVisibility="Auto"
Text="{Binding Output}"/> IsReadOnly="True"
Text="{Binding Output}"
VerticalScrollBarVisibility="Visible" />
</GroupBox> </GroupBox>
</Grid> </Grid>
<GridSplitter Grid.Column="0" VerticalAlignment="Stretch" Width="3" Background="White"/> <GridSplitter
Grid.Column="0"
Width="3"
VerticalAlignment="Stretch"
Background="White" />
<ProgressBar Foreground="{Binding Path=CurrentRunState, Converter={StaticResource TestStateConverter}, Mode=OneWay}" <ProgressBar
Minimum="0" Grid.Row="1"
Maximum="{Binding MaximumProgress}" Grid.Column="1"
Value="{Binding Path=TestsCompleted}" Margin="3"
Grid.Column="1" Foreground="{Binding Path=CurrentRunState, Converter={StaticResource TestStateConverter}, Mode=OneWay}"
Grid.Row="1" Maximum="{Binding MaximumProgress}"
Margin="3" /> Minimum="0"
Value="{Binding Path=TestsCompleted}" />
</Grid> </Grid>
<StackPanel Grid.Row="2"> <StackPanel Grid.Row="2">
<Border BorderBrush="LightGray" <Border
BorderThickness="1" Margin="3"
Margin="3" /> BorderBrush="LightGray"
BorderThickness="1" />
<StatusBar> <StatusBar>
<StatusBar.ItemsPanel> <StatusBar.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
@@ -343,7 +393,7 @@
<ColumnDefinition Width="auto" /> <ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" /> <ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" /> <ColumnDefinition Width="auto" />
<ColumnDefinition Width="16"/> <ColumnDefinition Width="16" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
</Grid> </Grid>
</ItemsPanelTemplate> </ItemsPanelTemplate>
@@ -351,28 +401,25 @@
<StatusBarItem Grid.Column="1"> <StatusBarItem Grid.Column="1">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="Tests passed: " <TextBlock Foreground="DarkGreen" Text="Tests passed: " />
Foreground="DarkGreen"/>
<TextBlock Text="{Binding TestsPassed, StringFormat={}{0:#\,0}}"/> <TextBlock Text="{Binding TestsPassed, StringFormat={}{0:#\,0}}" />
</StackPanel> </StackPanel>
</StatusBarItem> </StatusBarItem>
<StatusBarItem Grid.Column="2"> <StatusBarItem Grid.Column="2">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="Tests failed: " <TextBlock Foreground="DarkRed" Text="Tests failed: " />
Foreground="DarkRed"/>
<TextBlock Text="{Binding TestsFailed, StringFormat={}{0:#\,0}}"/> <TextBlock Text="{Binding TestsFailed, StringFormat={}{0:#\,0}}" />
</StackPanel> </StackPanel>
</StatusBarItem> </StatusBarItem>
<StatusBarItem Grid.Column="3"> <StatusBarItem Grid.Column="3">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="Tests skipped: " <TextBlock Foreground="DarkGoldenrod" Text="Tests skipped: " />
Foreground="DarkGoldenrod"/>
<TextBlock Text="{Binding TestsSkipped, StringFormat={}{0:#\,0}}"/> <TextBlock Text="{Binding TestsSkipped, StringFormat={}{0:#\,0}}" />
</StackPanel> </StackPanel>
</StatusBarItem> </StatusBarItem>
</StatusBar> </StatusBar>
+3
View File
@@ -11,12 +11,15 @@ namespace Xunit.Runner.Wpf
{ {
public static Window Instance { get; private set; } public static Window Instance { get; private set; }
// WPF generates fields that are marked as non-nullable, but not definitely initialized.
#pragma warning disable CS8618
public MainWindow() public MainWindow()
{ {
Instance = this; Instance = this;
InitializeComponent(); InitializeComponent();
} }
#pragma warning restore
protected override void OnSourceInitialized(EventArgs e) protected override void OnSourceInitialized(EventArgs e)
{ {
+4
View File
@@ -125,6 +125,10 @@ namespace Xunit.Runner.Wpf.Persistence
settings.autoReloadAssemblies = autoReloadAssemblies; settings.autoReloadAssemblies = autoReloadAssemblies;
} }
else
{
settings.autoReloadAssemblies = true;
}
return settings; return settings;
} }
+1 -1
View File
@@ -16,7 +16,7 @@ namespace Xunit.Runner.Wpf.Persistence
private static IsolatedStorageFile GetStorageFile() => IsolatedStorageFile.GetUserStoreForDomain(); private static IsolatedStorageFile GetStorageFile() => IsolatedStorageFile.GetUserStoreForDomain();
public static XmlTextReader OpenXmlFile(string fileName) public static XmlTextReader? OpenXmlFile(string fileName)
{ {
var storage = GetStorageFile(); var storage = GetStorageFile();
if (!storage.FileExists(fileName)) if (!storage.FileExists(fileName))
@@ -5,9 +5,9 @@ namespace Xunit.Runner.Wpf.ViewModel
public class AssemblyAndConfigFile public class AssemblyAndConfigFile
{ {
public string AssemblyFileName { get; } public string AssemblyFileName { get; }
public string ConfigFileName { get; } public string? ConfigFileName { get; }
public AssemblyAndConfigFile(string assemblyFileName, string configFileName) public AssemblyAndConfigFile(string assemblyFileName, string? configFileName)
{ {
this.AssemblyFileName = Path.GetFullPath(assemblyFileName); this.AssemblyFileName = Path.GetFullPath(assemblyFileName);
if (configFileName != null) if (configFileName != null)
+84 -39
View File
@@ -29,9 +29,11 @@ namespace Xunit.Runner.Wpf.ViewModel
private readonly HashSet<string> allTestCaseUniqueIDs = new HashSet<string>(); private readonly HashSet<string> allTestCaseUniqueIDs = new HashSet<string>();
private readonly ObservableCollection<TestCaseViewModel> allTestCases = new ObservableCollection<TestCaseViewModel>(); private readonly ObservableCollection<TestCaseViewModel> allTestCases = new ObservableCollection<TestCaseViewModel>();
private readonly TraitCollectionView traitCollectionView = new TraitCollectionView(); private readonly TraitCollectionView traitCollectionView = new TraitCollectionView();
private readonly HashSet<string> runningTestSet = new HashSet<string>();
private CancellationTokenSource filterCancellationTokenSource = new CancellationTokenSource(); private CancellationTokenSource filterCancellationTokenSource = new CancellationTokenSource();
private CancellationTokenSource cancellationTokenSource; private CancellationTokenSource? cancellationTokenSource;
private bool isBusy; private bool isBusy;
private SearchQuery searchQuery = new SearchQuery(); private SearchQuery searchQuery = new SearchQuery();
private bool autoReloadAssemblies; private bool autoReloadAssemblies;
@@ -52,7 +54,7 @@ namespace Xunit.Runner.Wpf.ViewModel
public ObservableCollection<RecentAssemblyViewModel> RecentAssemblies { get; } = new ObservableCollection<RecentAssemblyViewModel>(); public ObservableCollection<RecentAssemblyViewModel> RecentAssemblies { get; } = new ObservableCollection<RecentAssemblyViewModel>();
private ImmutableList<TestCaseViewModel> runningTests; private ImmutableList<TestCaseViewModel>? testsToRun;
public ICommand ExitCommand { get; } public ICommand ExitCommand { get; }
public ICommand WindowLoadedCommand { get; } public ICommand WindowLoadedCommand { get; }
@@ -61,7 +63,6 @@ namespace Xunit.Runner.Wpf.ViewModel
public RelayCommand RunSelectedCommand { get; } public RelayCommand RunSelectedCommand { get; }
public RelayCommand CancelCommand { get; } public RelayCommand CancelCommand { get; }
public ICommand TraitCheckedChangedCommand { get; } public ICommand TraitCheckedChangedCommand { get; }
public ICommand TraitSelectionChangedCommand { get; }
public ICommand TraitsClearCommand { get; } public ICommand TraitsClearCommand { get; }
public ICommand AssemblyReloadCommand { get; } public ICommand AssemblyReloadCommand { get; }
public ICommand AssemblyReloadAllCommand { get; } public ICommand AssemblyReloadAllCommand { get; }
@@ -107,6 +108,7 @@ namespace Xunit.Runner.Wpf.ViewModel
RebuildRecentAssembliesMenu(); RebuildRecentAssembliesMenu();
AutoReloadAssemblies = this.settings.GetAutoReloadAssemblies(); AutoReloadAssemblies = this.settings.GetAutoReloadAssemblies();
UpdateAutoReloadStatus();
} }
private void RebuildRecentAssembliesMenu() private void RebuildRecentAssembliesMenu()
@@ -152,10 +154,13 @@ namespace Xunit.Runner.Wpf.ViewModel
} }
} }
var noFilter = !(searchQuery.FilterFailedTests | searchQuery.FilterPassedTests | searchQuery.FilterSkippedTests); var noFilter = !(searchQuery.FilterRunningTests | searchQuery.FilterFailedTests | searchQuery.FilterPassedTests | searchQuery.FilterSkippedTests);
switch (testCase.State) switch (testCase.State)
{ {
case TestState.Running:
return noFilter || searchQuery.FilterRunningTests;
case TestState.Passed: case TestState.Passed:
return noFilter || searchQuery.FilterPassedTests; return noFilter || searchQuery.FilterPassedTests;
@@ -209,8 +214,8 @@ namespace Xunit.Runner.Wpf.ViewModel
get { return Assemblies.Where(x => x.IsSelected).ToList(); } get { return Assemblies.Where(x => x.IsSelected).ToList(); }
} }
private string testCasesCaption; private string? testCasesCaption;
public string TestCasesCaption public string? TestCasesCaption
{ {
get { return testCasesCaption; } get { return testCasesCaption; }
private set { Set(ref testCasesCaption, value); } private set { Set(ref testCasesCaption, value); }
@@ -228,8 +233,8 @@ namespace Xunit.Runner.Wpf.ViewModel
} }
} }
private TestCaseViewModel selectedTest; private TestCaseViewModel? selectedTest;
public TestCaseViewModel SelectedTestCase public TestCaseViewModel? SelectedTestCase
{ {
get { return selectedTest; } get { return selectedTest; }
@@ -262,6 +267,13 @@ namespace Xunit.Runner.Wpf.ViewModel
} }
} }
private int testsRunning = 0;
public int TestsRunning
{
get { return testsRunning; }
set { Set(ref testsRunning, value); }
}
private int testsPassed = 0; private int testsPassed = 0;
public int TestsPassed public int TestsPassed
{ {
@@ -388,7 +400,7 @@ namespace Xunit.Runner.Wpf.ViewModel
var taskList = new List<Task>(); var taskList = new List<Task>();
foreach (var assembly in assemblies) foreach (var assembly in assemblies)
{ {
taskList.Add(this.testUtil.Discover(assembly.AssemblyFileName, this.OnTestsDiscovered, this.cancellationTokenSource.Token)); taskList.Add(this.testUtil.Discover(assembly.AssemblyFileName, this.OnTestsDiscovered, CancellationToken));
var assemblyViewModel = new TestAssemblyViewModel(assembly); var assemblyViewModel = new TestAssemblyViewModel(assembly);
@@ -414,6 +426,11 @@ namespace Xunit.Runner.Wpf.ViewModel
} }
} }
private CancellationToken CancellationToken
=> cancellationTokenSource == null
? CancellationToken.None
: cancellationTokenSource.Token;
public bool ReloadAssemblies(IEnumerable<string> assemblies) public bool ReloadAssemblies(IEnumerable<string> assemblies)
{ {
if (IsBusy) if (IsBusy)
@@ -441,7 +458,7 @@ namespace Xunit.Runner.Wpf.ViewModel
var assemblyFileName = assemblyViewModel.FileName; var assemblyFileName = assemblyViewModel.FileName;
RemoveAssemblyTestCases(assemblyFileName); RemoveAssemblyTestCases(assemblyFileName);
taskList.Add(this.testUtil.Discover(assemblyFileName, OnTestsDiscovered, cancellationTokenSource.Token)); taskList.Add(this.testUtil.Discover(assemblyFileName, OnTestsDiscovered, CancellationToken));
} }
return taskList; return taskList;
@@ -536,7 +553,7 @@ namespace Xunit.Runner.Wpf.ViewModel
var assemblyFileName = enumerable.First(); var assemblyFileName = enumerable.First();
enumerable = enumerable.Skip(1); enumerable = enumerable.Skip(1);
var configFileName = (string)null; var configFileName = (string?)null;
if (IsConfigFile(enumerable.FirstOrDefault())) if (IsConfigFile(enumerable.FirstOrDefault()))
{ {
configFileName = enumerable.First(); configFileName = enumerable.First();
@@ -559,12 +576,12 @@ namespace Xunit.Runner.Wpf.ViewModel
private async void OnExecuteRunAll() private async void OnExecuteRunAll()
{ {
Debug.Assert(this.runningTests == null); Debug.Assert(this.testsToRun == null);
UpdateTestCaseInfo(useSelected: false); UpdateTestCaseInfo(useSelected: false);
await ExecuteTestSessionOperation(RunFilteredTests); await ExecuteTestSessionOperation(RunFilteredTests);
this.runningTests = null; this.testsToRun = null;
} }
private List<Task> RunFilteredTests() private List<Task> RunFilteredTests()
@@ -574,13 +591,13 @@ namespace Xunit.Runner.Wpf.ViewModel
private async void OnExecuteRunSelected() private async void OnExecuteRunSelected()
{ {
Debug.Assert(this.runningTests == null); Debug.Assert(this.testsToRun == null);
Debug.Assert(this.SelectedTestCase != null); Debug.Assert(this.SelectedTestCase != null);
UpdateTestCaseInfo(useSelected: true); UpdateTestCaseInfo(useSelected: true);
await ExecuteTestSessionOperation(RunSelectedTests); await ExecuteTestSessionOperation(RunSelectedTests);
this.runningTests = null; this.testsToRun = null;
} }
private List<Task> RunSelectedTests() private List<Task> RunSelectedTests()
@@ -592,37 +609,38 @@ namespace Xunit.Runner.Wpf.ViewModel
{ {
Debug.Assert(this.isBusy); Debug.Assert(this.isBusy);
Debug.Assert(this.cancellationTokenSource != null); Debug.Assert(this.cancellationTokenSource != null);
Debug.Assert(this.runningTests == null); Debug.Assert(this.testsToRun == null);
TestsCompleted = 0; TestsCompleted = 0;
TestsRunning = 0;
TestsPassed = 0; TestsPassed = 0;
TestsFailed = 0; TestsFailed = 0;
TestsSkipped = 0; TestsSkipped = 0;
CurrentRunState = TestState.NotRun; CurrentRunState = TestState.NotRun;
Output = string.Empty; 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; 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>(); 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; Task task;
if (runAll) if (runAll)
{ {
task = this.testUtil.RunAll(assemblyFileName, OnTestsFinished, this.cancellationTokenSource.Token); task = this.testUtil.RunAll(assemblyFileName, OnTestStateChange, CancellationToken);
} }
else else
{ {
var builder = ImmutableArray.CreateBuilder<string>(); var builder = ImmutableArray.CreateBuilder<string>();
foreach (var testCase in this.runningTests) foreach (var testCase in this.testsToRun)
{ {
if (testCase.AssemblyFileName == assemblyFileName) if (testCase.AssemblyFileName == assemblyFileName)
{ {
@@ -630,7 +648,7 @@ namespace Xunit.Runner.Wpf.ViewModel
} }
} }
task = this.testUtil.RunSpecific(assemblyFileName, builder.ToImmutable(), OnTestsFinished, this.cancellationTokenSource.Token); task = this.testUtil.RunSpecific(assemblyFileName, builder.ToImmutable(), OnTestStateChange, CancellationToken);
} }
testSessionList.Add(task); testSessionList.Add(task);
@@ -710,28 +728,43 @@ namespace Xunit.Runner.Wpf.ViewModel
} }
} }
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) 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; testCase.State = result.TestState;
TestsCompleted++; if (result.TestState == TestState.Running)
switch (result.TestState)
{ {
case TestState.Passed: if (runningTestSet.Add(result.TestCaseUniqueID))
TestsPassed++; {
break; TestsRunning++;
case TestState.Failed: }
TestsFailed++; }
Output = Output + result.Output; else
break; {
case TestState.Skipped: if (runningTestSet.Remove(result.TestCaseUniqueID))
TestsSkipped++; {
break; 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) if (result.TestState > CurrentRunState)
@@ -749,7 +782,7 @@ namespace Xunit.Runner.Wpf.ViewModel
private void OnExecuteCancel() private void OnExecuteCancel()
{ {
Debug.Assert(CanExecuteCancel()); Debug.Assert(CanExecuteCancel());
this.cancellationTokenSource.Cancel(); cancellationTokenSource?.Cancel();
} }
private void OnExecuteTraitCheckedChanged(TraitViewModel trait) private void OnExecuteTraitCheckedChanged(TraitViewModel trait)
@@ -819,6 +852,18 @@ namespace Xunit.Runner.Wpf.ViewModel
} }
} }
public bool FilterRunningTests
{
get { return searchQuery.FilterRunningTests; }
set
{
if (Set(ref searchQuery.FilterRunningTests, value))
{
FilterAfterDelay();
}
}
}
public bool FilterPassedTests public bool FilterPassedTests
{ {
get { return searchQuery.FilterPassedTests; } get { return searchQuery.FilterPassedTests; }
@@ -4,6 +4,7 @@ namespace Xunit.Runner.Wpf.ViewModel
{ {
public class SearchQuery public class SearchQuery
{ {
public bool FilterRunningTests = false;
public bool FilterFailedTests = false; public bool FilterFailedTests = false;
public bool FilterPassedTests = false; public bool FilterPassedTests = false;
public bool FilterSkippedTests = false; public bool FilterSkippedTests = false;
+2 -2
View File
@@ -7,7 +7,7 @@ namespace Xunit.Runner.Wpf.ViewModel
{ {
public partial class TraitViewModel : ViewModelBase public partial class TraitViewModel : ViewModelBase
{ {
private readonly TraitViewModel _parent; private readonly TraitViewModel? _parent;
private bool? _isChecked; private bool? _isChecked;
private bool _isExpanded; private bool _isExpanded;
private string _text; private string _text;
@@ -19,7 +19,7 @@ namespace Xunit.Runner.Wpf.ViewModel
{ {
} }
private TraitViewModel(TraitViewModel parent, string text) private TraitViewModel(TraitViewModel? parent, string text)
{ {
this._parent = parent; this._parent = parent;
this._isChecked = false; this._isChecked = false;
-1
View File
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="CommonServiceLocator" version="1.3" targetFramework="net452" /> <package id="CommonServiceLocator" version="1.3" targetFramework="net452" />
<package id="Microsoft.Net.Compilers" version="2.2.0" targetFramework="net46" developmentDependency="true" />
<package id="MvvmLight" version="5.3.0.0" targetFramework="net46" /> <package id="MvvmLight" version="5.3.0.0" targetFramework="net46" />
<package id="MvvmLightLibs" version="5.3.0.0" targetFramework="net46" /> <package id="MvvmLightLibs" version="5.3.0.0" targetFramework="net46" />
<package id="NuGet.CommandLine" version="2.8.3" targetFramework="net46" /> <package id="NuGet.CommandLine" version="2.8.3" targetFramework="net46" />
+7 -2
View File
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.props" Condition="Exists('..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.props')" /> <Import Project="..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.props" Condition="Exists('..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
@@ -161,6 +160,7 @@
<ItemGroup> <ItemGroup>
<NuGetManifest Include="xunit.runner.wpf.nuspec"> <NuGetManifest Include="xunit.runner.wpf.nuspec">
<Version>$(NuPkgVersion)</Version> <Version>$(NuPkgVersion)</Version>
<PackageAnalysis>False</PackageAnalysis>
</NuGetManifest> </NuGetManifest>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -202,6 +202,12 @@
<ItemGroup> <ItemGroup>
<Resource Include="Artwork\Application.ico" /> <Resource Include="Artwork\Application.ico" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Resource Include="Artwork\Running_large.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Artwork\Running_small.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup> <PropertyGroup>
@@ -209,7 +215,6 @@
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.props'))" /> <Error Condition="!Exists('..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.props'))" />
<Error Condition="!Exists('..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.targets'))" /> <Error Condition="!Exists('..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.2.0\build\Microsoft.Net.Compilers.props'))" />
</Target> </Target>
<Import Project="..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.targets" Condition="Exists('..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.targets')" /> <Import Project="..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.targets" Condition="Exists('..\packages\Tvl.NuGet.BuildTasks.1.0.0-alpha002\build\Tvl.NuGet.BuildTasks.targets')" />
</Project> </Project>
+4 -4
View File
@@ -12,9 +12,9 @@
<tags>XUnit Gui test runner</tags> <tags>XUnit Gui test runner</tags>
</metadata> </metadata>
<files> <files>
<file src="*.dll" target="tools\"/> <file src="bin\$Configuration$\*.dll" target="tools\"/>
<file src="xunit.runner.wpf.targets" target="build"/> <file src="xunit.runner.wpf.targets" target="build\net45"/>
<file src="*.exe" target="tools\" exclude="*vshost*"/> <file src="bin\$Configuration$\*.exe" target="tools\" exclude="**\*vshost*"/>
<file src="*.config" target="tools\" exclude="*vshost*"/> <file src="bin\$Configuration$\*.config" target="tools\" exclude="**\*vshost*"/>
</files> </files>
</package> </package>