6 Commits

Author SHA1 Message Date
Jared Parsons 13afb6eea5 Fixed the race condition in the run and discover tasks 2015-08-27 17:13:55 -07:00
Jared Parsons e3a17c5308 Respond to PR feedback
Handled everything but the race condition.  Going to fix that in a
separate commit.
2015-08-27 16:41:14 -07:00
Jared Parsons 89be98bebc Naming consistency 2015-08-23 23:09:23 -07:00
Jared Parsons 9e5ac70234 Fix reload bugs
Fixes a couple of bugs in the Reload / Remove logic.
2015-08-23 23:08:00 -07:00
Jared Parsons b35da545d6 Added remove all assemblies menu item 2015-08-23 23:01:54 -07:00
Jared Parsons 3a7d01b87e Add Assembly reload support
Can now reload individual or all currently loaded assemblies.

closes #2
2015-08-23 21:25:21 -07:00
11 changed files with 231 additions and 303 deletions
+3 -62
View File
@@ -17,75 +17,16 @@ namespace xunit.runner.wpf
/// <summary>
/// Discover the list of test cases which are available in the specified assembly.
/// </summary>
ITestDiscoverSession Discover(string assemblyPath, CancellationToken cancellationToken = default(CancellationToken));
Task Discover(string assemblyPath, Action<TestCaseData> callback, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Begin a run of all unit tests for the given assembly.
/// </summary>
ITestRunSession RunAll(string assemblyPath, CancellationToken cancellationToken = default(CancellationToken));
Task RunAll(string assemblyPath, Action<TestResultData> callback, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Begin a run of specific unit tests for the given assembly.
/// </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 ConcurrentQueue<T> _queue;
private readonly DispatcherTimer _timer;
private readonly Action<List<T>> _callback;
private readonly Action<T> _callback;
private readonly int _maxPerTick;
private readonly TaskCompletionSource<bool> _taskCompletionSource;
@@ -126,7 +126,7 @@ namespace xunit.runner.wpf.Impl
Connection connection,
Dispatcher dispatcher,
ConcurrentQueue<T> queue,
Action<List<T>> callback,
Action<T> callback,
int maxResultPerTick = MaxResultPerTick,
TimeSpan? interval = null)
{
@@ -159,16 +159,15 @@ namespace xunit.runner.wpf.Impl
list.Add(value);
}
if (list.Count > 0)
{
_callback(list);
foreach (var cur in list)
{
_callback(cur);
}
if (isDone)
{
try
{
_callback(null);
_timer.Stop();
_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
}
}
}
+47 -13
View File
@@ -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);
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);
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);
return new RunSession(connection, _dispatcher, testCaseDisplayNames, cancellationToken);
var queue = new ConcurrentQueue<TestResultData>();
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
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
+14
View File
@@ -95,6 +95,20 @@
<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="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>
<Label Content="Traits:"
Grid.Row="4" />
+140 -34
View File
@@ -49,8 +49,12 @@ namespace xunit.runner.wpf.ViewModel
this.WindowLoadedCommand = new RelayCommand(OnExecuteWindowLoaded);
this.RunCommand = new RelayCommand(OnExecuteRun, CanExecuteRun);
this.CancelCommand = new RelayCommand(OnExecuteCancel, CanExecuteCancel);
this.TraitSelectionChangedCommand = new RelayCommand(OnTraitSelectionChanged);
this.TraitsClearCommand = new RelayCommand(OnTraitsClear);
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)
@@ -110,9 +114,18 @@ namespace xunit.runner.wpf.ViewModel
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 List<TestAssemblyViewModel> SelectedAssemblies
{
get { return Assemblies.Where(x => x.IsSelected).ToList(); }
}
private string methodsCaption;
public string MethodsCaption
{
@@ -242,18 +255,15 @@ namespace xunit.runner.wpf.ViewModel
{
await ExecuteTestSessionOperation(() =>
{
var testSessionList = new List<ITestSession>();
var taskList = new List<Task>();
foreach (var assembly in assemblies)
{
var assemblyPath = assembly.AssemblyFileName;
var session = this.testUtil.Discover(assemblyPath, cancellationTokenSource.Token);
session.TestDiscovered += OnTestDiscovered;
testSessionList.Add(session);
taskList.Add(this.testUtil.Discover(assemblyPath, OnTestDiscovered, cancellationTokenSource.Token));
Assemblies.Add(new TestAssemblyViewModel(assembly));
}
return testSessionList;
return taskList;
});
}
finally
@@ -262,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
{
get { return isBusy; }
@@ -313,7 +392,7 @@ namespace xunit.runner.wpf.ViewModel
await ExecuteTestSessionOperation(RunTests);
}
private List<ITestSession> RunTests()
private List<Task> RunTests()
{
Debug.Assert(this.isBusy);
Debug.Assert(this.cancellationTokenSource != null);
@@ -331,14 +410,14 @@ namespace xunit.runner.wpf.ViewModel
}
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())
{
ITestRunSession session;
Task task;
if (runAll)
{
session = this.testUtil.RunAll(assemblyPath, this.cancellationTokenSource.Token);
task = this.testUtil.RunAll(assemblyPath, OnTestFinished, this.cancellationTokenSource.Token);
}
else
{
@@ -346,17 +425,16 @@ namespace xunit.runner.wpf.ViewModel
.Where(x => x.AssemblyFileName == assemblyPath)
.Select(x => x.DisplayName)
.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(session);
testSessionList.Add(task);
}
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.cancellationTokenSource == null);
@@ -366,8 +444,8 @@ namespace xunit.runner.wpf.ViewModel
this.IsBusy = true;
this.cancellationTokenSource = new CancellationTokenSource();
var testSessionList = operation();
await Task.WhenAll(testSessionList.Select(x => x.Task));
var taskList = operation();
await Task.WhenAll(taskList);
}
catch (Exception ex)
{
@@ -381,40 +459,38 @@ namespace xunit.runner.wpf.ViewModel
}
}
private void OnTestDiscovered(object sender, TestCaseDataEventArgs e)
private void OnTestDiscovered(TestCaseData testCaseData)
{
var t = e.TestCaseData;
var traitMap = t.TraitMap.Count == 0
var traitList = testCaseData.TraitMap.Count == 0
? ImmutableArray<TraitViewModel>.Empty
: t.TraitMap.SelectMany(pair => pair.Value.Select(value => new TraitViewModel(pair.Key, value))).ToImmutableArray();
this.allTestCases.Add(new TestCaseViewModel(t.SerializedForm, t.DisplayName, t.AssemblyPath, traitMap));
this.traitCollectionView.Add(traitMap);
: 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);
testCase.State = e.TestState;
var testCase = TestCases.Single(x => x.DisplayName == testResultData.TestCaseDisplayName);
testCase.State = testResultData.TestState;
TestsCompleted++;
switch (e.TestState)
switch (testResultData.TestState)
{
case TestState.Passed:
TestsPassed++;
break;
case TestState.Failed:
TestsFailed++;
Output = Output + e.Output;
Output = Output + testResultData.Output;
break;
case TestState.Skipped:
TestsSkipped++;
break;
}
if (e.TestState > CurrentRunState)
if (testResultData.TestState > CurrentRunState)
{
CurrentRunState = e.TestState;
CurrentRunState = testResultData.TestState;
}
}
@@ -429,7 +505,7 @@ namespace xunit.runner.wpf.ViewModel
this.cancellationTokenSource.Cancel();
}
private void OnTraitSelectionChanged()
private void OnExecuteTraitSelectionChanged()
{
this.searchQuery.TraitSet = new HashSet<TraitViewModel>(
this.traitCollectionView.Collection.Where(x => x.IsSelected),
@@ -437,7 +513,7 @@ namespace xunit.runner.wpf.ViewModel
FilterAfterDelay();
}
private void OnTraitsClear()
private void OnExecuteTraitsClear()
{
foreach (var cur in this.traitCollectionView.Collection)
{
@@ -445,6 +521,36 @@ namespace xunit.runner.wpf.ViewModel
}
}
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
{
get { return searchQuery.IncludePassedTests; }
@@ -10,15 +10,22 @@ namespace xunit.runner.wpf.ViewModel
{
public class TestAssemblyViewModel : ViewModelBase
{
private readonly AssemblyAndConfigFile assembly;
private readonly AssemblyAndConfigFile _assembly;
private bool _isSelected;
public TestAssemblyViewModel(AssemblyAndConfigFile assembly)
{
this.assembly = assembly;
_assembly = assembly;
}
public string FileName => assembly.AssemblyFileName;
public string ConfigFileName => Path.GetFileNameWithoutExtension(assembly.ConfigFileName);
public string DisplayName => Path.GetFileNameWithoutExtension(assembly.AssemblyFileName);
public string FileName => _assembly.AssemblyFileName;
public string ConfigFileName => Path.GetFileNameWithoutExtension(_assembly.ConfigFileName);
public string DisplayName => Path.GetFileNameWithoutExtension(_assembly.AssemblyFileName);
public bool IsSelected
{
get { return _isSelected; }
set { Set(ref _isSelected, value, nameof(IsSelected)); }
}
}
}
@@ -23,18 +23,18 @@ namespace xunit.runner.wpf.ViewModel
public void Add(ImmutableArray<TraitViewModel> traitList)
{
if (traitList.Length == 0)
if (traitList.IsDefaultOrEmpty)
{
return;
}
foreach (var traitViewModel in traitList)
{
TryInsert(traitViewModel);
InsertIfNotPresent(traitViewModel);
}
}
private void TryInsert(TraitViewModel trait)
private void InsertIfNotPresent(TraitViewModel trait)
{
// TODO: make it a binary search
for (int i = 0; i < _collection.Count; i++)
+7 -8
View File
@@ -9,24 +9,23 @@ namespace xunit.runner.wpf.ViewModel
{
public sealed class TraitViewModel : ViewModelBase
{
private readonly string _name;
private readonly string _value;
private bool _isSelected;
public string Name => _name;
public string Value => _value;
public string DisplayName => $"{Name}={Value}";
public string Name { get; }
public string Value { get; }
public string DisplayName { get; }
public bool IsSelected
{
get { return _isSelected; }
set { Set(ref _isSelected, value, nameof(IsSelected)); }
set { Set(ref _isSelected, value); }
}
public TraitViewModel(string name, string value)
{
_name = name;
_value = value;
Name = name;
Value = value;
DisplayName = $"{Name}={Value}";
}
}
}
-2
View File
@@ -86,8 +86,6 @@
<Compile Include="Extensions.cs" />
<Compile Include="FilteredCollectionView.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.cs" />
<Compile Include="ITestUtil.cs" />