blob: f33fabc7f1516dc34a456ecff12fc3c44441a405 [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package toposort
2
3import (
4 "reflect"
5 "testing"
6)
7
8func toStringSlice(input []interface{}) (output []string) {
9 output = make([]string, len(input))
10 for ix, ival := range input {
11 output[ix] = ival.(string)
12 }
13 return
14}
15
16func toStringCycles(input [][]interface{}) (output [][]string) {
17 output = make([][]string, len(input))
18 for ix, islice := range input {
19 output[ix] = toStringSlice(islice)
20 }
21 return
22}
23
24type orderChecker struct {
25 t *testing.T
26 original []string
27 orderMap map[string]int
28}
29
30func makeOrderChecker(t *testing.T, slice []interface{}) orderChecker {
31 result := orderChecker{t, toStringSlice(slice), make(map[string]int)}
32 for ix, val := range result.original {
33 result.orderMap[val] = ix
34 }
35 return result
36}
37
38func (oc *orderChecker) findValue(val string) int {
39 if index, ok := oc.orderMap[val]; ok {
40 return index
41 }
42 oc.t.Errorf("Couldn't find val %v in slice %v", val, oc.original)
43 return -1
44}
45
46func (oc *orderChecker) expectOrder(before, after string) {
47 if oc.findValue(before) >= oc.findValue(after) {
48 oc.t.Errorf("Expected %v before %v, slice %v", before, after, oc.original)
49 }
50}
51
52// Since sort is deterministic we can expect a particular total order, in
53// addition to the partial order checks.
54func (oc *orderChecker) expectTotalOrder(expect ...string) {
55 if !reflect.DeepEqual(oc.original, expect) {
56 oc.t.Errorf("Expected order %v, actual %v", expect, oc.original)
57 }
58}
59
60func expectCycles(t *testing.T, actual [][]interface{}, expect [][]string) {
61 actualStr := toStringCycles(actual)
62 if !reflect.DeepEqual(actualStr, expect) {
63 t.Errorf("Expected cycles %v, actual %v", expect, actualStr)
64 }
65}
66
67func TestSortDag(t *testing.T) {
68 // This is the graph:
69 // ,-->B
70 // |
71 // A-->C---->D
72 // | \
73 // | `-->E--.
74 // `-------------`-->F
Todd Wangc2cba882014-10-14 10:59:56 -070075 var sorter Sorter
Jiri Simsa5293dcb2014-05-10 09:56:38 -070076 sorter.AddEdge("A", "B")
77 sorter.AddEdge("A", "C")
78 sorter.AddEdge("A", "F")
79 sorter.AddEdge("C", "D")
80 sorter.AddEdge("C", "E")
81 sorter.AddEdge("E", "F")
82 sorted, cycles := sorter.Sort()
83 oc := makeOrderChecker(t, sorted)
84 oc.expectOrder("B", "A")
85 oc.expectOrder("C", "A")
86 oc.expectOrder("D", "A")
87 oc.expectOrder("E", "A")
88 oc.expectOrder("F", "A")
89 oc.expectOrder("D", "C")
90 oc.expectOrder("E", "C")
91 oc.expectOrder("F", "C")
92 oc.expectOrder("F", "E")
93 oc.expectTotalOrder("B", "D", "F", "E", "C", "A")
94 expectCycles(t, cycles, [][]string{})
95}
96
97func TestSortSelfCycle(t *testing.T) {
98 // This is the graph:
99 // ,---.
100 // | |
101 // A<--'
Todd Wangc2cba882014-10-14 10:59:56 -0700102 var sorter Sorter
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700103 sorter.AddEdge("A", "A")
104 sorted, cycles := sorter.Sort()
105 oc := makeOrderChecker(t, sorted)
106 oc.expectTotalOrder("A")
107 expectCycles(t, cycles, [][]string{{"A", "A"}})
108}
109
110func TestSortCycle(t *testing.T) {
111 // This is the graph:
112 // ,-->B-->C
113 // | |
114 // A<------'
Todd Wangc2cba882014-10-14 10:59:56 -0700115 var sorter Sorter
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700116 sorter.AddEdge("A", "B")
117 sorter.AddEdge("B", "C")
118 sorter.AddEdge("C", "A")
119 sorted, cycles := sorter.Sort()
120 oc := makeOrderChecker(t, sorted)
121 oc.expectTotalOrder("C", "B", "A")
122 expectCycles(t, cycles, [][]string{{"A", "C", "B", "A"}})
123}
124
125func TestSortContainsCycle1(t *testing.T) {
126 // This is the graph:
127 // ,-->B
128 // | ,-----.
129 // | v |
130 // A-->C---->D
131 // | \
132 // | `-->E--.
133 // `-------------`-->F
Todd Wangc2cba882014-10-14 10:59:56 -0700134 var sorter Sorter
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700135 sorter.AddEdge("A", "B")
136 sorter.AddEdge("A", "C")
137 sorter.AddEdge("A", "F")
138 sorter.AddEdge("C", "D")
139 sorter.AddEdge("C", "E")
140 sorter.AddEdge("D", "C") // creates the cycle
141 sorter.AddEdge("E", "F")
142 sorted, cycles := sorter.Sort()
143 oc := makeOrderChecker(t, sorted)
144 oc.expectOrder("B", "A")
145 oc.expectOrder("C", "A")
146 oc.expectOrder("D", "A")
147 oc.expectOrder("E", "A")
148 oc.expectOrder("F", "A")
149 // The difference with the dag is C, D may be in either order.
150 oc.expectOrder("E", "C")
151 oc.expectOrder("F", "C")
152 oc.expectOrder("F", "E")
153 oc.expectTotalOrder("B", "D", "F", "E", "C", "A")
154 expectCycles(t, cycles, [][]string{{"C", "D", "C"}})
155}
156
157func TestSortContainsCycle2(t *testing.T) {
158 // This is the graph:
159 // ,-->B
160 // | ,-------------.
161 // | v |
162 // A-->C---->D |
163 // | \ |
164 // | `-->E--. |
165 // `-------------`-->F
Todd Wangc2cba882014-10-14 10:59:56 -0700166 var sorter Sorter
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700167 sorter.AddEdge("A", "B")
168 sorter.AddEdge("A", "C")
169 sorter.AddEdge("A", "F")
170 sorter.AddEdge("C", "D")
171 sorter.AddEdge("C", "E")
172 sorter.AddEdge("E", "F")
173 sorter.AddEdge("F", "C") // creates the cycle
174 sorted, cycles := sorter.Sort()
175 oc := makeOrderChecker(t, sorted)
176 oc.expectOrder("B", "A")
177 oc.expectOrder("C", "A")
178 oc.expectOrder("D", "A")
179 oc.expectOrder("E", "A")
180 oc.expectOrder("F", "A")
181 oc.expectOrder("D", "C")
182 // The difference with the dag is C, E, F may be in any order.
183 oc.expectTotalOrder("B", "D", "F", "E", "C", "A")
184 expectCycles(t, cycles, [][]string{{"C", "F", "E", "C"}})
185}
186
187func TestSortMultiCycles(t *testing.T) {
188 // This is the graph:
189 // ,-->B
190 // | ,------------.
191 // | v |
192 // .--A-->C---->D |
193 // | ^ \ |
194 // | | `-->E--. |
195 // | | | | |
196 // | `---------' | |
197 // `---------------`-->F
Todd Wangc2cba882014-10-14 10:59:56 -0700198 var sorter Sorter
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700199 sorter.AddEdge("A", "B")
200 sorter.AddEdge("A", "C")
201 sorter.AddEdge("A", "F")
202 sorter.AddEdge("C", "D")
203 sorter.AddEdge("C", "E")
204 sorter.AddEdge("E", "A") // creates a cycle
205 sorter.AddEdge("E", "F")
206 sorter.AddEdge("F", "C") // creates a cycle
207 sorted, cycles := sorter.Sort()
208 oc := makeOrderChecker(t, sorted)
209 oc.expectOrder("B", "A")
210 oc.expectOrder("D", "A")
211 oc.expectOrder("F", "A")
212 oc.expectOrder("D", "C")
213 oc.expectTotalOrder("B", "D", "F", "E", "C", "A")
214 expectCycles(t, cycles, [][]string{{"A", "E", "C", "A"}, {"C", "F", "E", "C"}})
215}