Skip to content

Commit

Permalink
Merge pull request #1155 from TestCentric/issue-1148
Browse files Browse the repository at this point in the history
Provide filter logic of TestNodes; starting with filtering by outcome
  • Loading branch information
CharliePoole authored Oct 25, 2024
2 parents 1e18cd2 + bb208d5 commit 54a7abd
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 4 deletions.
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
/// </summary>
public bool IsVisible { get; set; }
public int TestCount => IsSuite ? GetAttribute("testcasecount", 0) : 1;
public RunState RunState => GetRunState();

Expand Down

0 comments on commit 54a7abd

Please sign in to comment.