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