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