github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/cmd/digraph/digraph_test.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 package main 5 6 import ( 7 "bytes" 8 "fmt" 9 "reflect" 10 "sort" 11 "strings" 12 "testing" 13 ) 14 15 func TestDigraph(t *testing.T) { 16 const g1 = ` 17 socks shoes 18 shorts pants 19 pants belt shoes 20 shirt tie sweater 21 sweater jacket 22 hat 23 ` 24 25 const g2 = ` 26 a b c 27 b d 28 c d 29 d c 30 ` 31 32 for _, test := range []struct { 33 name string 34 input string 35 cmd string 36 args []string 37 want string 38 }{ 39 {"nodes", g1, "nodes", nil, "belt\nhat\njacket\npants\nshirt\nshoes\nshorts\nsocks\nsweater\ntie\n"}, 40 {"reverse", g1, "reverse", []string{"jacket"}, "jacket\nshirt\nsweater\n"}, 41 {"transpose", g1, "transpose", nil, "belt pants\njacket sweater\npants shorts\nshoes pants\nshoes socks\nsweater shirt\ntie shirt\n"}, 42 {"forward", g1, "forward", []string{"socks"}, "shoes\nsocks\n"}, 43 {"forward multiple args", g1, "forward", []string{"socks", "sweater"}, "jacket\nshoes\nsocks\nsweater\n"}, 44 {"scss", g2, "sccs", nil, "a\nb\nc d\n"}, 45 {"scc", g2, "scc", []string{"d"}, "c\nd\n"}, 46 {"succs", g2, "succs", []string{"a"}, "b\nc\n"}, 47 {"preds", g2, "preds", []string{"c"}, "a\nd\n"}, 48 {"preds multiple args", g2, "preds", []string{"c", "d"}, "a\nb\nc\nd\n"}, 49 } { 50 t.Run(test.name, func(t *testing.T) { 51 stdin = strings.NewReader(test.input) 52 stdout = new(bytes.Buffer) 53 if err := digraph(test.cmd, test.args); err != nil { 54 t.Fatal(err) 55 } 56 57 got := stdout.(fmt.Stringer).String() 58 if got != test.want { 59 t.Errorf("digraph(%s, %s) = got %q, want %q", test.cmd, test.args, got, test.want) 60 } 61 }) 62 } 63 64 // TODO(adonovan): 65 // - test somepath (it's nondeterministic). 66 // - test errors 67 } 68 69 func TestAllpaths(t *testing.T) { 70 for _, test := range []struct { 71 name string 72 in string 73 to string // from is always "A" 74 want string 75 }{ 76 { 77 name: "Basic", 78 in: "A B\nB C", 79 to: "B", 80 want: "A B\n", 81 }, 82 { 83 name: "Long", 84 in: "A B\nB C\n", 85 to: "C", 86 want: "A B\nB C\n", 87 }, 88 { 89 name: "Cycle Basic", 90 in: "A B\nB A", 91 to: "B", 92 want: "A B\nB A\n", 93 }, 94 { 95 name: "Cycle Path Out", 96 // A <-> B -> C -> D 97 in: "A B\nB A\nB C\nC D", 98 to: "C", 99 want: "A B\nB A\nB C\n", 100 }, 101 { 102 name: "Cycle Path Out Further Out", 103 // A -> B <-> C -> D -> E 104 in: "A B\nB C\nC D\nC B\nD E", 105 to: "D", 106 want: "A B\nB C\nC B\nC D\n", 107 }, 108 { 109 name: "Two Paths Basic", 110 // /-> C --\ 111 // A -> B -- -> E -> F 112 // \-> D --/ 113 in: "A B\nB C\nC E\nB D\nD E\nE F", 114 to: "E", 115 want: "A B\nB C\nB D\nC E\nD E\n", 116 }, 117 { 118 name: "Two Paths With One Immediately From Start", 119 // /-> B -+ -> D 120 // A -- | 121 // \-> C <+ 122 in: "A B\nA C\nB C\nB D", 123 to: "C", 124 want: "A B\nA C\nB C\n", 125 }, 126 { 127 name: "Two Paths Further Up", 128 // /-> B --\ 129 // A -- -> D -> E -> F 130 // \-> C --/ 131 in: "A B\nA C\nB D\nC D\nD E\nE F", 132 to: "E", 133 want: "A B\nA C\nB D\nC D\nD E\n", 134 }, 135 { 136 // We should include A - C - D even though it's further up the 137 // second path than D (which would already be in the graph by 138 // the time we get around to integrating the second path). 139 name: "Two Splits", 140 // /-> B --\ /-> E --\ 141 // A -- -> D -- -> G -> H 142 // \-> C --/ \-> F --/ 143 in: "A B\nA C\nB D\nC D\nD E\nD F\nE G\nF G\nG H", 144 to: "G", 145 want: "A B\nA C\nB D\nC D\nD E\nD F\nE G\nF G\n", 146 }, 147 { 148 // D - E should not be duplicated. 149 name: "Two Paths - Two Splits With Gap", 150 // /-> B --\ /-> F --\ 151 // A -- -> D -> E -- -> H -> I 152 // \-> C --/ \-> G --/ 153 in: "A B\nA C\nB D\nC D\nD E\nE F\nE G\nF H\nG H\nH I", 154 to: "H", 155 want: "A B\nA C\nB D\nC D\nD E\nE F\nE G\nF H\nG H\n", 156 }, 157 } { 158 t.Run(test.name, func(t *testing.T) { 159 stdin = strings.NewReader(test.in) 160 stdout = new(bytes.Buffer) 161 if err := digraph("allpaths", []string{"A", test.to}); err != nil { 162 t.Fatal(err) 163 } 164 165 got := stdout.(fmt.Stringer).String() 166 if got != test.want { 167 t.Errorf("digraph(allpaths, A, %s) = got %q, want %q", test.to, got, test.want) 168 } 169 }) 170 } 171 } 172 173 func TestSomepath(t *testing.T) { 174 for _, test := range []struct { 175 name string 176 in string 177 to string 178 // somepath is non-deterministic, so we have to provide all the 179 // possible options. Each option is separated with |. 180 wantAnyOf string 181 }{ 182 { 183 name: "Basic", 184 in: "A B\n", 185 to: "B", 186 wantAnyOf: "A B", 187 }, 188 { 189 name: "Basic With Cycle", 190 in: "A B\nB A", 191 to: "B", 192 wantAnyOf: "A B", 193 }, 194 { 195 name: "Two Paths", 196 // /-> B --\ 197 // A -- -> D 198 // \-> C --/ 199 in: "A B\nA C\nB D\nC D", 200 to: "D", 201 wantAnyOf: "A B\nB D|A C\nC D", 202 }, 203 } { 204 t.Run(test.name, func(t *testing.T) { 205 stdin = strings.NewReader(test.in) 206 stdout = new(bytes.Buffer) 207 if err := digraph("somepath", []string{"A", test.to}); err != nil { 208 t.Fatal(err) 209 } 210 211 got := stdout.(fmt.Stringer).String() 212 lines := strings.Split(got, "\n") 213 sort.Strings(lines) 214 got = strings.Join(lines[1:], "\n") 215 216 var oneMatch bool 217 for _, want := range strings.Split(test.wantAnyOf, "|") { 218 if got == want { 219 oneMatch = true 220 } 221 } 222 if !oneMatch { 223 t.Errorf("digraph(somepath, A, %s) = got %q, want any of\n%s", test.to, got, test.wantAnyOf) 224 } 225 }) 226 } 227 } 228 229 func TestSplit(t *testing.T) { 230 for _, test := range []struct { 231 line string 232 want []string 233 }{ 234 {`one "2a 2b" three`, []string{"one", "2a 2b", "three"}}, 235 {`one tw"\n\x0a\u000a\012"o three`, []string{"one", "tw\n\n\n\no", "three"}}, 236 } { 237 got, err := split(test.line) 238 if err != nil { 239 t.Errorf("split(%s) failed: %v", test.line, err) 240 } 241 if !reflect.DeepEqual(got, test.want) { 242 t.Errorf("split(%s) = %v, want %v", test.line, got, test.want) 243 } 244 } 245 } 246 247 func TestQuotedLength(t *testing.T) { 248 for _, test := range []struct { 249 input string 250 want int 251 }{ 252 {`"abc"`, 5}, 253 {`"abc"def`, 5}, 254 {`"abc\"d"ef`, 8}, // "abc\"d" is consumed, ef is residue 255 {`"\012\n\x0a\u000a\U0000000a"`, 28}, 256 {"\"\xff\"", 3}, // bad UTF-8 is ok 257 {`"\xff"`, 6}, // hex escape for bad UTF-8 is ok 258 } { 259 got, ok := quotedLength(test.input) 260 if !ok { 261 got = 0 262 } 263 if got != test.want { 264 t.Errorf("quotedLength(%s) = %d, want %d", test.input, got, test.want) 265 } 266 } 267 268 // errors 269 for _, input := range []string{ 270 ``, // not a quotation 271 `a`, // not a quotation 272 `'a'`, // not a quotation 273 `"a`, // not terminated 274 `"\0"`, // short octal escape 275 `"\x1"`, // short hex escape 276 `"\u000"`, // short \u escape 277 `"\U0000000"`, // short \U escape 278 `"\k"`, // invalid escape 279 "\"ab\nc\"", // newline 280 } { 281 if n, ok := quotedLength(input); ok { 282 t.Errorf("quotedLength(%s) = %d, want !ok", input, n) 283 } 284 } 285 } 286 287 func TestFocus(t *testing.T) { 288 for _, test := range []struct { 289 name string 290 in string 291 focus string 292 want string 293 }{ 294 { 295 name: "Basic", 296 in: "A B", 297 focus: "B", 298 want: "A B\n", 299 }, 300 { 301 name: "Some Nodes Not Included", 302 // C does not have a path involving B, and should not be included 303 // in the output. 304 in: "A B\nA C", 305 focus: "B", 306 want: "A B\n", 307 }, 308 { 309 name: "Cycle In Path", 310 // A <-> B -> C 311 in: "A B\nB A\nB C", 312 focus: "C", 313 want: "A B\nB A\nB C\n", 314 }, 315 { 316 name: "Cycle Out Of Path", 317 // C <- A <->B 318 in: "A B\nB A\nB C", 319 focus: "C", 320 want: "A B\nB A\nB C\n", 321 }, 322 { 323 name: "Complex", 324 // Paths in and out from focus. 325 // /-> F 326 // /-> B -> D -- 327 // A -- \-> E 328 // \-> C 329 in: "A B\nA C\nB D\nD F\nD E", 330 focus: "D", 331 want: "A B\nB D\nD E\nD F\n", 332 }, 333 } { 334 t.Run(test.name, func(t *testing.T) { 335 stdin = strings.NewReader(test.in) 336 stdout = new(bytes.Buffer) 337 if err := digraph("focus", []string{test.focus}); err != nil { 338 t.Fatal(err) 339 } 340 got := stdout.(fmt.Stringer).String() 341 if got != test.want { 342 t.Errorf("digraph(focus, %s) = got %q, want %q", test.focus, got, test.want) 343 } 344 }) 345 } 346 }