Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 13afb6eea5 | |||
| e3a17c5308 | |||
| 89be98bebc | |||
| 9e5ac70234 | |||
| b35da545d6 | |||
| 3a7d01b87e | |||
| 0e24227de0 | |||
| 65f3fc970e | |||
| 06f1c8c703 | |||
| 29aa127230 |
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -12,27 +13,33 @@ namespace xunit.runner.data
|
|||||||
public string SerializedForm { get; set; }
|
public string SerializedForm { get; set; }
|
||||||
public string DisplayName { get; set; }
|
public string DisplayName { get; set; }
|
||||||
public string AssemblyPath { get; set; }
|
public string AssemblyPath { get; set; }
|
||||||
|
public Dictionary<string, List<string>> TraitMap { get; set; }
|
||||||
|
|
||||||
public TestCaseData(string serializedForm, string displayName, string assemblyPath)
|
public TestCaseData(string serializedForm, string displayName, string assemblyPath, Dictionary<string, List<string>> traitMap)
|
||||||
{
|
{
|
||||||
SerializedForm = serializedForm;
|
SerializedForm = serializedForm;
|
||||||
DisplayName = displayName;
|
DisplayName = displayName;
|
||||||
AssemblyPath = assemblyPath;
|
AssemblyPath = assemblyPath;
|
||||||
|
TraitMap = traitMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TestCaseData ReadFrom(BinaryReader reader)
|
public static TestCaseData ReadFrom(BinaryReader reader)
|
||||||
{
|
{
|
||||||
|
var formatter = new BinaryFormatter();
|
||||||
var serializedForm = reader.ReadString();
|
var serializedForm = reader.ReadString();
|
||||||
var displayName = reader.ReadString();
|
var displayName = reader.ReadString();
|
||||||
var assemblyPath = reader.ReadString();
|
var assemblyPath = reader.ReadString();
|
||||||
return new TestCaseData(serializedForm, displayName, assemblyPath);
|
var traitMap = (Dictionary<string, List<string>>)formatter.Deserialize(reader.BaseStream);
|
||||||
|
return new TestCaseData(serializedForm, displayName, assemblyPath, traitMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteTo(BinaryWriter writer)
|
public void WriteTo(BinaryWriter writer)
|
||||||
{
|
{
|
||||||
|
var formatter = new BinaryFormatter();
|
||||||
writer.Write(SerializedForm);
|
writer.Write(SerializedForm);
|
||||||
writer.Write(DisplayName);
|
writer.Write(DisplayName);
|
||||||
writer.Write(AssemblyPath);
|
writer.Write(AssemblyPath);
|
||||||
|
formatter.Serialize(writer.BaseStream, TraitMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,13 @@ namespace xunit.runner.worker
|
|||||||
{
|
{
|
||||||
private readonly ITestFrameworkDiscoverer _discoverer;
|
private readonly ITestFrameworkDiscoverer _discoverer;
|
||||||
private readonly ClientWriter _writer;
|
private readonly ClientWriter _writer;
|
||||||
|
private readonly Dictionary<string, List<string>> _traitMap;
|
||||||
|
|
||||||
internal Impl(ITestFrameworkDiscoverer discoverer, ClientWriter writer)
|
internal Impl(ITestFrameworkDiscoverer discoverer, ClientWriter writer)
|
||||||
{
|
{
|
||||||
_discoverer = discoverer;
|
_discoverer = discoverer;
|
||||||
_writer = writer;
|
_writer = writer;
|
||||||
|
_traitMap = new Dictionary<string, List<string>>(StringComparer.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool Visit(ITestCaseDiscoveryMessage testCaseDiscovered)
|
protected override bool Visit(ITestCaseDiscoveryMessage testCaseDiscovered)
|
||||||
@@ -29,7 +31,8 @@ namespace xunit.runner.worker
|
|||||||
var testCaseData = new TestCaseData(
|
var testCaseData = new TestCaseData(
|
||||||
_discoverer.Serialize(testCase),
|
_discoverer.Serialize(testCase),
|
||||||
testCase.DisplayName,
|
testCase.DisplayName,
|
||||||
testCaseDiscovered.TestAssembly.Assembly.AssemblyPath);
|
testCaseDiscovered.TestAssembly.Assembly.AssemblyPath,
|
||||||
|
testCase.Traits);
|
||||||
|
|
||||||
Console.WriteLine(testCase.DisplayName);
|
Console.WriteLine(testCase.DisplayName);
|
||||||
_writer.Write(TestDataKind.Value);
|
_writer.Write(TestDataKind.Value);
|
||||||
|
|||||||
@@ -17,75 +17,16 @@ namespace xunit.runner.wpf
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Discover the list of test cases which are available in the specified assembly.
|
/// Discover the list of test cases which are available in the specified assembly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ITestDiscoverSession Discover(string assemblyPath, CancellationToken cancellationToken = default(CancellationToken));
|
Task Discover(string assemblyPath, Action<TestCaseData> callback, CancellationToken cancellationToken = default(CancellationToken));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Begin a run of all unit tests for the given assembly.
|
/// Begin a run of all unit tests for the given assembly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ITestRunSession RunAll(string assemblyPath, CancellationToken cancellationToken = default(CancellationToken));
|
Task RunAll(string assemblyPath, Action<TestResultData> callback, CancellationToken cancellationToken = default(CancellationToken));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Begin a run of specific unit tests for the given assembly.
|
/// Begin a run of specific unit tests for the given assembly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ITestRunSession RunSpecific(string assemblyPath, ImmutableArray<string> testCaseDisplayNames, CancellationToken cancellationToken = default(CancellationToken));
|
Task RunSpecific(string assemblyPath, ImmutableArray<string> testCaseDisplayNames, Action<TestResultData> callback, CancellationToken cancellationToken = default(CancellationToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface ITestSession
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Task which will be completed when the session is finished.
|
|
||||||
/// </summary>
|
|
||||||
Task Task { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal interface ITestRunSession : ITestSession
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Raised when an individual test is finished running.
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler<TestResultDataEventArgs> TestFinished;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised when the session has finished executing all of the specified tests.
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler SessionFinished;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal interface ITestDiscoverSession : ITestSession
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Raised when an individual test is finished running.
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler<TestCaseDataEventArgs> TestDiscovered;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised when the session has finished executing all of the specified tests.
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler SessionFinished;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class TestCaseDataEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
internal readonly TestCaseData TestCaseData;
|
|
||||||
|
|
||||||
internal TestCaseDataEventArgs(TestCaseData data)
|
|
||||||
{
|
|
||||||
TestCaseData = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class TestResultDataEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
internal readonly TestResultData TestResultData;
|
|
||||||
|
|
||||||
internal string TestCaseDisplayName => TestResultData.TestCaseDisplayName;
|
|
||||||
internal TestState TestState => TestResultData.TestState;
|
|
||||||
internal string Output => TestResultData.Output;
|
|
||||||
|
|
||||||
internal TestResultDataEventArgs(TestResultData testResultData)
|
|
||||||
{
|
|
||||||
TestResultData = testResultData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ namespace xunit.runner.wpf.Impl
|
|||||||
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<T> _callback;
|
||||||
private readonly int _maxPerTick;
|
private readonly int _maxPerTick;
|
||||||
private readonly TaskCompletionSource<bool> _taskCompletionSource;
|
private readonly TaskCompletionSource<bool> _taskCompletionSource;
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ namespace xunit.runner.wpf.Impl
|
|||||||
Connection connection,
|
Connection connection,
|
||||||
Dispatcher dispatcher,
|
Dispatcher dispatcher,
|
||||||
ConcurrentQueue<T> queue,
|
ConcurrentQueue<T> queue,
|
||||||
Action<List<T>> callback,
|
Action<T> callback,
|
||||||
int maxResultPerTick = MaxResultPerTick,
|
int maxResultPerTick = MaxResultPerTick,
|
||||||
TimeSpan? interval = null)
|
TimeSpan? interval = null)
|
||||||
{
|
{
|
||||||
@@ -159,16 +159,15 @@ namespace xunit.runner.wpf.Impl
|
|||||||
list.Add(value);
|
list.Add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.Count > 0)
|
foreach (var cur in list)
|
||||||
{
|
{
|
||||||
_callback(list);
|
_callback(cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDone)
|
if (isDone)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_callback(null);
|
|
||||||
_timer.Stop();
|
_timer.Stop();
|
||||||
_connection.Dispose();
|
_connection.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
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;
|
|
||||||
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 DiscoverSession : ITestDiscoverSession
|
|
||||||
{
|
|
||||||
private readonly Task _task;
|
|
||||||
private event EventHandler<TestCaseDataEventArgs> _testDiscovered;
|
|
||||||
private event EventHandler _sessionFinished;
|
|
||||||
|
|
||||||
internal DiscoverSession(Connection connection, Dispatcher dispatcher, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var queue = new ConcurrentQueue<TestCaseData>();
|
|
||||||
var backgroundReader = new BackgroundReader<TestCaseData>(queue, new ClientReader(connection.Stream), r => r.ReadTestCaseData(), cancellationToken);
|
|
||||||
backgroundReader.ReadAsync();
|
|
||||||
|
|
||||||
var backgroundProducer = new BackgroundProducer<TestCaseData>(connection, dispatcher, queue, OnDiscovered);
|
|
||||||
_task = backgroundProducer.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDiscovered(List<TestCaseData> list)
|
|
||||||
{
|
|
||||||
Debug.Assert(!_task.IsCompleted);
|
|
||||||
|
|
||||||
if (list == null)
|
|
||||||
{
|
|
||||||
_sessionFinished?.Invoke(this, EventArgs.Empty);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var cur in list)
|
|
||||||
{
|
|
||||||
_testDiscovered?.Invoke(this, new TestCaseDataEventArgs(cur));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region ITestRunSession
|
|
||||||
|
|
||||||
Task ITestSession.Task => _task;
|
|
||||||
|
|
||||||
event EventHandler<TestCaseDataEventArgs> ITestDiscoverSession.TestDiscovered
|
|
||||||
{
|
|
||||||
add { _testDiscovered += value; }
|
|
||||||
remove { _testDiscovered -= value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
event EventHandler ITestDiscoverSession.SessionFinished
|
|
||||||
{
|
|
||||||
add { _sessionFinished += value; }
|
|
||||||
remove { _sessionFinished -= value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.Diagnostics;
|
|
||||||
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;
|
|
||||||
using xunit.runner.wpf.ViewModel;
|
|
||||||
|
|
||||||
namespace xunit.runner.wpf.Impl
|
|
||||||
{
|
|
||||||
internal partial class RemoteTestUtil
|
|
||||||
{
|
|
||||||
private sealed class RunSession : ITestRunSession
|
|
||||||
{
|
|
||||||
private readonly Task _task;
|
|
||||||
private event EventHandler<TestResultDataEventArgs> _testFinished;
|
|
||||||
private event EventHandler _sessionFinished;
|
|
||||||
|
|
||||||
internal RunSession(Connection connection, Dispatcher dispatcher, ImmutableArray<string> testCaseDisplayNames, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var queue = CreateQueue(connection, testCaseDisplayNames, cancellationToken);
|
|
||||||
var backgroundProducer = new BackgroundProducer<TestResultData>(connection, dispatcher, queue, OnDataProduced);
|
|
||||||
_task = backgroundProducer.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create the <see cref="ConcurrentQueue{T}"/> which will be populated with the <see cref="TestResultData"/>
|
|
||||||
/// as it arrives from the worker.
|
|
||||||
/// </summary>
|
|
||||||
private static ConcurrentQueue<TestResultData> CreateQueue(Connection connection, ImmutableArray<string> testCaseDisplayNames, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var queue = new ConcurrentQueue<TestResultData>();
|
|
||||||
var unused = CreateQueueCore(queue, connection, testCaseDisplayNames, cancellationToken);
|
|
||||||
return queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task CreateQueueCore(ConcurrentQueue<TestResultData> queue, Connection connection, ImmutableArray<string> testCaseDisplayNames, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!testCaseDisplayNames.IsDefaultOrEmpty)
|
|
||||||
{
|
|
||||||
var backgroundWriter = new BackgroundWriter<string>(new ClientWriter(connection.Stream), testCaseDisplayNames, (w, s) => w.Write(s), cancellationToken);
|
|
||||||
await backgroundWriter.WriteAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
var backgroundReader = new BackgroundReader<TestResultData>(queue, new ClientReader(connection.Stream), r => r.ReadTestResultData(), cancellationToken);
|
|
||||||
await backgroundReader.ReadAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.Fail(ex.Message);
|
|
||||||
|
|
||||||
// Signal data completed
|
|
||||||
queue.Enqueue(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDataProduced(List<TestResultData> list)
|
|
||||||
{
|
|
||||||
Debug.Assert(!_task.IsCompleted);
|
|
||||||
if (list == null)
|
|
||||||
{
|
|
||||||
_sessionFinished?.Invoke(this, EventArgs.Empty);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var cur in list)
|
|
||||||
{
|
|
||||||
_testFinished?.Invoke(this, new wpf.TestResultDataEventArgs(cur));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region ITestRunSession
|
|
||||||
|
|
||||||
Task ITestSession.Task => _task;
|
|
||||||
|
|
||||||
event EventHandler<TestResultDataEventArgs> ITestRunSession.TestFinished
|
|
||||||
{
|
|
||||||
add { _testFinished += value; }
|
|
||||||
remove { _testFinished -= value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
event EventHandler ITestRunSession.SessionFinished
|
|
||||||
{
|
|
||||||
add { _sessionFinished += value; }
|
|
||||||
remove { _sessionFinished -= value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -46,39 +46,73 @@ namespace xunit.runner.wpf.Impl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DiscoverSession Discover(string assemblyPath, CancellationToken cancellationToken)
|
private Task Discover(string assemblyPath, Action<TestCaseData> callback, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var connection = StartWorkerProcess(Constants.ActionDiscover, assemblyPath);
|
var connection = StartWorkerProcess(Constants.ActionDiscover, assemblyPath);
|
||||||
return new DiscoverSession(connection, _dispatcher, cancellationToken);
|
var queue = new ConcurrentQueue<TestCaseData>();
|
||||||
|
var backgroundReader = new BackgroundReader<TestCaseData>(queue, new ClientReader(connection.Stream), r => r.ReadTestCaseData(), cancellationToken);
|
||||||
|
backgroundReader.ReadAsync();
|
||||||
|
|
||||||
|
var backgroundProducer = new BackgroundProducer<TestCaseData>(connection, _dispatcher, queue, callback);
|
||||||
|
return backgroundProducer.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RunSession RunAll(string assemblyPath, CancellationToken cancellationToken)
|
private Task RunCore(string actionName, string assemblyPath, ImmutableArray<string> testCaseDisplayNames, Action<TestResultData> callback, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var connection = StartWorkerProcess(Constants.ActionRunAll, assemblyPath);
|
var connection = StartWorkerProcess(Constants.ActionRunAll, assemblyPath);
|
||||||
return new RunSession(connection, _dispatcher, ImmutableArray<string>.Empty, cancellationToken);
|
var queue = CreateRunQueue(connection, testCaseDisplayNames, cancellationToken);
|
||||||
|
var backgroundProducer = new BackgroundProducer<TestResultData>(connection, _dispatcher, queue, callback);
|
||||||
|
return backgroundProducer.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RunSession RunSpecific(string assemblyPath, ImmutableArray<string> testCaseDisplayNames, CancellationToken cancellationToken)
|
/// <summary>
|
||||||
|
/// Create the <see cref="ConcurrentQueue{T}"/> which will be populated with the <see cref="TestResultData"/>
|
||||||
|
/// as it arrives from the worker.
|
||||||
|
/// </summary>
|
||||||
|
private static ConcurrentQueue<TestResultData> CreateRunQueue(Connection connection, ImmutableArray<string> testCaseDisplayNames, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var connection = StartWorkerProcess(Constants.ActionRunSpecific, assemblyPath);
|
var queue = new ConcurrentQueue<TestResultData>();
|
||||||
return new RunSession(connection, _dispatcher, testCaseDisplayNames, cancellationToken);
|
var unused = CreateRunQueueCore(queue, connection, testCaseDisplayNames, cancellationToken);
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task CreateRunQueueCore(ConcurrentQueue<TestResultData> queue, Connection connection, ImmutableArray<string> testCaseDisplayNames, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!testCaseDisplayNames.IsDefaultOrEmpty)
|
||||||
|
{
|
||||||
|
var backgroundWriter = new BackgroundWriter<string>(new ClientWriter(connection.Stream), testCaseDisplayNames, (w, s) => w.Write(s), cancellationToken);
|
||||||
|
await backgroundWriter.WriteAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var backgroundReader = new BackgroundReader<TestResultData>(queue, new ClientReader(connection.Stream), r => r.ReadTestResultData(), cancellationToken);
|
||||||
|
await backgroundReader.ReadAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.Fail(ex.Message);
|
||||||
|
|
||||||
|
// Signal data completed
|
||||||
|
queue.Enqueue(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region ITestUtil
|
#region ITestUtil
|
||||||
|
|
||||||
ITestDiscoverSession ITestUtil.Discover(string assemblyPath, CancellationToken cancellationToken)
|
Task ITestUtil.Discover(string assemblyPath, Action<TestCaseData> callback, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Discover(assemblyPath, cancellationToken);
|
return Discover(assemblyPath, callback, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
ITestRunSession ITestUtil.RunAll(string assemblyPath, CancellationToken cancellationToken)
|
Task ITestUtil.RunAll(string assemblyPath, Action<TestResultData> callback, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return RunAll(assemblyPath, cancellationToken);
|
return RunCore(Constants.ActionRunAll, assemblyPath, ImmutableArray<string>.Empty, callback, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
ITestRunSession ITestUtil.RunSpecific(string assemblyPath, ImmutableArray<string> testCaseDisplayNames, CancellationToken cancellationToken)
|
Task ITestUtil.RunSpecific(string assemblyPath, ImmutableArray<string> testCaseDisplayNames, Action<TestResultData> callback, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return RunSpecific(assemblyPath, testCaseDisplayNames, cancellationToken);
|
return RunCore(Constants.ActionRunSpecific, assemblyPath, testCaseDisplayNames, callback, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -95,10 +95,46 @@
|
|||||||
<TextBlock Text="{Binding DisplayName}" />
|
<TextBlock Text="{Binding DisplayName}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
|
<ListBox.ItemContainerStyle>
|
||||||
|
<Style TargetType="{x:Type ListBoxItem}">
|
||||||
|
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
|
||||||
|
</Style>
|
||||||
|
</ListBox.ItemContainerStyle>
|
||||||
|
<ListBox.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem Header="Reload" Command="{Binding AssemblyReloadCommand}" />
|
||||||
|
<MenuItem Header="Reload All" Command="{Binding AssemblyReloadAllCommand}" />
|
||||||
|
<Separator />
|
||||||
|
<MenuItem Header="Remove" Command="{Binding AssemblyRemoveCommand}" />
|
||||||
|
<MenuItem Header="Remove All" Command="{Binding AssemblyRemoveAllCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</ListBox.ContextMenu>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
<Label Content="Traits:"
|
<Label Content="Traits:"
|
||||||
Grid.Row="4" />
|
Grid.Row="4" />
|
||||||
<ListBox Grid.Row="5" />
|
<ListBox Grid.Row="5"
|
||||||
|
ItemsSource="{Binding Traits}">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate DataType="vm:TraitViewModel">
|
||||||
|
<TextBlock Text="{Binding DisplayName}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
<ListBox.ItemContainerStyle>
|
||||||
|
<Style TargetType="{x:Type ListBoxItem}">
|
||||||
|
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
|
||||||
|
</Style>
|
||||||
|
</ListBox.ItemContainerStyle>
|
||||||
|
<ListBox.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem Header="Clear" Command="{Binding TraitsClearCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</ListBox.ContextMenu>
|
||||||
|
<i:Interaction.Triggers>
|
||||||
|
<i:EventTrigger EventName="SelectionChanged">
|
||||||
|
<cmd:EventToCommand Command="{Binding TraitSelectionChangedCommand}" />
|
||||||
|
</i:EventTrigger>
|
||||||
|
</i:Interaction.Triggers>
|
||||||
|
</ListBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
{
|
{
|
||||||
private readonly ITestUtil testUtil;
|
private readonly ITestUtil testUtil;
|
||||||
private readonly ObservableCollection<TestCaseViewModel> allTestCases = new ObservableCollection<TestCaseViewModel>();
|
private readonly ObservableCollection<TestCaseViewModel> allTestCases = new ObservableCollection<TestCaseViewModel>();
|
||||||
|
private readonly TraitCollectionView traitCollectionView = new TraitCollectionView();
|
||||||
private CancellationTokenSource filterCancellationTokenSource = new CancellationTokenSource();
|
private CancellationTokenSource filterCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
private CancellationTokenSource cancellationTokenSource;
|
private CancellationTokenSource cancellationTokenSource;
|
||||||
@@ -48,6 +49,12 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
this.WindowLoadedCommand = new RelayCommand(OnExecuteWindowLoaded);
|
this.WindowLoadedCommand = new RelayCommand(OnExecuteWindowLoaded);
|
||||||
this.RunCommand = new RelayCommand(OnExecuteRun, CanExecuteRun);
|
this.RunCommand = new RelayCommand(OnExecuteRun, CanExecuteRun);
|
||||||
this.CancelCommand = new RelayCommand(OnExecuteCancel, CanExecuteCancel);
|
this.CancelCommand = new RelayCommand(OnExecuteCancel, CanExecuteCancel);
|
||||||
|
this.TraitSelectionChangedCommand = new RelayCommand(OnExecuteTraitSelectionChanged);
|
||||||
|
this.TraitsClearCommand = new RelayCommand(OnExecuteTraitsClear);
|
||||||
|
this.AssemblyReloadCommand = new RelayCommand(OnExecuteAssemblyReload, CanExecuteAssemblyReload);
|
||||||
|
this.AssemblyReloadAllCommand = new RelayCommand(OnExecuteAssemblyReloadAll);
|
||||||
|
this.AssemblyRemoveCommand = new RelayCommand(OnExecuteAssemblyRemove, CanExecuteAssemblyRemove);
|
||||||
|
this.AssemblyRemoveAllCommand = new RelayCommand(OnExecuteAssemblyRemoveAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TestCaseMatches(TestCaseViewModel testCase, SearchQuery searchQuery)
|
private static bool TestCaseMatches(TestCaseViewModel testCase, SearchQuery searchQuery)
|
||||||
@@ -57,6 +64,24 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (searchQuery.TraitSet.Count > 0)
|
||||||
|
{
|
||||||
|
var anyMatch = false;
|
||||||
|
foreach (var cur in testCase.Traits)
|
||||||
|
{
|
||||||
|
if (searchQuery.TraitSet.Contains(cur))
|
||||||
|
{
|
||||||
|
anyMatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anyMatch)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (testCase.State)
|
switch (testCase.State)
|
||||||
{
|
{
|
||||||
case TestState.Passed:
|
case TestState.Passed:
|
||||||
@@ -87,9 +112,20 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
public ICommand WindowLoadedCommand { get; }
|
public ICommand WindowLoadedCommand { get; }
|
||||||
public RelayCommand RunCommand { get; }
|
public RelayCommand RunCommand { get; }
|
||||||
public RelayCommand CancelCommand { get; }
|
public RelayCommand CancelCommand { get; }
|
||||||
|
public ICommand TraitSelectionChangedCommand { get; }
|
||||||
|
public ICommand TraitsClearCommand { get; }
|
||||||
|
public ICommand AssemblyReloadCommand { get; }
|
||||||
|
public ICommand AssemblyReloadAllCommand { get; }
|
||||||
|
public ICommand AssemblyRemoveCommand { get; }
|
||||||
|
public ICommand AssemblyRemoveAllCommand { get; }
|
||||||
|
|
||||||
public CommandBindingCollection CommandBindings { get; }
|
public CommandBindingCollection CommandBindings { get; }
|
||||||
|
|
||||||
|
public List<TestAssemblyViewModel> SelectedAssemblies
|
||||||
|
{
|
||||||
|
get { return Assemblies.Where(x => x.IsSelected).ToList(); }
|
||||||
|
}
|
||||||
|
|
||||||
private string methodsCaption;
|
private string methodsCaption;
|
||||||
public string MethodsCaption
|
public string MethodsCaption
|
||||||
{
|
{
|
||||||
@@ -189,6 +225,7 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
|
|
||||||
public ObservableCollection<TestAssemblyViewModel> Assemblies { get; } = new ObservableCollection<TestAssemblyViewModel>();
|
public ObservableCollection<TestAssemblyViewModel> Assemblies { get; } = new ObservableCollection<TestAssemblyViewModel>();
|
||||||
public FilteredCollectionView<TestCaseViewModel, SearchQuery> TestCases { get; }
|
public FilteredCollectionView<TestCaseViewModel, SearchQuery> TestCases { get; }
|
||||||
|
public ObservableCollection<TraitViewModel> Traits => this.traitCollectionView.Collection;
|
||||||
|
|
||||||
private async void OnExecuteOpen(object sender, ExecutedRoutedEventArgs e)
|
private async void OnExecuteOpen(object sender, ExecutedRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -218,18 +255,15 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
{
|
{
|
||||||
await ExecuteTestSessionOperation(() =>
|
await ExecuteTestSessionOperation(() =>
|
||||||
{
|
{
|
||||||
var testSessionList = new List<ITestSession>();
|
var taskList = new List<Task>();
|
||||||
foreach (var assembly in assemblies)
|
foreach (var assembly in assemblies)
|
||||||
{
|
{
|
||||||
var assemblyPath = assembly.AssemblyFileName;
|
var assemblyPath = assembly.AssemblyFileName;
|
||||||
var session = this.testUtil.Discover(assemblyPath, cancellationTokenSource.Token);
|
taskList.Add(this.testUtil.Discover(assemblyPath, OnTestDiscovered, cancellationTokenSource.Token));
|
||||||
session.TestDiscovered += OnTestDiscovered;
|
|
||||||
|
|
||||||
testSessionList.Add(session);
|
|
||||||
Assemblies.Add(new TestAssemblyViewModel(assembly));
|
Assemblies.Add(new TestAssemblyViewModel(assembly));
|
||||||
}
|
}
|
||||||
|
|
||||||
return testSessionList;
|
return taskList;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -238,6 +272,75 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ReloadAssemblies(IEnumerable<TestAssemblyViewModel> assemblies)
|
||||||
|
{
|
||||||
|
var loadingDialog = new LoadingDialog { Owner = MainWindow.Instance };
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteTestSessionOperation(() =>
|
||||||
|
{
|
||||||
|
var taskList = new List<Task>();
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
|
{
|
||||||
|
var assemblyPath = assembly.FileName;
|
||||||
|
RemoveAssemblyTestCases(assemblyPath);
|
||||||
|
|
||||||
|
taskList.Add(this.testUtil.Discover(assemblyPath, OnTestDiscovered, cancellationTokenSource.Token));
|
||||||
|
}
|
||||||
|
|
||||||
|
return taskList;
|
||||||
|
});
|
||||||
|
|
||||||
|
RebuildTraits();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
loadingDialog.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveAssemblies(IEnumerable<TestAssemblyViewModel> assemblies)
|
||||||
|
{
|
||||||
|
foreach (var assembly in assemblies.ToList())
|
||||||
|
{
|
||||||
|
RemoveAssemblyTestCases(assembly.FileName);
|
||||||
|
Assemblies.Remove(assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
RebuildTraits();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveAssemblyTestCases(string assemblyPath)
|
||||||
|
{
|
||||||
|
var i = 0;
|
||||||
|
while (i < this.allTestCases.Count)
|
||||||
|
{
|
||||||
|
if (this.allTestCases[i].AssemblyFileName == assemblyPath)
|
||||||
|
{
|
||||||
|
this.allTestCases.RemoveAt(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reloading an assembly could have changed the traits. There is no easy way
|
||||||
|
/// to selectively edit this list (traits can cross assembly boundaries). Just
|
||||||
|
/// do a full reload instead.
|
||||||
|
/// way to
|
||||||
|
/// </summary>
|
||||||
|
private void RebuildTraits()
|
||||||
|
{
|
||||||
|
this.traitCollectionView.Collection.Clear();
|
||||||
|
foreach (var testCase in this.allTestCases)
|
||||||
|
{
|
||||||
|
this.traitCollectionView.Add(testCase.Traits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool IsBusy
|
private bool IsBusy
|
||||||
{
|
{
|
||||||
get { return isBusy; }
|
get { return isBusy; }
|
||||||
@@ -289,7 +392,7 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
await ExecuteTestSessionOperation(RunTests);
|
await ExecuteTestSessionOperation(RunTests);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ITestSession> RunTests()
|
private List<Task> RunTests()
|
||||||
{
|
{
|
||||||
Debug.Assert(this.isBusy);
|
Debug.Assert(this.isBusy);
|
||||||
Debug.Assert(this.cancellationTokenSource != null);
|
Debug.Assert(this.cancellationTokenSource != null);
|
||||||
@@ -306,17 +409,15 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
tc.State = TestState.NotRun;
|
tc.State = TestState.NotRun;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Need a way to filter based on traits
|
|
||||||
|
|
||||||
var runAll = TestCases.Count == this.allTestCases.Count;
|
var runAll = TestCases.Count == this.allTestCases.Count;
|
||||||
var testSessionList = new List<ITestSession>();
|
var testSessionList = new List<Task>();
|
||||||
|
|
||||||
foreach (var assemblyPath in TestCases.Select(x => x.AssemblyFileName).Distinct())
|
foreach (var assemblyPath in TestCases.Select(x => x.AssemblyFileName).Distinct())
|
||||||
{
|
{
|
||||||
ITestRunSession session;
|
Task task;
|
||||||
if (runAll)
|
if (runAll)
|
||||||
{
|
{
|
||||||
session = this.testUtil.RunAll(assemblyPath, this.cancellationTokenSource.Token);
|
task = this.testUtil.RunAll(assemblyPath, OnTestFinished, this.cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -324,17 +425,16 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
.Where(x => x.AssemblyFileName == assemblyPath)
|
.Where(x => x.AssemblyFileName == assemblyPath)
|
||||||
.Select(x => x.DisplayName)
|
.Select(x => x.DisplayName)
|
||||||
.ToImmutableArray();
|
.ToImmutableArray();
|
||||||
session = this.testUtil.RunSpecific(assemblyPath, testCaseDisplayNames, this.cancellationTokenSource.Token);
|
task = this.testUtil.RunSpecific(assemblyPath, testCaseDisplayNames, OnTestFinished, this.cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
session.TestFinished += OnTestFinished;
|
testSessionList.Add(task);
|
||||||
testSessionList.Add(session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return testSessionList;
|
return testSessionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteTestSessionOperation(Func<List<ITestSession>> operation)
|
private async Task ExecuteTestSessionOperation(Func<List<Task>> operation)
|
||||||
{
|
{
|
||||||
Debug.Assert(!this.IsBusy);
|
Debug.Assert(!this.IsBusy);
|
||||||
Debug.Assert(this.cancellationTokenSource == null);
|
Debug.Assert(this.cancellationTokenSource == null);
|
||||||
@@ -344,8 +444,8 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
this.IsBusy = true;
|
this.IsBusy = true;
|
||||||
this.cancellationTokenSource = new CancellationTokenSource();
|
this.cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
var testSessionList = operation();
|
var taskList = operation();
|
||||||
await Task.WhenAll(testSessionList.Select(x => x.Task));
|
await Task.WhenAll(taskList);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -359,35 +459,38 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTestDiscovered(object sender, TestCaseDataEventArgs e)
|
private void OnTestDiscovered(TestCaseData testCaseData)
|
||||||
{
|
{
|
||||||
var t = e.TestCaseData;
|
var traitList = testCaseData.TraitMap.Count == 0
|
||||||
allTestCases.Add(new TestCaseViewModel(t.SerializedForm, t.DisplayName, t.AssemblyPath));
|
? ImmutableArray<TraitViewModel>.Empty
|
||||||
|
: testCaseData.TraitMap.SelectMany(pair => pair.Value.Select(value => new TraitViewModel(pair.Key, value))).ToImmutableArray();
|
||||||
|
this.allTestCases.Add(new TestCaseViewModel(testCaseData.SerializedForm, testCaseData.DisplayName, testCaseData.AssemblyPath, traitList));
|
||||||
|
this.traitCollectionView.Add(traitList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTestFinished(object sender, TestResultDataEventArgs e)
|
private void OnTestFinished(TestResultData testResultData)
|
||||||
{
|
{
|
||||||
var testCase = TestCases.Single(x => x.DisplayName == e.TestCaseDisplayName);
|
var testCase = TestCases.Single(x => x.DisplayName == testResultData.TestCaseDisplayName);
|
||||||
testCase.State = e.TestState;
|
testCase.State = testResultData.TestState;
|
||||||
|
|
||||||
TestsCompleted++;
|
TestsCompleted++;
|
||||||
switch (e.TestState)
|
switch (testResultData.TestState)
|
||||||
{
|
{
|
||||||
case TestState.Passed:
|
case TestState.Passed:
|
||||||
TestsPassed++;
|
TestsPassed++;
|
||||||
break;
|
break;
|
||||||
case TestState.Failed:
|
case TestState.Failed:
|
||||||
TestsFailed++;
|
TestsFailed++;
|
||||||
Output = Output + e.Output;
|
Output = Output + testResultData.Output;
|
||||||
break;
|
break;
|
||||||
case TestState.Skipped:
|
case TestState.Skipped:
|
||||||
TestsSkipped++;
|
TestsSkipped++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.TestState > CurrentRunState)
|
if (testResultData.TestState > CurrentRunState)
|
||||||
{
|
{
|
||||||
CurrentRunState = e.TestState;
|
CurrentRunState = testResultData.TestState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,6 +505,52 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
this.cancellationTokenSource.Cancel();
|
this.cancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnExecuteTraitSelectionChanged()
|
||||||
|
{
|
||||||
|
this.searchQuery.TraitSet = new HashSet<TraitViewModel>(
|
||||||
|
this.traitCollectionView.Collection.Where(x => x.IsSelected),
|
||||||
|
TraitViewModelComparer.Instance);
|
||||||
|
FilterAfterDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExecuteTraitsClear()
|
||||||
|
{
|
||||||
|
foreach (var cur in this.traitCollectionView.Collection)
|
||||||
|
{
|
||||||
|
cur.IsSelected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanExecuteAssemblyReload()
|
||||||
|
{
|
||||||
|
return SelectedAssemblies.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnExecuteAssemblyReload()
|
||||||
|
{
|
||||||
|
await ReloadAssemblies(SelectedAssemblies);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnExecuteAssemblyReloadAll()
|
||||||
|
{
|
||||||
|
await ReloadAssemblies(Assemblies);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanExecuteAssemblyRemove()
|
||||||
|
{
|
||||||
|
return SelectedAssemblies.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExecuteAssemblyRemove()
|
||||||
|
{
|
||||||
|
RemoveAssemblies(SelectedAssemblies);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExecuteAssemblyRemoveAll()
|
||||||
|
{
|
||||||
|
RemoveAssemblies(Assemblies.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
public bool IncludePassedTests
|
public bool IncludePassedTests
|
||||||
{
|
{
|
||||||
get { return searchQuery.IncludePassedTests; }
|
get { return searchQuery.IncludePassedTests; }
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
public bool IncludeFailedTests = true;
|
public bool IncludeFailedTests = true;
|
||||||
public bool IncludePassedTests = true;
|
public bool IncludePassedTests = true;
|
||||||
public bool IncludeSkippedTests = true;
|
public bool IncludeSkippedTests = true;
|
||||||
|
|
||||||
public string SearchString = string.Empty;
|
public string SearchString = string.Empty;
|
||||||
|
public HashSet<TraitViewModel> TraitSet = new HashSet<TraitViewModel>(TraitViewModelComparer.Instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,22 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
{
|
{
|
||||||
public class TestAssemblyViewModel : ViewModelBase
|
public class TestAssemblyViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly AssemblyAndConfigFile assembly;
|
private readonly AssemblyAndConfigFile _assembly;
|
||||||
|
private bool _isSelected;
|
||||||
|
|
||||||
public TestAssemblyViewModel(AssemblyAndConfigFile assembly)
|
public TestAssemblyViewModel(AssemblyAndConfigFile assembly)
|
||||||
{
|
{
|
||||||
this.assembly = assembly;
|
_assembly = assembly;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string FileName => assembly.AssemblyFileName;
|
public string FileName => _assembly.AssemblyFileName;
|
||||||
public string ConfigFileName => Path.GetFileNameWithoutExtension(assembly.ConfigFileName);
|
public string ConfigFileName => Path.GetFileNameWithoutExtension(_assembly.ConfigFileName);
|
||||||
public string DisplayName => Path.GetFileNameWithoutExtension(assembly.AssemblyFileName);
|
public string DisplayName => Path.GetFileNameWithoutExtension(_assembly.AssemblyFileName);
|
||||||
|
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get { return _isSelected; }
|
||||||
|
set { Set(ref _isSelected, value, nameof(IsSelected)); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using GalaSoft.MvvmLight;
|
using GalaSoft.MvvmLight;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -11,24 +12,28 @@ namespace xunit.runner.wpf.ViewModel
|
|||||||
{
|
{
|
||||||
public class TestCaseViewModel : ViewModelBase
|
public class TestCaseViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
public TestCaseViewModel(string testCase, string displayName, string assemblyFileName)
|
private TestState _state = TestState.NotRun;
|
||||||
|
|
||||||
|
public TestCaseViewModel(string testCase, string displayName, string assemblyFileName, ImmutableArray<TraitViewModel> traits)
|
||||||
{
|
{
|
||||||
this.TestCase = testCase;
|
this.TestCase = testCase;
|
||||||
this.DisplayName = displayName;
|
this.DisplayName = displayName;
|
||||||
this.AssemblyFileName = assemblyFileName;
|
this.AssemblyFileName = assemblyFileName;
|
||||||
|
this.Traits = traits;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DisplayName { get; }
|
public string DisplayName { get; }
|
||||||
|
|
||||||
private TestState state = TestState.NotRun;
|
|
||||||
public TestState State
|
public TestState State
|
||||||
{
|
{
|
||||||
get { return state; }
|
get { return _state; }
|
||||||
set { Set(ref state, value); }
|
set { Set(ref _state, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string AssemblyFileName { get; }
|
public string AssemblyFileName { get; }
|
||||||
|
|
||||||
public string TestCase { get; }
|
public string TestCase { get; }
|
||||||
|
|
||||||
|
public ImmutableArray<TraitViewModel> Traits { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace xunit.runner.wpf.ViewModel
|
||||||
|
{
|
||||||
|
public sealed partial class TraitCollectionView
|
||||||
|
{
|
||||||
|
private readonly TraitViewModelComparer _comparer = TraitViewModelComparer.Instance;
|
||||||
|
private readonly ObservableCollection<TraitViewModel> _collection = new ObservableCollection<TraitViewModel>();
|
||||||
|
|
||||||
|
public ObservableCollection<TraitViewModel> Collection => _collection;
|
||||||
|
|
||||||
|
public TraitCollectionView()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(ImmutableArray<TraitViewModel> traitList)
|
||||||
|
{
|
||||||
|
if (traitList.IsDefaultOrEmpty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var traitViewModel in traitList)
|
||||||
|
{
|
||||||
|
InsertIfNotPresent(traitViewModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InsertIfNotPresent(TraitViewModel trait)
|
||||||
|
{
|
||||||
|
// TODO: make it a binary search
|
||||||
|
for (int i = 0; i < _collection.Count; i++)
|
||||||
|
{
|
||||||
|
var current = _collection[i];
|
||||||
|
var result = _comparer.Compare(trait, current);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
_collection.Insert(i, trait);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_collection.Add(trait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using GalaSoft.MvvmLight;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace xunit.runner.wpf.ViewModel
|
||||||
|
{
|
||||||
|
public sealed class TraitViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private bool _isSelected;
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public string Value { get; }
|
||||||
|
public string DisplayName { get; }
|
||||||
|
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get { return _isSelected; }
|
||||||
|
set { Set(ref _isSelected, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public TraitViewModel(string name, string value)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Value = value;
|
||||||
|
DisplayName = $"{Name}={Value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace xunit.runner.wpf.ViewModel
|
||||||
|
{
|
||||||
|
internal sealed class TraitViewModelComparer : IEqualityComparer<TraitViewModel>, IComparer<TraitViewModel>
|
||||||
|
{
|
||||||
|
internal static readonly TraitViewModelComparer Instance = new TraitViewModelComparer();
|
||||||
|
|
||||||
|
private readonly StringComparer _comparer = StringComparer.Ordinal;
|
||||||
|
|
||||||
|
public int Compare(TraitViewModel x, TraitViewModel y)
|
||||||
|
{
|
||||||
|
var result = _comparer.Compare(x.Name, y.Name);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _comparer.Compare(x.Value, y.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(TraitViewModel x, TraitViewModel y)
|
||||||
|
{
|
||||||
|
return _comparer.Equals(x.Name, y.Name)
|
||||||
|
&& _comparer.Equals(x.Value, y.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(TraitViewModel obj)
|
||||||
|
{
|
||||||
|
return obj.Name.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -86,19 +86,20 @@
|
|||||||
<Compile Include="Extensions.cs" />
|
<Compile Include="Extensions.cs" />
|
||||||
<Compile Include="FilteredCollectionView.cs" />
|
<Compile Include="FilteredCollectionView.cs" />
|
||||||
<Compile Include="Impl\RemoteTestUtil.Connection.cs" />
|
<Compile Include="Impl\RemoteTestUtil.Connection.cs" />
|
||||||
<Compile Include="Impl\RemoteTestUtil.DiscoverSession.cs" />
|
|
||||||
<Compile Include="Impl\RemoteTestUtil.RunSession.cs" />
|
|
||||||
<Compile Include="Impl\RemoteTestUtil.BackgroundRunner.cs" />
|
<Compile Include="Impl\RemoteTestUtil.BackgroundRunner.cs" />
|
||||||
<Compile Include="Impl\RemoteTestUtil.cs" />
|
<Compile Include="Impl\RemoteTestUtil.cs" />
|
||||||
<Compile Include="ITestUtil.cs" />
|
<Compile Include="ITestUtil.cs" />
|
||||||
<Compile Include="LoadingDialog.xaml.cs">
|
<Compile Include="LoadingDialog.xaml.cs">
|
||||||
<DependentUpon>LoadingDialog.xaml</DependentUpon>
|
<DependentUpon>LoadingDialog.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="ViewModel\TraitCollectionView.cs" />
|
||||||
<Compile Include="ViewModel\AssemblyAndConfigFile.cs" />
|
<Compile Include="ViewModel\AssemblyAndConfigFile.cs" />
|
||||||
<Compile Include="ViewModel\MainViewModel.cs" />
|
<Compile Include="ViewModel\MainViewModel.cs" />
|
||||||
<Compile Include="ViewModel\SearchQuery.cs" />
|
<Compile Include="ViewModel\SearchQuery.cs" />
|
||||||
<Compile Include="ViewModel\TestCaseViewModel.cs" />
|
<Compile Include="ViewModel\TestCaseViewModel.cs" />
|
||||||
<Compile Include="ViewModel\TestAssemblyViewModel.cs" />
|
<Compile Include="ViewModel\TestAssemblyViewModel.cs" />
|
||||||
|
<Compile Include="ViewModel\TraitViewModel.cs" />
|
||||||
|
<Compile Include="ViewModel\TraitViewModelComparer.cs" />
|
||||||
<Compile Include="ViewModel\ViewModelLocator.cs" />
|
<Compile Include="ViewModel\ViewModelLocator.cs" />
|
||||||
<Page Include="LoadingDialog.xaml">
|
<Page Include="LoadingDialog.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
|
|||||||
Reference in New Issue
Block a user