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.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);
} }
} }
} }
+4 -1
View File
@@ -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);
+23 -1
View File
@@ -98,7 +98,29 @@
</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>
+46 -3
View File
@@ -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,8 @@ 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(OnTraitSelectionChanged);
this.TraitsClearCommand = new RelayCommand(OnTraitsClear);
} }
private static bool TestCaseMatches(TestCaseViewModel testCase, SearchQuery searchQuery) private static bool TestCaseMatches(TestCaseViewModel testCase, SearchQuery searchQuery)
@@ -57,6 +60,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,6 +108,8 @@ 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 CommandBindingCollection CommandBindings { get; } public CommandBindingCollection CommandBindings { get; }
@@ -189,6 +212,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)
{ {
@@ -306,8 +330,6 @@ 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<ITestSession>();
@@ -362,7 +384,12 @@ namespace xunit.runner.wpf.ViewModel
private void OnTestDiscovered(object sender, TestCaseDataEventArgs e) private void OnTestDiscovered(object sender, TestCaseDataEventArgs e)
{ {
var t = e.TestCaseData; 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) private void OnTestFinished(object sender, TestResultDataEventArgs e)
@@ -402,6 +429,22 @@ namespace xunit.runner.wpf.ViewModel
this.cancellationTokenSource.Cancel(); 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 public bool IncludePassedTests
{ {
get { return searchQuery.IncludePassedTests; } get { return searchQuery.IncludePassedTests; }
+1 -1
View File
@@ -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);
} }
} }
@@ -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.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"> <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>