-
Notifications
You must be signed in to change notification settings - Fork 0
/
topological_sort_test.go
111 lines (102 loc) · 2.71 KB
/
topological_sort_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package dag
import (
"errors"
"fmt"
"testing"
)
var topologicalSortCases = []struct {
Name string
Graph func() (Graph, error)
ExpectError error
ExpectResult []string
}{
{
Name: "empty",
Graph: func() (Graph, error) {
return New()
},
ExpectResult: []string{},
},
{
Name: "one node",
Graph: func() (Graph, error) {
return New(
NewNode("1", Constant(1)),
)
},
ExpectResult: []string{"1"},
},
{
Name: "two nodes",
Graph: func() (Graph, error) {
return New(
NewNode("1", Constant(1),
NewNode("max", Max),
),
)
},
ExpectResult: []string{"1", "max"},
},
{
Name: "assignment",
Graph: assignmentGraph,
},
}
func TestTopologicalSort(t *testing.T) {
for i, test := range topologicalSortCases {
t.Run(fmt.Sprintf("%d_%s", i, test.Name), func(t *testing.T) {
graph, err := test.Graph()
if err != nil && !errors.Is(err, test.ExpectError) {
t.Fatalf("unexpected error from calling Graph(): %s", err)
}
sorted, err := graph.TopologicalSort()
if err != nil && !errors.Is(err, test.ExpectError) {
t.Fatalf("unexpected error from calling TopologicalSort(): %s", err)
}
ids := make([]string, len(sorted))
for i, node := range sorted {
ids[i] = node.ID
}
t.Log("Result:", ids)
// If a specific result is expected, assert that it matches.
if test.ExpectResult != nil {
if len(sorted) != len(test.ExpectResult) {
t.Fatalf("expected result to have length %d but got %d", len(test.ExpectResult), len(sorted))
}
for i, expectNodeID := range test.ExpectResult {
if nodeID := sorted[i].ID; nodeID != expectNodeID {
t.Fatalf("unexpected sorting result: want %s at position %d but got %s", expectNodeID, i, nodeID)
}
}
}
// Assert that the result is a topological ordering.
// For each node, we check that every dependency was visited first.
// Create a list of the dependencies for each node.
deps := map[string]map[string]struct{}{}
for id := range graph {
deps[id] = make(map[string]struct{})
}
graph.Walk(func(current *Node, prev []*Node) error {
for _, dep := range prev {
deps[current.ID][dep.ID] = struct{}{}
}
return nil
})
// Record whether each Node has been visited.
visited := map[string]bool{}
for id := range graph {
visited[id] = false
}
// Step through the topological ordering while marking each node as visited,
// and checking if the deps of each node have also been visited yet.
for _, node := range sorted {
for dep := range deps[node.ID] {
if _, ok := visited[dep]; !ok {
t.Fatalf("invalid result order: node %s should come before node %s", dep, node.ID)
}
}
visited[node.ID] = true
}
})
}
}