Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply test filter for test run #1172

Merged
merged 1 commit into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,10 @@ private void RunFailedTests()
{
var test = entry.Value;
if (!test.IsSuite && test.Outcome.Status == TestStatus.Failed)
failedTests.Add(test);
{
TestNode testNode = _model.GetTestById(test.Id);
failedTests.Add(testNode);
}
}

_model.RunTests(failedTests);
Expand Down
4 changes: 3 additions & 1 deletion src/TestModel/model/Filter/CategoryFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class CategoryFilter : ITestFilter

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

internal CategoryFilter(ITestModel model)
public CategoryFilter(ITestModel model)
{
TestModel = model;
}
Expand All @@ -33,6 +33,8 @@ public IEnumerable<string> Condition
set { _condition = value.ToList(); }
}

public bool IsActive => AllCategories.Except(_condition).Any();

public IEnumerable<string> AllCategories { get; private set; }

public bool IsMatching(TestNode testNode)
Expand Down
5 changes: 5 additions & 0 deletions src/TestModel/model/Filter/ITestCentricTestFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public interface ITestCentricTestFilter
/// </summary>
IEnumerable<string> AllCategories { get; }

/// <summary>
/// Checks if any filter is active
/// </summary>
bool IsActive { get; }

/// <summary>
/// Clear all actives filters and reset them to default
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/TestModel/model/Filter/ITestFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,10 @@ internal interface ITestFilter
/// Checks if the testNode matches the filter condition
/// </summary>
bool IsMatching(TestNode testNode);

/// <summary>
/// Checks if the filter is active
/// </summary>
bool IsActive { get; }
}
}
4 changes: 3 additions & 1 deletion src/TestModel/model/Filter/OutcomeFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class OutcomeFilter : ITestFilter

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

internal OutcomeFilter(ITestModel model)
public OutcomeFilter(ITestModel model)
{
TestModel = model;
}
Expand All @@ -33,6 +33,8 @@ public IEnumerable<string> Condition
set { _condition = value.ToList(); }
}

public bool IsActive => _condition.Any();

public bool IsMatching(TestNode testNode)
{
// All kind of outcomes should be displayed (no outcome filtering)
Expand Down
2 changes: 2 additions & 0 deletions src/TestModel/model/Filter/TestCentricTestFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public IEnumerable<string> AllCategories
}
}

public bool IsActive => _filters.Any(x => x.IsActive);

public void ClearAllFilters()
{
foreach (ITestFilter filter in _filters)
Expand Down
4 changes: 3 additions & 1 deletion src/TestModel/model/Filter/TextFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace TestCentric.Gui.Model.Filter
/// <summary>
/// Filters the TestNodes by matching a text (for example: Namespace, Class name or test method name - filter is case insensitive)
/// </summary>
internal class TextFilter : ITestFilter
public class TextFilter : ITestFilter
{
private string _condition = string.Empty;

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

public bool IsActive => string.IsNullOrEmpty( _condition) == false;

public bool IsMatching(TestNode testNode)
{
if (string.IsNullOrEmpty(_condition))
Expand Down
29 changes: 29 additions & 0 deletions src/TestModel/model/TestFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,35 @@ public static TestFilter MakeCategoryFilter(IList<string> categories)
return new TestFilter(sb.ToString());
}

/// <summary>
/// Creates a TestFilter which contains the IDs of all visible child nodes
/// </summary>
public static TestFilter MakeVisibleIdFilter(IEnumerable<TestNode> testNodes)
{
StringBuilder sb = new StringBuilder("<filter><or>");

foreach (TestNode test in testNodes)
MakeVisibleIdFilter(test, sb);

sb.Append("</or></filter>");

return new TestFilter(sb.ToString());
}

private static void MakeVisibleIdFilter(TestNode testNode, StringBuilder sb)
{
// If testNode is not visible, don't add it or any child to filter
if (!testNode.IsVisible)
return;

// Add only Id for leaf nodes
if (!testNode.IsProject && !testNode.IsSuite && testNode.Children.Count == 0)
sb.Append($"<id>{testNode.Id}</id>");

foreach (TestNode childNode in testNode.Children)
MakeVisibleIdFilter(childNode, sb);
}

public static TestFilter MakeNotFilter(TestFilter filter)
{
return new TestFilter($"<filter><not>{filter.InnerXml}</not></filter>");
Expand Down
4 changes: 4 additions & 0 deletions src/TestModel/model/TestModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,10 @@ private void RunTests(TestRunSpecification runSpec)
if (!runSpec.CategoryFilter.IsEmpty)
filter = TestFilter.MakeAndFilter(filter, runSpec.CategoryFilter);

// 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.
if (Settings.Gui.TestTree.DisplayFormat == "NUNIT_TREE" && TestCentricTestFilter.IsActive)
filter = TestFilter.MakeVisibleIdFilter(runSpec.SelectedTests);

// We need to re-create the test runner because settings such
// as debugging have already been passed to the test runner.
// For performance reasons, we only do this if we did run
Expand Down
175 changes: 175 additions & 0 deletions src/TestModel/tests/Filter/CategoryFilterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// ***********************************************************************
// Copyright (c) Charlie Poole and TestCentric contributors.
// Licensed under the MIT License. See LICENSE file in root directory.
// ***********************************************************************

using NSubstitute;
using NUnit.Framework;
using System.Collections.Generic;

namespace TestCentric.Gui.Model.Filter
{
[TestFixture]
internal class CategoryFilterTests
{
[Test]
public void Create_TestFilter_ConditionIsEmpty()
{
// 1. Arrange + Act
ITestModel testModel = Substitute.For<ITestModel>();
CategoryFilter filter = new CategoryFilter(testModel);

// 2. Assert
Assert.That(filter.Condition, Is.Empty);
}

[TestCase(new[] { "CategoryA" }, new[] { "CategoryA" })]
[TestCase(new[] { "CategoryA", "CategoryB" }, new[] { "CategoryB" })]
[TestCase(new[] { "CategoryB" }, new[] { "CategoryA", "CategoryB" })]
[TestCase(new[] { CategoryFilter.NoCategory }, new string[]{ })]

public void IsMatching_CategoryMatchesCategoryFilter_ReturnsTrue(IList<string> categoryFilter, IList<string> testCategories)
{
// 1. Arrange
ITestModel testModel = Substitute.For<ITestModel>();
CategoryFilter filter = new CategoryFilter(testModel);
filter.Condition = categoryFilter;

string xml = CreateTestcaseXml("1", "TestA", testCategories);
TestNode testNode = new TestNode(xml);

// 2. Act
bool isMatch = filter.IsMatching(testNode);

// 3. Assert
Assert.That(isMatch, Is.True);
}

[TestCase(new[] { "CategoryA" }, new[] { "CategoryB" })]
[TestCase(new[] { "CategoryA" }, new[] { "" })]
[TestCase(new[] { CategoryFilter.NoCategory }, new[] { "CategoryB" })]
public void IsMatching_CategoryNotMatchesCategoryFilter_ReturnsFalse(IList<string> categoryFilter, IList<string> testCategories)
{
// 1. Arrange
ITestModel testModel = Substitute.For<ITestModel>();
CategoryFilter filter = new CategoryFilter(testModel);
filter.Condition = categoryFilter;

string xml = CreateTestcaseXml("1", "TestA", testCategories);
TestNode testNode = new TestNode(xml);

// 2. Act
bool isMatch = filter.IsMatching(testNode);

// 3. Assert
Assert.That(isMatch, Is.False);
}

[Test]
public void Init_Condition_ContainsAllCategories()
{
// 1. Arrange
var availableCategories = new List<string>() { "CategoryA", "CategoryB" };
ITestModel testModel = Substitute.For<ITestModel>();
testModel.AvailableCategories.Returns(availableCategories);
CategoryFilter filter = new CategoryFilter(testModel);
filter.Condition = new[] { "CategoryA" };

// 2. Act
filter.Init();

// 3. Assert
Assert.That(filter.Condition, Has.Exactly(3).Items);
Assert.That(filter.Condition, Does.Contain("CategoryA"));
Assert.That(filter.Condition, Does.Contain("CategoryB"));
Assert.That(filter.Condition, Does.Contain(CategoryFilter.NoCategory));
}

[Test]
public void Reset_Condition_ContainsAllCategories()
{
// 1. Arrange
var availableCategories = new List<string>() { "CategoryA", "CategoryB" };
ITestModel testModel = Substitute.For<ITestModel>();
testModel.AvailableCategories.Returns(availableCategories);
CategoryFilter filter = new CategoryFilter(testModel);
filter.Condition = new[] { "CategoryA" };

// 2. Act
filter.Reset();

// 3. Assert
Assert.That(filter.Condition, Has.Exactly(3).Items);
Assert.That(filter.Condition, Does.Contain("CategoryA"));
Assert.That(filter.Condition, Does.Contain("CategoryB"));
Assert.That(filter.Condition, Does.Contain(CategoryFilter.NoCategory));
}

[Test]
public void IsActive_Condition_IsSet_ReturnsTrue()
{
// 1. Arrange
var availableCategories = new List<string>() { "CategoryA", "CategoryB" };
ITestModel testModel = Substitute.For<ITestModel>();
testModel.AvailableCategories.Returns(availableCategories);
CategoryFilter filter = new CategoryFilter(testModel);
filter.Init();

// 2. Act
filter.Condition = new[] { "CategoryA" };

// 3. Assert
Assert.That(filter.IsActive, Is.EqualTo(true));
}

[Test]
public void IsActive_FilterIsReset_ReturnsFalse()
{
// 1. Arrange
var availableCategories = new List<string>() { "CategoryA", "CategoryB" };
ITestModel testModel = Substitute.For<ITestModel>();
testModel.AvailableCategories.Returns(availableCategories);
CategoryFilter filter = new CategoryFilter(testModel);
filter.Init();
filter.Condition = new[] { "CategoryA" };

// 2. Act
filter.Reset();

// 3. Assert
Assert.That(filter.IsActive, Is.False);
}

[Test]
public void IsActive_FilterIsInit_ReturnsFalse()
{
// 1. Arrange
var availableCategories = new List<string>() { "CategoryA", "CategoryB" };
ITestModel testModel = Substitute.For<ITestModel>();
testModel.AvailableCategories.Returns(availableCategories);
CategoryFilter filter = new CategoryFilter(testModel);
filter.Init();
filter.Condition = new[] { "CategoryA" };

// 2. Act
filter.Init();

// 3. Assert
Assert.That(filter.IsActive, Is.False);
}

private string CreateTestcaseXml(string testId, string testName, IList<string> categories)
{
string str = $"<test-case id='{testId}' fullname='{testName}'> ";

str += "<properties> ";
foreach (string category in categories)
str += $"<property name='Category' value='{category}' /> ";
str += "</properties> ";

str += "</test-case> ";

return str;
}
}
}
Loading