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

Sort tree nodes by name or duration #1179

Merged
merged 2 commits into from
Feb 9, 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
6 changes: 4 additions & 2 deletions src/TestCentric/testcentric.gui/Presenters/DisplayStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ namespace TestCentric.Gui.Presenters
using Model;
using Model.Settings;
using Views;
using Elements;
using System.IO;
using System.Linq;

/// <summary>
Expand Down Expand Up @@ -96,6 +94,10 @@ public virtual void OnTestRunStarting()

public virtual void OnTestRunFinished()
{
if (_view.SortCommand.SelectedItem == TreeViewNodeComparer.Duration)
{
_view.InvokeIfRequired(() => _view.Sort());
}
if (_settings.Gui.TestTree.ShowTestDuration)
_view.InvokeIfRequired(() => UpdateTreeNodeNames());
}
Expand Down
112 changes: 112 additions & 0 deletions src/TestCentric/testcentric.gui/Presenters/TreeViewNodeComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// ***********************************************************************
// Copyright (c) Charlie Poole and TestCentric contributors.
// Licensed under the MIT License. See LICENSE file in root directory.
// ***********************************************************************

using System.Collections;
using System.Windows.Forms;
using TestCentric.Gui.Model;

namespace TestCentric.Gui.Presenters
{
/// <summary>
/// This class is responsible for the sorting functionality of tree nodes
/// by providing different IComparer implementations.
/// </summary>
public class TreeViewNodeComparer
{
public const string Name = "Name";
public const string Duration = "Duration";
public const string Ascending = "Ascending";
public const string Descending = "Descending";


/// <summary>
/// Get a IComparer implementation used to sort the tree nodes
/// The sortModes are used by the context menu items in their Tag property
/// </summary>
public static IComparer GetComparer(ITestModel model, string sortMode, string sortDirection)
{
bool ascending = sortDirection == Ascending;

if (sortMode == Duration)
return new DurationComparer(model, ascending);

return new NameComparer(ascending);
}

/// <summary>
/// The NameComparer uses the Name of the TestNodes (either Namespace or class or method name)
/// It's invoked by Windows Forms for all nodes of one hierarchy level to provide a proper ordering
/// </summary>
private class NameComparer : IComparer
{
private bool _ascending;

internal NameComparer(bool ascending)
{
_ascending = ascending;
}

public int Compare(object x, object y)
{
TreeNode node1 = x as TreeNode;
TreeNode node2 = y as TreeNode;

if (!_ascending)
Swap(ref node1, ref node2);

if (node1 != null && node2 != null)
return node1.Text.CompareTo(node2.Text);

return 1;
}
}

/// <summary>
/// The DurationComparer uses the Duration of the TestResults
/// It no test results are available yet, it uses the Name.
/// </summary>
private class DurationComparer : IComparer
{
private ITestModel _model;
private bool _ascending;

internal DurationComparer(ITestModel model, bool ascending)
{
_model = model;
_ascending = ascending;
}

public int Compare(object x, object y)
{
TreeNode node1 = x as TreeNode;
TreeNode node2 = y as TreeNode;

if (!_ascending)
Swap(ref node1, ref node2);

TestNode testNode1 = node1?.Tag as TestNode;
TestNode testNode2 = node2?.Tag as TestNode;

if (testNode1 == null || testNode2 == null)
return 1;

ResultNode resultNode1 = _model.GetResultForTest(testNode1.Id);
ResultNode resultNode2 = _model.GetResultForTest(testNode2.Id);

if (resultNode1 != null && resultNode2 != null)
return resultNode1.Duration.CompareTo(resultNode2.Duration);

return node1.Text.CompareTo(node2.Text); ;
}
}

internal static void Swap<T>(ref T x, ref T y)
{
T tmp = x;
x = y;
y = tmp;
}
}
}
22 changes: 18 additions & 4 deletions src/TestCentric/testcentric.gui/Presenters/TreeViewPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,16 @@
// ***********************************************************************

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using TestCentric.Common;

namespace TestCentric.Gui.Presenters
{
using Model;
using Views;
using Dialogs;
using System.Xml;
using System.Drawing;
using System.IO;
using TestCentric.Gui.Controls;
using System.Collections;

/// <summary>
/// TreeViewPresenter is the presenter for the TestTreeView
Expand Down Expand Up @@ -44,6 +41,7 @@ public TreeViewPresenter(ITestTreeView treeView, ITestModel model, ITreeDisplayS
_view.ShowCheckBoxes.Checked = _view.CheckBoxes = _treeSettings.ShowCheckBoxes;
_view.ShowTestDuration.Checked = _treeSettings.ShowTestDuration;
_view.AlternateImageSet = _treeSettings.AlternateImageSet;
UpdateTreeViewSortMode();

WireUpEvents();
}
Expand Down Expand Up @@ -172,6 +170,10 @@ private void WireUpEvents()
Strategy?.UpdateTreeNodeNames();
};

_view.SortCommand.SelectionChanged += () => UpdateTreeViewSortMode();

_view.SortDirectionCommand.SelectionChanged += () => UpdateTreeViewSortMode();

_view.RunContextCommand.Execute += () =>
{
if (_view.ContextNode != null)
Expand Down Expand Up @@ -300,6 +302,18 @@ private void WireUpEvents()
//};
}

private void UpdateTreeViewSortMode()
{
var sortMode = _view.SortCommand.SelectedItem;

// Activate 'ShowTestDuration' in case sort by duration is selected
if (sortMode == TreeViewNodeComparer.Duration)
_view.ShowTestDuration.Checked = true;

IComparer comparer = TreeViewNodeComparer.GetComparer(_model, sortMode, _view.SortDirectionCommand.SelectedItem);
_view.Sort(comparer);
}

private void UpdateTreeSettingsFromVisualState(VisualState visualState)
{
_treeSettings.DisplayFormat = visualState.DisplayStrategy;
Expand Down
7 changes: 7 additions & 0 deletions src/TestCentric/testcentric.gui/Views/ITestTreeView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

namespace TestCentric.Gui.Views
{
using System.Collections;
using System.Collections.Generic;
using Elements;

Expand All @@ -29,6 +30,9 @@ public interface ITestTreeView : IView
IToolStripMenu ActiveConfiguration { get; }
IChecked ShowCheckBoxes { get; }
IChecked ShowTestDuration { get; }
ISelection SortCommand { get; }
ISelection SortDirectionCommand { get; }

ICommand ExpandAllCommand { get; }
ICommand CollapseAllCommand { get; }
ICommand CollapseToFixturesCommand { get; }
Expand Down Expand Up @@ -66,6 +70,9 @@ public interface ITestTreeView : IView
void Add(TreeNode treeNode);
void ExpandAll();
void CollapseAll();

void Sort();
void Sort(IComparer comparer);
void SetImageIndex(TreeNode treeNode, int imageIndex);

/// <summary>
Expand Down
73 changes: 73 additions & 0 deletions src/TestCentric/testcentric.gui/Views/TestTreeView.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions src/TestCentric/testcentric.gui/Views/TestTreeView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace TestCentric.Gui.Views
{
using System;
using System.Collections;
using System.Collections.Generic;
using Elements;

Expand Down Expand Up @@ -48,6 +49,8 @@ public TestTreeView()
CollapseToFixturesCommand = new CommandMenuElement(collapseToFixturesMenuItem);
TestPropertiesCommand = new CommandMenuElement(testPropertiesMenuItem);
ViewAsXmlCommand = new CommandMenuElement(viewAsXmlMenuItem);
SortCommand = new CheckedToolStripMenuGroup("Sort", sortByNameMenuItem, sortByDurationMenuItem);
SortDirectionCommand = new CheckedToolStripMenuGroup("SortDirection", sortAscendingMenuItem, sortDescendingMenuItem);
OutcomeFilter = new MultiCheckedToolStripButtonGroup(new[] { filterOutcomePassedButton, filterOutcomeFailedButton, filterOutcomeWarningButton, filterOutcomeNotRunButton });
TextFilter = new ToolStripTextBoxElement(filterTextBox, "Filter...");
CategoryFilter = new ToolStripCategoryFilterButton(filterByCategory);
Expand Down Expand Up @@ -119,6 +122,9 @@ public bool CheckBoxes
public IToolStripMenu ActiveConfiguration { get; private set; }
public IChecked ShowCheckBoxes { get; private set; }
public IChecked ShowTestDuration { get; private set; }

public ISelection SortCommand { get; private set; }
public ISelection SortDirectionCommand { get; private set; }
public ICommand ExpandAllCommand { get; private set; }
public ICommand CollapseAllCommand { get; private set; }
public ICommand CollapseToFixturesCommand { get; private set; }
Expand Down Expand Up @@ -245,6 +251,28 @@ public void LoadAlternateImages(string imageSet)
this.Refresh();
}

/// <summary>
/// Apply the current active TreeViewNodeSorter to sort the tree view
/// </summary>
public void Sort()
{
// Restore selected node after tree sorting
var selectedNode = treeView.SelectedNode;
treeView.Sort();
treeView.SelectedNode = selectedNode;
}

/// <summary>
/// Set a comparer as the TreeViewNodeSorter to sort the tree view
/// </summary>
public void Sort(IComparer comparer)
{
// Restore selected node after tree sorting
var selectedNode = treeView.SelectedNode;
treeView.TreeViewNodeSorter = comparer;
treeView.SelectedNode = selectedNode;
}

#endregion

#region Helper Methods
Expand Down
Loading