github.com/gechr/complete@v0.0.0-20191016221035-401475e3ce1e/complete_test.go (about) 1 package complete 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "sort" 8 "strconv" 9 "strings" 10 "testing" 11 ) 12 13 func TestCompleter_Complete(t *testing.T) { 14 initTests() 15 16 c := Command{ 17 Sub: Commands{ 18 "sub1": { 19 Flags: Flags{ 20 "-flag1": PredictAnything, 21 "-flag2": PredictNothing, 22 }, 23 }, 24 "sub2": { 25 Flags: Flags{ 26 "-flag2": PredictNothing, 27 "-flag3": PredictSet("opt1", "opt2", "opt12"), 28 }, 29 Args: PredictFiles("*.md"), 30 }, 31 }, 32 Flags: Flags{ 33 "-o": PredictFiles("*.txt"), 34 }, 35 GlobalFlags: Flags{ 36 "-h": PredictNothing, 37 "-global1": PredictAnything, 38 }, 39 } 40 cmp := New("cmd", c) 41 42 tests := []struct { 43 line string 44 point int // -1 indicates len(line) 45 want []string 46 }{ 47 { 48 line: "cmd ", 49 point: -1, 50 want: []string{"sub1", "sub2"}, 51 }, 52 { 53 line: "cmd -", 54 point: -1, 55 want: []string{"-h", "-global1", "-o"}, 56 }, 57 { 58 line: "cmd -h ", 59 point: -1, 60 want: []string{"sub1", "sub2"}, 61 }, 62 { 63 line: "cmd -global1 ", // global1 is known follow flag 64 point: -1, 65 want: []string{}, 66 }, 67 { 68 line: "cmd sub", 69 point: -1, 70 want: []string{"sub1", "sub2"}, 71 }, 72 { 73 line: "cmd sub1", 74 point: -1, 75 want: []string{"sub1"}, 76 }, 77 { 78 line: "cmd sub2", 79 point: -1, 80 want: []string{"sub2"}, 81 }, 82 { 83 line: "cmd sub1 ", 84 point: -1, 85 want: []string{}, 86 }, 87 { 88 line: "cmd sub1 -", 89 point: -1, 90 want: []string{"-flag1", "-flag2", "-h", "-global1"}, 91 }, 92 { 93 line: "cmd sub2 ", 94 point: -1, 95 want: []string{"./", "dir/", "outer/", "readme.md"}, 96 }, 97 { 98 line: "cmd sub2 ./", 99 point: -1, 100 want: []string{"./", "./readme.md", "./dir/", "./outer/"}, 101 }, 102 { 103 line: "cmd sub2 re", 104 point: -1, 105 want: []string{"readme.md"}, 106 }, 107 { 108 line: "cmd sub2 ./re", 109 point: -1, 110 want: []string{"./readme.md"}, 111 }, 112 { 113 line: "cmd sub2 -flag2 ", 114 point: -1, 115 want: []string{"./", "dir/", "outer/", "readme.md"}, 116 }, 117 { 118 line: "cmd sub1 -fl", 119 point: -1, 120 want: []string{"-flag1", "-flag2"}, 121 }, 122 { 123 line: "cmd sub1 -flag1", 124 point: -1, 125 want: []string{"-flag1"}, 126 }, 127 { 128 line: "cmd sub1 -flag1 ", 129 point: -1, 130 want: []string{}, // flag1 is unknown follow flag 131 }, 132 { 133 line: "cmd sub1 -flag2 -", 134 point: -1, 135 want: []string{"-flag1", "-flag2", "-h", "-global1"}, 136 }, 137 { 138 line: "cmd -no-such-flag", 139 point: -1, 140 want: []string{}, 141 }, 142 { 143 line: "cmd -no-such-flag ", 144 point: -1, 145 want: []string{"sub1", "sub2"}, 146 }, 147 { 148 line: "cmd -no-such-flag -", 149 point: -1, 150 want: []string{"-h", "-global1", "-o"}, 151 }, 152 { 153 line: "cmd no-such-command", 154 point: -1, 155 want: []string{}, 156 }, 157 { 158 line: "cmd no-such-command ", 159 point: -1, 160 want: []string{"sub1", "sub2"}, 161 }, 162 { 163 line: "cmd -o ", 164 point: -1, 165 want: []string{"a.txt", "b.txt", "c.txt", ".dot.txt", "./", "dir/", "outer/"}, 166 }, 167 { 168 line: "cmd -o ./no-su", 169 point: -1, 170 want: []string{}, 171 }, 172 { 173 line: "cmd -o ./", 174 point: -1, 175 want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"}, 176 }, 177 { 178 line: "cmd -o=./", 179 point: -1, 180 want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"}, 181 }, 182 { 183 line: "cmd -o .", 184 point: -1, 185 want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"}, 186 }, 187 { 188 line: "cmd -o ./b", 189 point: -1, 190 want: []string{"./b.txt"}, 191 }, 192 { 193 line: "cmd -o=./b", 194 point: -1, 195 want: []string{"./b.txt"}, 196 }, 197 { 198 line: "cmd -o ./read", 199 point: -1, 200 want: []string{}, 201 }, 202 { 203 line: "cmd -o=./read", 204 point: -1, 205 want: []string{}, 206 }, 207 { 208 line: "cmd -o ./readme.md", 209 point: -1, 210 want: []string{}, 211 }, 212 { 213 line: "cmd -o ./readme.md ", 214 point: -1, 215 want: []string{"sub1", "sub2"}, 216 }, 217 { 218 line: "cmd -o=./readme.md ", 219 point: -1, 220 want: []string{"sub1", "sub2"}, 221 }, 222 { 223 line: "cmd -o sub2 -flag3 ", 224 point: -1, 225 want: []string{"opt1", "opt2", "opt12"}, 226 }, 227 { 228 line: "cmd -o sub2 -flag3 opt1", 229 point: -1, 230 want: []string{"opt1", "opt12"}, 231 }, 232 { 233 line: "cmd -o sub2 -flag3 opt", 234 point: -1, 235 want: []string{"opt1", "opt2", "opt12"}, 236 }, 237 { 238 line: "cmd -o ./b foo", 239 // ^ 240 point: 10, 241 want: []string{"./b.txt"}, 242 }, 243 { 244 line: "cmd -o=./b foo", 245 // ^ 246 point: 10, 247 want: []string{"./b.txt"}, 248 }, 249 { 250 line: "cmd -o sub2 -flag3 optfoo", 251 // ^ 252 point: 22, 253 want: []string{"opt1", "opt2", "opt12"}, 254 }, 255 { 256 line: "cmd -o ", 257 // ^ 258 point: 4, 259 want: []string{"sub1", "sub2"}, 260 }, 261 } 262 263 for _, tt := range tests { 264 t.Run(fmt.Sprintf("%s@%d", tt.line, tt.point), func(t *testing.T) { 265 got := runComplete(cmp, tt.line, tt.point) 266 267 sort.Strings(tt.want) 268 sort.Strings(got) 269 270 if !equalSlices(got, tt.want) { 271 t.Errorf("failed '%s'\ngot: %s\nwant: %s", t.Name(), got, tt.want) 272 } 273 }) 274 } 275 } 276 277 func TestCompleter_Complete_SharedPrefix(t *testing.T) { 278 initTests() 279 280 c := Command{ 281 Sub: Commands{ 282 "status": { 283 Flags: Flags{ 284 "-f3": PredictNothing, 285 }, 286 }, 287 "job": { 288 Sub: Commands{ 289 "status": { 290 Flags: Flags{ 291 "-f4": PredictNothing, 292 }, 293 }, 294 }, 295 }, 296 }, 297 Flags: Flags{ 298 "-o": PredictFiles("*.txt"), 299 }, 300 GlobalFlags: Flags{ 301 "-h": PredictNothing, 302 "-global1": PredictAnything, 303 }, 304 } 305 306 cmp := New("cmd", c) 307 308 tests := []struct { 309 line string 310 point int // -1 indicates len(line) 311 want []string 312 }{ 313 { 314 line: "cmd ", 315 point: -1, 316 want: []string{"status", "job"}, 317 }, 318 { 319 line: "cmd -", 320 point: -1, 321 want: []string{"-h", "-global1", "-o"}, 322 }, 323 { 324 line: "cmd j", 325 point: -1, 326 want: []string{"job"}, 327 }, 328 { 329 line: "cmd job ", 330 point: -1, 331 want: []string{"status"}, 332 }, 333 { 334 line: "cmd job -", 335 point: -1, 336 want: []string{"-h", "-global1"}, 337 }, 338 { 339 line: "cmd job status ", 340 point: -1, 341 want: []string{}, 342 }, 343 { 344 line: "cmd job status -", 345 point: -1, 346 want: []string{"-f4", "-h", "-global1"}, 347 }, 348 } 349 350 for _, tt := range tests { 351 t.Run(tt.line, func(t *testing.T) { 352 got := runComplete(cmp, tt.line, tt.point) 353 354 sort.Strings(tt.want) 355 sort.Strings(got) 356 357 if !equalSlices(got, tt.want) { 358 t.Errorf("failed '%s'\ngot = %s\nwant: %s", t.Name(), got, tt.want) 359 } 360 }) 361 } 362 } 363 364 // runComplete runs the complete login for test purposes 365 // it gets the complete struct and command line arguments and returns 366 // the complete options 367 func runComplete(c *Complete, line string, point int) (completions []string) { 368 if point == -1 { 369 point = len(line) 370 } 371 os.Setenv(envLine, line) 372 os.Setenv(envPoint, strconv.Itoa(point)) 373 b := bytes.NewBuffer(nil) 374 c.Out = b 375 c.Complete() 376 completions = parseOutput(b.String()) 377 return 378 } 379 380 func parseOutput(output string) []string { 381 lines := strings.Split(output, "\n") 382 options := []string{} 383 for _, l := range lines { 384 if l != "" { 385 options = append(options, l) 386 } 387 } 388 return options 389 } 390 391 func equalSlices(a, b []string) bool { 392 if len(a) != len(b) { 393 return false 394 } 395 for i := range a { 396 if a[i] != b[i] { 397 return false 398 } 399 } 400 return true 401 }