4 Commits

Author SHA1 Message Date
Jared Parsons 0e24227de0 Ability to clear trait selection
This should fulfill issue #7
2015-08-23 21:00:28 -07:00
Jared Parsons 65f3fc970e Test run can be filtered by traits 2015-08-23 20:50:59 -07:00
Jared Parsons 06f1c8c703 Traits displaying in the UI 2015-08-23 19:20:34 -07:00
Jared Parsons 29aa127230 Merge pull request #19 from Pilchie/remote
Move discover and execution to a remote process
2015-08-22 12:02:28 -07:00
10 changed files with 224 additions and 12 deletions
+9 -2
View File
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
@@ -12,27 +13,33 @@ namespace xunit.runner.data
public string SerializedForm { get; set; }
public string DisplayName { 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;
DisplayName = displayName;
AssemblyPath = assemblyPath;
TraitMap = traitMap;
}
public static TestCaseData ReadFrom(BinaryReader reader)
{
var formatter = new BinaryFormatter();
var serializedForm = reader.ReadString();
var displayName = 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)
{
var formatter = new BinaryFormatter();
writer.Write(SerializedForm);
writer.Write(DisplayName);
writer.Write(AssemblyPath);
formatter.Serialize(writer.BaseStream, TraitMap);
}
}
}
+4 -1
View File
@@ -16,11 +16,13 @@ namespace xunit.runner.worker
{
private readonly ITestFrameworkDiscoverer _discoverer;
private readonly ClientWriter _writer;
private readonly Dictionary<string, List<string>> _traitMap;
internal Impl(ITestFrameworkDiscoverer discoverer, ClientWriter writer)
{
_discoverer = discoverer;
_writer = writer;
_traitMap = new Dictionary<string, List<string>>(StringComparer.Ordinal);
}
protected override bool Visit(ITestCaseDiscoveryMessage testCaseDiscovered)
@@ -29,7 +31,8 @@ namespace xunit.runner.worker
var testCaseData = new TestCaseData(
_discoverer.Serialize(testCase),
testCase.DisplayName,
testCaseDiscovered.TestAssembly.Assembly.AssemblyPath);
testCaseDiscovered.TestAssembly.Assembly.AssemblyPath,
testCase.Traits);
Console.WriteLine(testCase.DisplayName);
_writer.Write(TestDataKind.Value);
+23 -1
View File
@@ -98,7 +98,29 @@
</ListBox>
<Label Content="Traits:"
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>
</GroupBox>
+46 -3
View File
@@ -24,6 +24,7 @@ namespace xunit.runner.wpf.ViewModel
{
private readonly ITestUtil testUtil;
private readonly ObservableCollection<TestCaseViewModel> allTestCases = new ObservableCollection<TestCaseViewModel>();
private readonly TraitCollectionView traitCollectionView = new TraitCollectionView();
private CancellationTokenSource filterCancellationTokenSource = new CancellationTokenSource();
private CancellationTokenSource cancellationTokenSource;
@@ -48,6 +49,8 @@ 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);
}
private static bool TestCaseMatches(TestCaseViewModel testCase, SearchQuery searchQuery)
@@ -57,6 +60,24 @@ namespace xunit.runner.wpf.ViewModel
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)
{
case TestState.Passed:
@@ -87,6 +108,8 @@ namespace xunit.runner.wpf.ViewModel
public ICommand WindowLoadedCommand { get; }
public RelayCommand RunCommand { get; }
public RelayCommand CancelCommand { get; }
public ICommand TraitSelectionChangedCommand { get; }
public ICommand TraitsClearCommand { get; }
public CommandBindingCollection CommandBindings { get; }
@@ -189,6 +212,7 @@ namespace xunit.runner.wpf.ViewModel
public ObservableCollection<TestAssemblyViewModel> Assemblies { get; } = new ObservableCollection<TestAssemblyViewModel>();
public FilteredCollectionView<TestCaseViewModel, SearchQuery> TestCases { get; }
public ObservableCollection<TraitViewModel> Traits => this.traitCollectionView.Collection;
private async void OnExecuteOpen(object sender, ExecutedRoutedEventArgs e)
{
@@ -306,8 +330,6 @@ namespace xunit.runner.wpf.ViewModel
tc.State = TestState.NotRun;
}
// TODO: Need a way to filter based on traits
var runAll = TestCases.Count == this.allTestCases.Count;
var testSessionList = new List<ITestSession>();
@@ -362,7 +384,12 @@ namespace xunit.runner.wpf.ViewModel
private void OnTestDiscovered(object sender, TestCaseDataEventArgs e)
{
var t = e.TestCaseData;
allTestCases.Add(new TestCaseViewModel(t.SerializedForm, t.DisplayName, t.AssemblyPath));
var traitMap = t.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);
}
private void OnTestFinished(object sender, TestResultDataEventArgs e)
@@ -402,6 +429,22 @@ namespace xunit.runner.wpf.ViewModel
this.cancellationTokenSource.Cancel();
}
private void OnTraitSelectionChanged()
{
this.searchQuery.TraitSet = new HashSet<TraitViewModel>(
this.traitCollectionView.Collection.Where(x => x.IsSelected),
TraitViewModelComparer.Instance);
FilterAfterDelay();
}
private void OnTraitsClear()
{
foreach (var cur in this.traitCollectionView.Collection)
{
cur.IsSelected = false;
}
}
public bool IncludePassedTests
{
get { return searchQuery.IncludePassedTests; }
+1 -1
View File
@@ -11,7 +11,7 @@ namespace xunit.runner.wpf.ViewModel
public bool IncludeFailedTests = true;
public bool IncludePassedTests = true;
public bool IncludeSkippedTests = true;
public string SearchString = string.Empty;
public HashSet<TraitViewModel> TraitSet = new HashSet<TraitViewModel>(TraitViewModelComparer.Instance);
}
}
@@ -1,6 +1,7 @@
using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
@@ -11,24 +12,28 @@ namespace xunit.runner.wpf.ViewModel
{
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.DisplayName = displayName;
this.AssemblyFileName = assemblyFileName;
this.Traits = traits;
}
public string DisplayName { get; }
private TestState state = TestState.NotRun;
public TestState State
{
get { return state; }
set { Set(ref state, value); }
get { return _state; }
set { Set(ref _state, value); }
}
public string AssemblyFileName { 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.Length == 0)
{
return;
}
foreach (var traitViewModel in traitList)
{
TryInsert(traitViewModel);
}
}
private void TryInsert(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,32 @@
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 readonly string _name;
private readonly string _value;
private bool _isSelected;
public string Name => _name;
public string Value => _value;
public string DisplayName => $"{Name}={Value}";
public bool IsSelected
{
get { return _isSelected; }
set { Set(ref _isSelected, value, nameof(IsSelected)); }
}
public TraitViewModel(string name, string value)
{
_name = name;
_value = 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();
}
}
}
+3
View File
@@ -94,11 +94,14 @@
<Compile Include="LoadingDialog.xaml.cs">
<DependentUpon>LoadingDialog.xaml</DependentUpon>
</Compile>
<Compile Include="ViewModel\TraitCollectionView.cs" />
<Compile Include="ViewModel\AssemblyAndConfigFile.cs" />
<Compile Include="ViewModel\MainViewModel.cs" />
<Compile Include="ViewModel\SearchQuery.cs" />
<Compile Include="ViewModel\TestCaseViewModel.cs" />
<Compile Include="ViewModel\TestAssemblyViewModel.cs" />
<Compile Include="ViewModel\TraitViewModel.cs" />
<Compile Include="ViewModel\TraitViewModelComparer.cs" />
<Compile Include="ViewModel\ViewModelLocator.cs" />
<Page Include="LoadingDialog.xaml">
<SubType>Designer</SubType>