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

Provide filter logic of TestNodes; starting with filtering by outcome #1155

Merged
merged 1 commit into from
Oct 25, 2024
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 @@ -38,7 +38,8 @@ public override void OnTestLoaded(TestNode testNode, VisualState visualState)
_foldedNodeNames.Clear();

foreach (var topLevelNode in testNode.Children)
_view.Add(CreateNUnitTreeNode(null, topLevelNode));
if (topLevelNode.IsVisible)
_view.Add(CreateNUnitTreeNode(null, topLevelNode));

if (visualState != null)
visualState.ApplyTo(_view.TreeView);
Expand Down Expand Up @@ -81,9 +82,8 @@ private TreeNode CreateNUnitTreeNode(TreeNode parentNode, TestNode testNode)
}

foreach (TestNode child in testNode.Children)
{
CreateNUnitTreeNode(parentNode, child);
}
if (child.IsVisible)
CreateNUnitTreeNode(parentNode, child);

return treeNode;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ private void WireUpEvents()
Strategy.OnTestRunFinished();
};

_model.Events.TestFilterChanged += (ea) =>
{
Strategy?.Reload();
};

_model.Events.TestFinished += OnTestFinished;
_model.Events.SuiteFinished += OnTestFinished;

Expand Down
20 changes: 20 additions & 0 deletions src/TestModel/model/ITestCentricTestFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************************
// Copyright (c) Charlie Poole and TestCentric contributors.
// Licensed under the MIT License. See LICENSE file in root directory.
// ***********************************************************************

using System.Collections.Generic;

namespace TestCentric.Gui.Model
{
/// <summary>
/// Provides filter functionality: by outcome, by duration, by category...
/// </summary>
public interface ITestCentricTestFilter
{
/// <summary>
/// Filters the loaded TestNodes by outcome
/// </summary>
IEnumerable<string> OutcomeFilter { get; set; }
}
}
1 change: 1 addition & 0 deletions src/TestModel/model/ITestEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,6 @@ public interface ITestEvents
event TestSelectionEventHandler SelectedTestsChanged;

event TestEventHandler CategorySelectionChanged;
event TestEventHandler TestFilterChanged;
}
}
5 changes: 5 additions & 0 deletions src/TestModel/model/ITestModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ public interface ITestModel : IDisposable

TestFilter CategoryFilter { get; }

/// <summary>
/// Provides filter functionality: by outcome, by duration, by category...
/// </summary>
ITestCentricTestFilter TestCentricTestFilter { get; }

#endregion

#region Methods
Expand Down
84 changes: 84 additions & 0 deletions src/TestModel/model/TestCentricTestFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// ***********************************************************************
// Copyright (c) Charlie Poole and TestCentric contributors.
// Licensed under the MIT License. See LICENSE file in root directory.
// ***********************************************************************

using System;
using System.Collections.Generic;
using System.Linq;

namespace TestCentric.Gui.Model
{
internal class TestCentricTestFilter : ITestCentricTestFilter
{
// By default: all outcome filters are enabled
List<string> _outcomeFilter = new List<string>()
{
"Passed",
"Failed",
"Ignored",
"Skipped",
"Inconclusive",
"Not Run",
};

internal TestCentricTestFilter(TestModel model, Action filterChangedEvent)
{
TestModel = model;
FireFilterChangedEvent = filterChangedEvent;
}

private ITestModel TestModel { get; }

private Action FireFilterChangedEvent;

public IEnumerable<string> OutcomeFilter
{
get => _outcomeFilter;

set
{
_outcomeFilter = value.ToList();
FilterNodes(TestModel.LoadedTests);
FireFilterChangedEvent();
}
}

private bool FilterNodes(TestNode testNode)
{
// 1. Check if any child is visible => parent must be visible too
bool childIsVisible = false;
foreach (TestNode child in testNode.Children)
if (FilterNodes(child))
childIsVisible = true;

// 2. Check if node itself is visible
bool isVisible = IsOutcomeFilterMatching(testNode);
testNode.IsVisible = isVisible || childIsVisible;
return testNode.IsVisible;
}

private bool IsOutcomeFilterMatching(TestNode testNode)
{
string outcome = "Not Run";

var result = TestModel.GetResultForTest(testNode.Id);
if (result != null)
{
switch (result.Outcome.Status)
{
case TestStatus.Failed:
case TestStatus.Passed:
case TestStatus.Inconclusive:
outcome = result.Outcome.Status.ToString();
break;
case TestStatus.Skipped:
outcome = result.Outcome.Label == "Ignored" ? "Ignored" : "Skippeed";
break;
}
}

return OutcomeFilter.Contains(outcome);
}
}
}
6 changes: 6 additions & 0 deletions src/TestModel/model/TestEventDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ public void FireCategorySelectionChanged()
CategorySelectionChanged?.Invoke(new TestEventArgs());
}

public void FireTestFilterChanged()
{
TestFilterChanged?.Invoke(new TestEventArgs());
}

#endregion

#region ITestEvents Implementation
Expand Down Expand Up @@ -164,6 +169,7 @@ public void FireCategorySelectionChanged()
public event TestSelectionEventHandler SelectedTestsChanged;

public event TestEventHandler CategorySelectionChanged;
public event TestEventHandler TestFilterChanged;

#endregion

Expand Down
3 changes: 3 additions & 0 deletions src/TestModel/model/TestModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public TestModel(ITestEngine testEngine, CommandLineOptions options = null)
RecentFiles = new RecentFiles(_settingsService);

Services = new TestServices(testEngine);
TestCentricTestFilter = new TestCentricTestFilter(this, () => _events.FireTestFilterChanged());

AvailableAgents = new List<string>(
Services.TestAgentService.GetAvailableAgents().Select((a) => a.AgentName));
Expand Down Expand Up @@ -207,6 +208,8 @@ public TestSelection SelectedTests

public TestFilter CategoryFilter { get; private set; } = TestFilter.Empty;

public ITestCentricTestFilter TestCentricTestFilter { get; private set; }

#endregion

#region Specifications passed as arguments to methods
Expand Down
5 changes: 5 additions & 0 deletions src/TestModel/model/TestNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class TestNode : ITestItem

public TestNode(XmlNode xmlNode)
{
IsVisible = true;
Xml = xmlNode;

// It's a quirk of the test engine that the test-run element does;
Expand Down Expand Up @@ -76,6 +77,10 @@ public TestFilter GetTestFilter()
public bool IsAssembly => Type == "Assembly";
public bool IsProject => Type == "Project";

/// <summary>
/// Controls if the TestNode should be visible or hidden in the TestTree
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"In the NUnit Test Tree" right?

/// </summary>
public bool IsVisible { get; set; }
public int TestCount => IsSuite ? GetAttribute("testcasecount", 0) : 1;
public RunState RunState => GetRunState();

Expand Down