Skip to content

Commit

Permalink
tests for topological sort
Browse files Browse the repository at this point in the history
  • Loading branch information
eosfor committed Oct 7, 2024
1 parent 285f770 commit 0e94fd0
Show file tree
Hide file tree
Showing 4 changed files with 264 additions and 27 deletions.
101 changes: 101 additions & 0 deletions PSGraph.Tests/GetGraphTopologicSortCmdletTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using Xunit;
using System;
using System.Management.Automation;
using PSGraph.Model;
using FluentAssertions;
using System.Collections.Generic;
using System.Linq;

namespace PSGraph.Tests
{
public class GetGraphTopologicSortCmdletTests : IDisposable
{
private PowerShell _powershell;

public GetGraphTopologicSortCmdletTests()
{
_powershell = PowerShell.Create();
_powershell.AddCommand("Import-Module")
.AddParameter("Assembly", typeof(PSGraph.Cmdlets.GetGraphTopologicSort).Assembly);
_powershell.Invoke();
_powershell.Commands.Clear();
}

public void Dispose()
{
_powershell.Dispose();
}

[Fact]
public void GetGraphTopologicSort_ShouldReturnTopologicallySortedVertices()
{
// Arrange
var graph = GraphTestData.SimpleTestGraph6; // Using real graph data

// Add the parameters for Get-GraphTopologicSort
_powershell.AddCommand("Get-GraphTopologicSort")
.AddParameter("Graph", graph);

// Act
var results = _powershell.Invoke();

// Assert
results.Should().NotBeNullOrEmpty();
var sortedVertices = results[0].BaseObject as IEnumerable<PSVertex>;
sortedVertices.Should().NotBeNull();

// Check that vertices are sorted topologically.
// In SimpleTestGraph5, A should appear before D and E, D and E before F, etc.
var sortedList = sortedVertices.ToList();
var vertexA = sortedList.FindIndex(v => v.Label == "A");
var vertexD = sortedList.FindIndex(v => v.Label == "D");
var vertexE = sortedList.FindIndex(v => v.Label == "E");
var vertexF = sortedList.FindIndex(v => v.Label == "F");


vertexA.Should().BeLessThan(vertexD);
vertexA.Should().BeLessThan(vertexE);
vertexD.Should().BeLessThan(vertexF);
vertexE.Should().BeLessThan(vertexF);
}

[Fact]
public void GetGraphTopologicSort_ShouldReturnEmptyForEmptyGraph()
{
// Arrange
var emptyGraph = new PsBidirectionalGraph(); // Create an empty graph

// Add the parameters for Get-GraphTopologicSort
_powershell.AddCommand("Get-GraphTopologicSort")
.AddParameter("Graph", emptyGraph);

// Act
var results = _powershell.Invoke();

// Assert
results.Should().ContainSingle("because the graph contains a single empty element")
.Which.Should().BeOfType<PSObject>()
.Which.BaseObject.Should().BeAssignableTo<IEnumerable<PSVertex>>()
.Which.Should().BeEmpty("because the single result is an empty collection of vertices");


}

[Fact]
public void GetGraphTopologicSort_ShouldThrowExceptionForCyclicGraph()
{
// Arrange
var graph = GraphTestData.SimpleTestGraph1; // Using a cyclic graph

// Add the parameters for Get-GraphTopologicSort
_powershell.AddCommand("Get-GraphTopologicSort")
.AddParameter("Graph", graph);

// Act
Action act = () => _powershell.Invoke();

// Assert
act.Should().Throw<Exception>().WithMessage("*The graph contains at least one cycle*");
}
}
}
102 changes: 75 additions & 27 deletions PSGraph.Tests/GraphTestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ public static class GraphTestData
public static PsBidirectionalGraph SimpleTestGraph3 { get; private set; }
public static PsBidirectionalGraph SimpleTestGraph4 { get; private set; }
public static PsBidirectionalGraph DSMFull { get; private set; }

public static PsBidirectionalGraph SimpleTestGraph5 { get; private set; }
public static PsBidirectionalGraph SimpleTestGraph6 { get; private set; }

static GraphTestData()
{
Expand All @@ -26,6 +27,7 @@ static GraphTestData()
SimpleTestGraph3 = InitializeSimpleTestGraph3();
SimpleTestGraph4 = InitializeSimpleTestGraph4();
SimpleTestGraph5 = InitializeSimpleTestGraph5();
SimpleTestGraph6 = InitializeSimpleTestGraph6();
DSMFull = InitializeDSMFull();
}

Expand All @@ -42,41 +44,87 @@ private static PsBidirectionalGraph InitializeSimpleTestGraph5()
g.AddVertex(new PSVertex("B"));
g.AddVertex(new PSVertex("H"));
g.AddVertex(new PSVertex("I"));
// Add-Edge -From A -To D -Graph $g | Out-Null
// Add-Edge -From A -To E -Graph $g | Out-Null
// Add-Edge -From D -to F -Graph $g | Out-Null
// Add-Edge -From E -To F -Graph $g | Out-Null
// Add-Edge -From G -To M -Graph $g | Out-Null
// Add-Edge -From B -To E -Graph $g | Out-Null
// Add-Edge -From B -To G -Graph $g | Out-Null
// Add-Edge -From B -To H -Graph $g | Out-Null
// Add-Edge -From H -To I -Graph $g | Out-Null
// Add-Edge -From M -To B -Graph $g | Out-Null



// Add-Edge -From A -To D -Graph $g | Out-Null
// Add-Edge -From A -To E -Graph $g | Out-Null
// Add-Edge -From D -to F -Graph $g | Out-Null
// Add-Edge -From E -To F -Graph $g | Out-Null
// Add-Edge -From G -To M -Graph $g | Out-Null
// Add-Edge -From B -To E -Graph $g | Out-Null
// Add-Edge -From B -To G -Graph $g | Out-Null
// Add-Edge -From B -To H -Graph $g | Out-Null
// Add-Edge -From H -To I -Graph $g | Out-Null
// Add-Edge -From M -To B -Graph $g | Out-Null


g.AddEdge(new PSEdge(new PSVertex("A"), new PSVertex("D"), new PSEdgeTag()));
g.AddEdge(new PSEdge(new PSVertex("A"), new PSVertex("E"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("D"), new PSVertex("F"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("E"), new PSVertex("F"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("G"), new PSVertex("M"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("B"), new PSVertex("E"), new PSEdgeTag()));
g.AddEdge(new PSEdge(new PSVertex("B"), new PSVertex("G"), new PSEdgeTag()));
g.AddEdge(new PSEdge(new PSVertex("B"), new PSVertex("H"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("H"), new PSVertex("I"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("M"), new PSVertex("B"), new PSEdgeTag()));


return g;
}

private static PsBidirectionalGraph InitializeSimpleTestGraph6()
{
var g = new PsBidirectionalGraph();

g.AddVertex(new PSVertex("A"));
g.AddVertex(new PSVertex("D"));
g.AddVertex(new PSVertex("E"));
g.AddVertex(new PSVertex("F"));
g.AddVertex(new PSVertex("G"));
g.AddVertex(new PSVertex("M"));
g.AddVertex(new PSVertex("B"));
g.AddVertex(new PSVertex("H"));
g.AddVertex(new PSVertex("I"));


// Add-Edge -From A -To D -Graph $g | Out-Null
// Add-Edge -From A -To E -Graph $g | Out-Null
// Add-Edge -From D -to F -Graph $g | Out-Null
// Add-Edge -From E -To F -Graph $g | Out-Null
// Add-Edge -From G -To M -Graph $g | Out-Null
// Add-Edge -From B -To E -Graph $g | Out-Null
// Add-Edge -From B -To G -Graph $g | Out-Null
// Add-Edge -From B -To H -Graph $g | Out-Null
// Add-Edge -From H -To I -Graph $g | Out-Null
// Add-Edge -From M -To B -Graph $g | Out-Null


g.AddEdge(new PSEdge(new PSVertex("A"), new PSVertex("D"), new PSEdgeTag()));
g.AddEdge(new PSEdge(new PSVertex("A"), new PSVertex("E"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("D"), new PSVertex("F"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("E"), new PSVertex("F"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("G"), new PSVertex("M"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("B"), new PSVertex("E"), new PSEdgeTag()));
g.AddEdge(new PSEdge(new PSVertex("B"), new PSVertex("G"), new PSEdgeTag()));
g.AddEdge(new PSEdge(new PSVertex("B"), new PSVertex("H"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("H"), new PSVertex("I"), new PSEdgeTag()));

return g;
}

private static PsBidirectionalGraph? InitializeDSMFull(){

private static PsBidirectionalGraph? InitializeDSMFull()
{
var g = new PsBidirectionalGraph();

g.AddVertex(new PSVertex("A"));
Expand All @@ -91,14 +139,14 @@ private static PsBidirectionalGraph InitializeSimpleTestGraph5()
g.AddEdge(new PSEdge(new PSVertex("A"), new PSVertex("D"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("B"), new PSVertex("G"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("C"), new PSVertex("A"), new PSEdgeTag()));
g.AddEdge(new PSEdge(new PSVertex("C"), new PSVertex("B"), new PSEdgeTag()));
g.AddEdge(new PSEdge(new PSVertex("C"), new PSVertex("F"), new PSEdgeTag()));
g.AddEdge(new PSEdge(new PSVertex("C"), new PSVertex("G"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("D"), new PSVertex("B"), new PSEdgeTag()));

g.AddEdge(new PSEdge(new PSVertex("E"), new PSVertex("C"), new PSEdgeTag()));
g.AddEdge(new PSEdge(new PSVertex("E"), new PSVertex("F"), new PSEdgeTag()));

Expand All @@ -110,7 +158,7 @@ private static PsBidirectionalGraph InitializeSimpleTestGraph5()
private static PsBidirectionalGraph InitializeSimpleTestGraph4()
{
var d = System.IO.Directory.GetCurrentDirectory();
var testGraphPath = System.IO.Path.Combine(d, "Data" ,"vms.graphml");
var testGraphPath = System.IO.Path.Combine(d, "Data", "vms.graphml");
var graph = new PsBidirectionalGraph(false);
using (var xmlReader = XmlReader.Create(testGraphPath))
{
Expand Down
21 changes: 21 additions & 0 deletions PSGraphv2/cmdlets/graph/GetGraphTopologicSort.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Management.Automation;
using QuikGraph.Algorithms;
using PSGraph.Model;

namespace PSGraph.Cmdlets
{
[Cmdlet(VerbsCommon.Get, "GraphTopologicSort")]
public class GetGraphTopologicSort : PSCmdlet
{
[Parameter(Mandatory = true)]
[ValidateNotNullOrEmpty]
public PsBidirectionalGraph Graph;

protected override void EndProcessing()
{
var res = Graph.TopologicalSort();
if (res != null)
WriteObject(res);
}
}
}
67 changes: 67 additions & 0 deletions PsGraph.Pester.Tests/PSGraph.TopologySort.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
BeforeAll {
Import-Module "/workspaces/PSGraph/PSGraph.Tests/bin/Debug/net8.0/PSQuickGraph.psd1"
}

Describe 'Get-GraphTopologicSort' {
BeforeEach {
# Initialize a new graph before each test
$graph = New-Graph

# Add vertices
Add-Vertex -Graph $graph -Vertex 'A'
Add-Vertex -Graph $graph -Vertex 'B'
Add-Vertex -Graph $graph -Vertex 'C'
Add-Vertex -Graph $graph -Vertex 'D'
Add-Vertex -Graph $graph -Vertex 'E'

# Add edges
Add-Edge -From 'A' -To 'B' -Graph $graph | Out-Null
Add-Edge -From 'A' -To 'C' -Graph $graph | Out-Null
Add-Edge -From 'B' -To 'D' -Graph $graph | Out-Null
Add-Edge -From 'C' -To 'D' -Graph $graph | Out-Null
Add-Edge -From 'D' -To 'E' -Graph $graph | Out-Null
}

It 'Should return a topologically sorted list of vertices' {
$sortedVertices = Get-GraphTopologicSort -Graph $graph

$sortedVertices | Should -Not -BeNullOrEmpty

$sortedVertices2 = $sortedVertices | ForEach-Object { $_ }


# $sortedVertices2 | Should -BeExactly @('A', 'C', 'B', 'D', 'E')

# Additional checks to ensure the topological order is valid
# 'A' must come before 'B', 'C', 'D', 'E'
$sortedVertices2.IndexOf('A') | Should -BeLessThan $sortedVertices2.IndexOf('B')
$sortedVertices2.IndexOf('A') | Should -BeLessThan $sortedVertices2.IndexOf('C')
$sortedVertices2.IndexOf('A') | Should -BeLessThan $sortedVertices2.IndexOf('D')
$sortedVertices2.IndexOf('A') | Should -BeLessThan $sortedVertices2.IndexOf('E'

# 'B' must come before 'D'
$sortedVertices2.IndexOf('B') | Should -BeLessThan $sortedVertices2.IndexOf('D')

# 'C' must come before 'D'
$sortedVertices2.IndexOf('C') | Should -BeLessThan $sortedVertices2.IndexOf('D')

# 'D' must come before 'E'
$sortedVertices2.IndexOf('D') | Should -BeLessThan $sortedVertices2.IndexOf('E')


}

It 'Should return $null for an empty graph' {
$emptyGraph = New-Graph
$sortedVertices = Get-GraphTopologicSort -Graph $emptyGraph

$sortedVertices | Should -BeNullOrEmpty
}

It 'Should throw an error for a cyclic graph' {
# Add a cycle
Add-Edge -From 'E' -To 'A' -Graph $graph | Out-Null

{ Get-GraphTopologicSort -Graph $graph } | Should -Throw -ErrorId 'QuikGraph.NonAcyclicGraphException,PSGraph.Cmdlets.GetGraphTopologicSort'
}
}

0 comments on commit 0e94fd0

Please sign in to comment.