Skip to content

Commit 89beaf6

Browse files
committed
Apply test filter for test run
1 parent 8d60a2c commit 89beaf6

File tree

14 files changed

+717
-9
lines changed

14 files changed

+717
-9
lines changed

src/TestCentric/testcentric.gui/Presenters/TestCentricPresenter.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,10 @@ private void RunFailedTests()
987987
{
988988
var test = entry.Value;
989989
if (!test.IsSuite && test.Outcome.Status == TestStatus.Failed)
990-
failedTests.Add(test);
990+
{
991+
TestNode testNode = _model.GetTestById(test.Id);
992+
failedTests.Add(testNode);
993+
}
991994
}
992995

993996
_model.RunTests(failedTests);

src/TestModel/model/Filter/CategoryFilter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class CategoryFilter : ITestFilter
1818

1919
private List<string> _condition = new List<string>();
2020

21-
internal CategoryFilter(ITestModel model)
21+
public CategoryFilter(ITestModel model)
2222
{
2323
TestModel = model;
2424
}
@@ -33,6 +33,8 @@ public IEnumerable<string> Condition
3333
set { _condition = value.ToList(); }
3434
}
3535

36+
public bool IsActive => AllCategories.Except(_condition).Any();
37+
3638
public IEnumerable<string> AllCategories { get; private set; }
3739

3840
public bool IsMatching(TestNode testNode)

src/TestModel/model/Filter/ITestCentricTestFilter.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ public interface ITestCentricTestFilter
3232
/// </summary>
3333
IEnumerable<string> AllCategories { get; }
3434

35+
/// <summary>
36+
/// Checks if any filter is active
37+
/// </summary>
38+
bool IsActive { get; }
39+
3540
/// <summary>
3641
/// Clear all actives filters and reset them to default
3742
/// </summary>

src/TestModel/model/Filter/ITestFilter.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,10 @@ internal interface ITestFilter
3636
/// Checks if the testNode matches the filter condition
3737
/// </summary>
3838
bool IsMatching(TestNode testNode);
39+
40+
/// <summary>
41+
/// Checks if the filter is active
42+
/// </summary>
43+
bool IsActive { get; }
3944
}
4045
}

src/TestModel/model/Filter/OutcomeFilter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class OutcomeFilter : ITestFilter
1818

1919
private List<string> _condition = new List<string>();
2020

21-
internal OutcomeFilter(ITestModel model)
21+
public OutcomeFilter(ITestModel model)
2222
{
2323
TestModel = model;
2424
}
@@ -33,6 +33,8 @@ public IEnumerable<string> Condition
3333
set { _condition = value.ToList(); }
3434
}
3535

36+
public bool IsActive => _condition.Any();
37+
3638
public bool IsMatching(TestNode testNode)
3739
{
3840
// All kind of outcomes should be displayed (no outcome filtering)

src/TestModel/model/Filter/TestCentricTestFilter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public IEnumerable<string> AllCategories
5555
}
5656
}
5757

58+
public bool IsActive => _filters.Any(x => x.IsActive);
59+
5860
public void ClearAllFilters()
5961
{
6062
foreach (ITestFilter filter in _filters)

src/TestModel/model/Filter/TextFilter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace TestCentric.Gui.Model.Filter
1212
/// <summary>
1313
/// Filters the TestNodes by matching a text (for example: Namespace, Class name or test method name - filter is case insensitive)
1414
/// </summary>
15-
internal class TextFilter : ITestFilter
15+
public class TextFilter : ITestFilter
1616
{
1717
private string _condition = string.Empty;
1818

@@ -24,6 +24,8 @@ public IEnumerable<string> Condition
2424
set { _condition = value.FirstOrDefault(); }
2525
}
2626

27+
public bool IsActive => string.IsNullOrEmpty( _condition) == false;
28+
2729
public bool IsMatching(TestNode testNode)
2830
{
2931
if (string.IsNullOrEmpty(_condition))

src/TestModel/model/TestFilter.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,35 @@ public static TestFilter MakeCategoryFilter(IList<string> categories)
8080
return new TestFilter(sb.ToString());
8181
}
8282

83+
/// <summary>
84+
/// Creates a TestFilter which contains the IDs of all visible child nodes
85+
/// </summary>
86+
public static TestFilter MakeVisibleIdFilter(IEnumerable<TestNode> testNodes)
87+
{
88+
StringBuilder sb = new StringBuilder("<filter><or>");
89+
90+
foreach (TestNode test in testNodes)
91+
MakeVisibleIdFilter(test, sb);
92+
93+
sb.Append("</or></filter>");
94+
95+
return new TestFilter(sb.ToString());
96+
}
97+
98+
private static void MakeVisibleIdFilter(TestNode testNode, StringBuilder sb)
99+
{
100+
// If testNode is not visible, don't add it or any child to filter
101+
if (!testNode.IsVisible)
102+
return;
103+
104+
// Add only Id for leaf nodes
105+
if (!testNode.IsProject && !testNode.IsSuite && testNode.Children.Count == 0)
106+
sb.Append($"<id>{testNode.Id}</id>");
107+
108+
foreach (TestNode childNode in testNode.Children)
109+
MakeVisibleIdFilter(childNode, sb);
110+
}
111+
83112
public static TestFilter MakeNotFilter(TestFilter filter)
84113
{
85114
return new TestFilter($"<filter><not>{filter.InnerXml}</not></filter>");

src/TestModel/model/TestModel.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,10 @@ private void RunTests(TestRunSpecification runSpec)
680680
if (!runSpec.CategoryFilter.IsEmpty)
681681
filter = TestFilter.MakeAndFilter(filter, runSpec.CategoryFilter);
682682

683+
// If a filter is active in the UI, a TestFilter must be created accordingly that contains the ID all visible children of the selected nodes.
684+
if (Settings.Gui.TestTree.DisplayFormat == "NUNIT_TREE" && TestCentricTestFilter.IsActive)
685+
filter = TestFilter.MakeVisibleIdFilter(runSpec.SelectedTests);
686+
683687
// We need to re-create the test runner because settings such
684688
// as debugging have already been passed to the test runner.
685689
// For performance reasons, we only do this if we did run
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// ***********************************************************************
2+
// Copyright (c) Charlie Poole and TestCentric contributors.
3+
// Licensed under the MIT License. See LICENSE file in root directory.
4+
// ***********************************************************************
5+
6+
using NSubstitute;
7+
using NUnit.Framework;
8+
using System.Collections.Generic;
9+
10+
namespace TestCentric.Gui.Model.Filter
11+
{
12+
[TestFixture]
13+
internal class CategoryFilterTests
14+
{
15+
[Test]
16+
public void Create_TestFilter_ConditionIsEmpty()
17+
{
18+
// 1. Arrange + Act
19+
ITestModel testModel = Substitute.For<ITestModel>();
20+
CategoryFilter filter = new CategoryFilter(testModel);
21+
22+
// 2. Assert
23+
Assert.That(filter.Condition, Is.Empty);
24+
}
25+
26+
[TestCase(new[] { "CategoryA" }, new[] { "CategoryA" })]
27+
[TestCase(new[] { "CategoryA", "CategoryB" }, new[] { "CategoryB" })]
28+
[TestCase(new[] { "CategoryB" }, new[] { "CategoryA", "CategoryB" })]
29+
[TestCase(new[] { CategoryFilter.NoCategory }, new string[]{ })]
30+
31+
public void IsMatching_CategoryMatchesCategoryFilter_ReturnsTrue(IList<string> categoryFilter, IList<string> testCategories)
32+
{
33+
// 1. Arrange
34+
ITestModel testModel = Substitute.For<ITestModel>();
35+
CategoryFilter filter = new CategoryFilter(testModel);
36+
filter.Condition = categoryFilter;
37+
38+
string xml = CreateTestcaseXml("1", "TestA", testCategories);
39+
TestNode testNode = new TestNode(xml);
40+
41+
// 2. Act
42+
bool isMatch = filter.IsMatching(testNode);
43+
44+
// 3. Assert
45+
Assert.That(isMatch, Is.True);
46+
}
47+
48+
[TestCase(new[] { "CategoryA" }, new[] { "CategoryB" })]
49+
[TestCase(new[] { "CategoryA" }, new[] { "" })]
50+
[TestCase(new[] { CategoryFilter.NoCategory }, new[] { "CategoryB" })]
51+
public void IsMatching_CategoryNotMatchesCategoryFilter_ReturnsFalse(IList<string> categoryFilter, IList<string> testCategories)
52+
{
53+
// 1. Arrange
54+
ITestModel testModel = Substitute.For<ITestModel>();
55+
CategoryFilter filter = new CategoryFilter(testModel);
56+
filter.Condition = categoryFilter;
57+
58+
string xml = CreateTestcaseXml("1", "TestA", testCategories);
59+
TestNode testNode = new TestNode(xml);
60+
61+
// 2. Act
62+
bool isMatch = filter.IsMatching(testNode);
63+
64+
// 3. Assert
65+
Assert.That(isMatch, Is.False);
66+
}
67+
68+
[Test]
69+
public void Init_Condition_ContainsAllCategories()
70+
{
71+
// 1. Arrange
72+
var availableCategories = new List<string>() { "CategoryA", "CategoryB" };
73+
ITestModel testModel = Substitute.For<ITestModel>();
74+
testModel.AvailableCategories.Returns(availableCategories);
75+
CategoryFilter filter = new CategoryFilter(testModel);
76+
filter.Condition = new[] { "CategoryA" };
77+
78+
// 2. Act
79+
filter.Init();
80+
81+
// 3. Assert
82+
Assert.That(filter.Condition, Has.Exactly(3).Items);
83+
Assert.That(filter.Condition, Does.Contain("CategoryA"));
84+
Assert.That(filter.Condition, Does.Contain("CategoryB"));
85+
Assert.That(filter.Condition, Does.Contain(CategoryFilter.NoCategory));
86+
}
87+
88+
[Test]
89+
public void Reset_Condition_ContainsAllCategories()
90+
{
91+
// 1. Arrange
92+
var availableCategories = new List<string>() { "CategoryA", "CategoryB" };
93+
ITestModel testModel = Substitute.For<ITestModel>();
94+
testModel.AvailableCategories.Returns(availableCategories);
95+
CategoryFilter filter = new CategoryFilter(testModel);
96+
filter.Condition = new[] { "CategoryA" };
97+
98+
// 2. Act
99+
filter.Reset();
100+
101+
// 3. Assert
102+
Assert.That(filter.Condition, Has.Exactly(3).Items);
103+
Assert.That(filter.Condition, Does.Contain("CategoryA"));
104+
Assert.That(filter.Condition, Does.Contain("CategoryB"));
105+
Assert.That(filter.Condition, Does.Contain(CategoryFilter.NoCategory));
106+
}
107+
108+
[Test]
109+
public void IsActive_Condition_IsSet_ReturnsTrue()
110+
{
111+
// 1. Arrange
112+
var availableCategories = new List<string>() { "CategoryA", "CategoryB" };
113+
ITestModel testModel = Substitute.For<ITestModel>();
114+
testModel.AvailableCategories.Returns(availableCategories);
115+
CategoryFilter filter = new CategoryFilter(testModel);
116+
filter.Init();
117+
118+
// 2. Act
119+
filter.Condition = new[] { "CategoryA" };
120+
121+
// 3. Assert
122+
Assert.That(filter.IsActive, Is.EqualTo(true));
123+
}
124+
125+
[Test]
126+
public void IsActive_FilterIsReset_ReturnsFalse()
127+
{
128+
// 1. Arrange
129+
var availableCategories = new List<string>() { "CategoryA", "CategoryB" };
130+
ITestModel testModel = Substitute.For<ITestModel>();
131+
testModel.AvailableCategories.Returns(availableCategories);
132+
CategoryFilter filter = new CategoryFilter(testModel);
133+
filter.Init();
134+
filter.Condition = new[] { "CategoryA" };
135+
136+
// 2. Act
137+
filter.Reset();
138+
139+
// 3. Assert
140+
Assert.That(filter.IsActive, Is.False);
141+
}
142+
143+
[Test]
144+
public void IsActive_FilterIsInit_ReturnsFalse()
145+
{
146+
// 1. Arrange
147+
var availableCategories = new List<string>() { "CategoryA", "CategoryB" };
148+
ITestModel testModel = Substitute.For<ITestModel>();
149+
testModel.AvailableCategories.Returns(availableCategories);
150+
CategoryFilter filter = new CategoryFilter(testModel);
151+
filter.Init();
152+
filter.Condition = new[] { "CategoryA" };
153+
154+
// 2. Act
155+
filter.Init();
156+
157+
// 3. Assert
158+
Assert.That(filter.IsActive, Is.False);
159+
}
160+
161+
private string CreateTestcaseXml(string testId, string testName, IList<string> categories)
162+
{
163+
string str = $"<test-case id='{testId}' fullname='{testName}'> ";
164+
165+
str += "<properties> ";
166+
foreach (string category in categories)
167+
str += $"<property name='Category' value='{category}' /> ";
168+
str += "</properties> ";
169+
170+
str += "</test-case> ";
171+
172+
return str;
173+
}
174+
}
175+
}

0 commit comments

Comments
 (0)