1package cobra
2
3import (
4	"bytes"
5	"strings"
6	"testing"
7)
8
9func validArgsFunc(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
10	if len(args) != 0 {
11		return nil, ShellCompDirectiveNoFileComp
12	}
13
14	var completions []string
15	for _, comp := range []string{"one\tThe first", "two\tThe second"} {
16		if strings.HasPrefix(comp, toComplete) {
17			completions = append(completions, comp)
18		}
19	}
20	return completions, ShellCompDirectiveDefault
21}
22
23func validArgsFunc2(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
24	if len(args) != 0 {
25		return nil, ShellCompDirectiveNoFileComp
26	}
27
28	var completions []string
29	for _, comp := range []string{"three\tThe third", "four\tThe fourth"} {
30		if strings.HasPrefix(comp, toComplete) {
31			completions = append(completions, comp)
32		}
33	}
34	return completions, ShellCompDirectiveDefault
35}
36
37func TestCmdNameCompletionInGo(t *testing.T) {
38	rootCmd := &Command{
39		Use: "root",
40		Run: emptyRun,
41	}
42	childCmd1 := &Command{
43		Use:   "firstChild",
44		Short: "First command",
45		Run:   emptyRun,
46	}
47	childCmd2 := &Command{
48		Use: "secondChild",
49		Run: emptyRun,
50	}
51	hiddenCmd := &Command{
52		Use:    "testHidden",
53		Hidden: true, // Not completed
54		Run:    emptyRun,
55	}
56	deprecatedCmd := &Command{
57		Use:        "testDeprecated",
58		Deprecated: "deprecated", // Not completed
59		Run:        emptyRun,
60	}
61	aliasedCmd := &Command{
62		Use:     "aliased",
63		Short:   "A command with aliases",
64		Aliases: []string{"testAlias", "testSynonym"}, // Not completed
65		Run:     emptyRun,
66	}
67
68	rootCmd.AddCommand(childCmd1, childCmd2, hiddenCmd, deprecatedCmd, aliasedCmd)
69
70	// Test that sub-command names are completed
71	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
72	if err != nil {
73		t.Errorf("Unexpected error: %v", err)
74	}
75
76	expected := strings.Join([]string{
77		"aliased",
78		"firstChild",
79		"help",
80		"secondChild",
81		":4",
82		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
83
84	if output != expected {
85		t.Errorf("expected: %q, got: %q", expected, output)
86	}
87
88	// Test that sub-command names are completed with prefix
89	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "s")
90	if err != nil {
91		t.Errorf("Unexpected error: %v", err)
92	}
93
94	expected = strings.Join([]string{
95		"secondChild",
96		":4",
97		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
98
99	if output != expected {
100		t.Errorf("expected: %q, got: %q", expected, output)
101	}
102
103	// Test that even with no valid sub-command matches, hidden, deprecated and
104	// aliases are not completed
105	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "test")
106	if err != nil {
107		t.Errorf("Unexpected error: %v", err)
108	}
109
110	expected = strings.Join([]string{
111		":4",
112		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
113
114	if output != expected {
115		t.Errorf("expected: %q, got: %q", expected, output)
116	}
117
118	// Test that sub-command names are completed with description
119	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "")
120	if err != nil {
121		t.Errorf("Unexpected error: %v", err)
122	}
123
124	expected = strings.Join([]string{
125		"aliased\tA command with aliases",
126		"firstChild\tFirst command",
127		"help\tHelp about any command",
128		"secondChild",
129		":4",
130		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
131
132	if output != expected {
133		t.Errorf("expected: %q, got: %q", expected, output)
134	}
135}
136
137func TestNoCmdNameCompletionInGo(t *testing.T) {
138	rootCmd := &Command{
139		Use: "root",
140		Run: emptyRun,
141	}
142	rootCmd.Flags().String("localroot", "", "local root flag")
143
144	childCmd1 := &Command{
145		Use:   "childCmd1",
146		Short: "First command",
147		Args:  MinimumNArgs(0),
148		Run:   emptyRun,
149	}
150	rootCmd.AddCommand(childCmd1)
151	childCmd1.PersistentFlags().StringP("persistent", "p", "", "persistent flag")
152	persistentFlag := childCmd1.PersistentFlags().Lookup("persistent")
153	childCmd1.Flags().StringP("nonPersistent", "n", "", "non-persistent flag")
154	nonPersistentFlag := childCmd1.Flags().Lookup("nonPersistent")
155
156	childCmd2 := &Command{
157		Use: "childCmd2",
158		Run: emptyRun,
159	}
160	childCmd1.AddCommand(childCmd2)
161
162	// Test that sub-command names are not completed if there is an argument already
163	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "arg1", "")
164	if err != nil {
165		t.Errorf("Unexpected error: %v", err)
166	}
167
168	expected := strings.Join([]string{
169		":0",
170		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
171
172	if output != expected {
173		t.Errorf("expected: %q, got: %q", expected, output)
174	}
175
176	// Test that sub-command names are not completed if a local non-persistent flag is present
177	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--nonPersistent", "value", "")
178	if err != nil {
179		t.Errorf("Unexpected error: %v", err)
180	}
181	// Reset the flag for the next command
182	nonPersistentFlag.Changed = false
183
184	expected = strings.Join([]string{
185		":0",
186		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
187
188	if output != expected {
189		t.Errorf("expected: %q, got: %q", expected, output)
190	}
191
192	// Test that sub-command names are completed if a local non-persistent flag is present and TraverseChildren is set to true
193	// set TraverseChildren to true on the root cmd
194	rootCmd.TraverseChildren = true
195
196	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "")
197	if err != nil {
198		t.Errorf("Unexpected error: %v", err)
199	}
200	// Reset TraverseChildren for next command
201	rootCmd.TraverseChildren = false
202
203	expected = strings.Join([]string{
204		"childCmd1",
205		"help",
206		":4",
207		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
208
209	if output != expected {
210		t.Errorf("expected: %q, got: %q", expected, output)
211	}
212
213	// Test that sub-command names from a child cmd are completed if a local non-persistent flag is present
214	// and TraverseChildren is set to true on the root cmd
215	rootCmd.TraverseChildren = true
216
217	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "--nonPersistent", "value", "")
218	if err != nil {
219		t.Errorf("Unexpected error: %v", err)
220	}
221	// Reset TraverseChildren for next command
222	rootCmd.TraverseChildren = false
223	// Reset the flag for the next command
224	nonPersistentFlag.Changed = false
225
226	expected = strings.Join([]string{
227		"childCmd2",
228		":4",
229		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
230
231	if output != expected {
232		t.Errorf("expected: %q, got: %q", expected, output)
233	}
234
235	// Test that we don't use Traverse when we shouldn't.
236	// This command should not return a completion since the command line is invalid without TraverseChildren.
237	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "")
238	if err != nil {
239		t.Errorf("Unexpected error: %v", err)
240	}
241
242	expected = strings.Join([]string{
243		":0",
244		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
245
246	if output != expected {
247		t.Errorf("expected: %q, got: %q", expected, output)
248	}
249
250	// Test that sub-command names are not completed if a local non-persistent short flag is present
251	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-n", "value", "")
252	if err != nil {
253		t.Errorf("Unexpected error: %v", err)
254	}
255	// Reset the flag for the next command
256	nonPersistentFlag.Changed = false
257
258	expected = strings.Join([]string{
259		":0",
260		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
261
262	if output != expected {
263		t.Errorf("expected: %q, got: %q", expected, output)
264	}
265
266	// Test that sub-command names are completed with a persistent flag
267	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--persistent", "value", "")
268	if err != nil {
269		t.Errorf("Unexpected error: %v", err)
270	}
271	// Reset the flag for the next command
272	persistentFlag.Changed = false
273
274	expected = strings.Join([]string{
275		"childCmd2",
276		":4",
277		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
278
279	if output != expected {
280		t.Errorf("expected: %q, got: %q", expected, output)
281	}
282
283	// Test that sub-command names are completed with a persistent short flag
284	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-p", "value", "")
285	if err != nil {
286		t.Errorf("Unexpected error: %v", err)
287	}
288	// Reset the flag for the next command
289	persistentFlag.Changed = false
290
291	expected = strings.Join([]string{
292		"childCmd2",
293		":4",
294		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
295
296	if output != expected {
297		t.Errorf("expected: %q, got: %q", expected, output)
298	}
299}
300
301func TestValidArgsCompletionInGo(t *testing.T) {
302	rootCmd := &Command{
303		Use:       "root",
304		ValidArgs: []string{"one", "two", "three"},
305		Args:      MinimumNArgs(1),
306	}
307
308	// Test that validArgs are completed
309	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
310	if err != nil {
311		t.Errorf("Unexpected error: %v", err)
312	}
313
314	expected := strings.Join([]string{
315		"one",
316		"two",
317		"three",
318		":4",
319		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
320
321	if output != expected {
322		t.Errorf("expected: %q, got: %q", expected, output)
323	}
324
325	// Test that validArgs are completed with prefix
326	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "o")
327	if err != nil {
328		t.Errorf("Unexpected error: %v", err)
329	}
330
331	expected = strings.Join([]string{
332		"one",
333		":4",
334		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
335
336	if output != expected {
337		t.Errorf("expected: %q, got: %q", expected, output)
338	}
339
340	// Test that validArgs don't repeat
341	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "one", "")
342	if err != nil {
343		t.Errorf("Unexpected error: %v", err)
344	}
345
346	expected = strings.Join([]string{
347		":0",
348		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
349
350	if output != expected {
351		t.Errorf("expected: %q, got: %q", expected, output)
352	}
353}
354
355func TestValidArgsAndCmdCompletionInGo(t *testing.T) {
356	rootCmd := &Command{
357		Use:       "root",
358		ValidArgs: []string{"one", "two"},
359		Run:       emptyRun,
360	}
361
362	childCmd := &Command{
363		Use: "thechild",
364		Run: emptyRun,
365	}
366
367	rootCmd.AddCommand(childCmd)
368
369	// Test that both sub-commands and validArgs are completed
370	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
371	if err != nil {
372		t.Errorf("Unexpected error: %v", err)
373	}
374
375	expected := strings.Join([]string{
376		"help",
377		"thechild",
378		"one",
379		"two",
380		":4",
381		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
382
383	if output != expected {
384		t.Errorf("expected: %q, got: %q", expected, output)
385	}
386
387	// Test that both sub-commands and validArgs are completed with prefix
388	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
389	if err != nil {
390		t.Errorf("Unexpected error: %v", err)
391	}
392
393	expected = strings.Join([]string{
394		"thechild",
395		"two",
396		":4",
397		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
398
399	if output != expected {
400		t.Errorf("expected: %q, got: %q", expected, output)
401	}
402}
403
404func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) {
405	rootCmd := &Command{
406		Use:               "root",
407		ValidArgsFunction: validArgsFunc,
408		Run:               emptyRun,
409	}
410
411	childCmd := &Command{
412		Use:   "thechild",
413		Short: "The child command",
414		Run:   emptyRun,
415	}
416
417	rootCmd.AddCommand(childCmd)
418
419	// Test that both sub-commands and validArgsFunction are completed
420	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
421	if err != nil {
422		t.Errorf("Unexpected error: %v", err)
423	}
424
425	expected := strings.Join([]string{
426		"help",
427		"thechild",
428		"one",
429		"two",
430		":0",
431		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
432
433	if output != expected {
434		t.Errorf("expected: %q, got: %q", expected, output)
435	}
436
437	// Test that both sub-commands and validArgs are completed with prefix
438	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
439	if err != nil {
440		t.Errorf("Unexpected error: %v", err)
441	}
442
443	expected = strings.Join([]string{
444		"thechild",
445		"two",
446		":0",
447		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
448
449	if output != expected {
450		t.Errorf("expected: %q, got: %q", expected, output)
451	}
452
453	// Test that both sub-commands and validArgs are completed with description
454	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "t")
455	if err != nil {
456		t.Errorf("Unexpected error: %v", err)
457	}
458
459	expected = strings.Join([]string{
460		"thechild\tThe child command",
461		"two\tThe second",
462		":0",
463		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
464
465	if output != expected {
466		t.Errorf("expected: %q, got: %q", expected, output)
467	}
468}
469
470func TestFlagNameCompletionInGo(t *testing.T) {
471	rootCmd := &Command{
472		Use: "root",
473		Run: emptyRun,
474	}
475	childCmd := &Command{
476		Use: "childCmd",
477		Run: emptyRun,
478	}
479	rootCmd.AddCommand(childCmd)
480
481	rootCmd.Flags().IntP("first", "f", -1, "first flag")
482	rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag")
483	childCmd.Flags().String("subFlag", "", "sub flag")
484
485	// Test that flag names are not shown if the user has not given the '-' prefix
486	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
487	if err != nil {
488		t.Errorf("Unexpected error: %v", err)
489	}
490
491	expected := strings.Join([]string{
492		"childCmd",
493		"help",
494		":4",
495		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
496
497	if output != expected {
498		t.Errorf("expected: %q, got: %q", expected, output)
499	}
500
501	// Test that flag names are completed
502	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-")
503	if err != nil {
504		t.Errorf("Unexpected error: %v", err)
505	}
506
507	expected = strings.Join([]string{
508		"--first",
509		"-f",
510		"--second",
511		"-s",
512		":4",
513		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
514
515	if output != expected {
516		t.Errorf("expected: %q, got: %q", expected, output)
517	}
518
519	// Test that flag names are completed when a prefix is given
520	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--f")
521	if err != nil {
522		t.Errorf("Unexpected error: %v", err)
523	}
524
525	expected = strings.Join([]string{
526		"--first",
527		":4",
528		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
529
530	if output != expected {
531		t.Errorf("expected: %q, got: %q", expected, output)
532	}
533
534	// Test that flag names are completed in a sub-cmd
535	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-")
536	if err != nil {
537		t.Errorf("Unexpected error: %v", err)
538	}
539
540	expected = strings.Join([]string{
541		"--second",
542		"-s",
543		"--subFlag",
544		":4",
545		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
546
547	if output != expected {
548		t.Errorf("expected: %q, got: %q", expected, output)
549	}
550}
551
552func TestFlagNameCompletionInGoWithDesc(t *testing.T) {
553	rootCmd := &Command{
554		Use: "root",
555		Run: emptyRun,
556	}
557	childCmd := &Command{
558		Use:   "childCmd",
559		Short: "first command",
560		Run:   emptyRun,
561	}
562	rootCmd.AddCommand(childCmd)
563
564	rootCmd.Flags().IntP("first", "f", -1, "first flag\nlonger description for flag")
565	rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag")
566	childCmd.Flags().String("subFlag", "", "sub flag")
567
568	// Test that flag names are not shown if the user has not given the '-' prefix
569	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "")
570	if err != nil {
571		t.Errorf("Unexpected error: %v", err)
572	}
573
574	expected := strings.Join([]string{
575		"childCmd\tfirst command",
576		"help\tHelp about any command",
577		":4",
578		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
579
580	if output != expected {
581		t.Errorf("expected: %q, got: %q", expected, output)
582	}
583
584	// Test that flag names are completed
585	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "-")
586	if err != nil {
587		t.Errorf("Unexpected error: %v", err)
588	}
589
590	expected = strings.Join([]string{
591		"--first\tfirst flag",
592		"-f\tfirst flag",
593		"--second\tsecond flag",
594		"-s\tsecond flag",
595		":4",
596		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
597
598	if output != expected {
599		t.Errorf("expected: %q, got: %q", expected, output)
600	}
601
602	// Test that flag names are completed when a prefix is given
603	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--f")
604	if err != nil {
605		t.Errorf("Unexpected error: %v", err)
606	}
607
608	expected = strings.Join([]string{
609		"--first\tfirst flag",
610		":4",
611		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
612
613	if output != expected {
614		t.Errorf("expected: %q, got: %q", expected, output)
615	}
616
617	// Test that flag names are completed in a sub-cmd
618	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "childCmd", "-")
619	if err != nil {
620		t.Errorf("Unexpected error: %v", err)
621	}
622
623	expected = strings.Join([]string{
624		"--second\tsecond flag",
625		"-s\tsecond flag",
626		"--subFlag\tsub flag",
627		":4",
628		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
629
630	if output != expected {
631		t.Errorf("expected: %q, got: %q", expected, output)
632	}
633}
634
635func TestFlagNameCompletionRepeat(t *testing.T) {
636	rootCmd := &Command{
637		Use: "root",
638		Run: emptyRun,
639	}
640	childCmd := &Command{
641		Use:   "childCmd",
642		Short: "first command",
643		Run:   emptyRun,
644	}
645	rootCmd.AddCommand(childCmd)
646
647	rootCmd.Flags().IntP("first", "f", -1, "first flag")
648	firstFlag := rootCmd.Flags().Lookup("first")
649	rootCmd.Flags().BoolP("second", "s", false, "second flag")
650	secondFlag := rootCmd.Flags().Lookup("second")
651	rootCmd.Flags().StringArrayP("array", "a", nil, "array flag")
652	arrayFlag := rootCmd.Flags().Lookup("array")
653	rootCmd.Flags().IntSliceP("slice", "l", nil, "slice flag")
654	sliceFlag := rootCmd.Flags().Lookup("slice")
655	rootCmd.Flags().BoolSliceP("bslice", "b", nil, "bool slice flag")
656	bsliceFlag := rootCmd.Flags().Lookup("bslice")
657
658	// Test that flag names are not repeated unless they are an array or slice
659	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--")
660	if err != nil {
661		t.Errorf("Unexpected error: %v", err)
662	}
663	// Reset the flag for the next command
664	firstFlag.Changed = false
665
666	expected := strings.Join([]string{
667		"--array",
668		"--bslice",
669		"--second",
670		"--slice",
671		":4",
672		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
673
674	if output != expected {
675		t.Errorf("expected: %q, got: %q", expected, output)
676	}
677
678	// Test that flag names are not repeated unless they are an array or slice
679	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--second=false", "--")
680	if err != nil {
681		t.Errorf("Unexpected error: %v", err)
682	}
683	// Reset the flag for the next command
684	firstFlag.Changed = false
685	secondFlag.Changed = false
686
687	expected = strings.Join([]string{
688		"--array",
689		"--bslice",
690		"--slice",
691		":4",
692		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
693
694	if output != expected {
695		t.Errorf("expected: %q, got: %q", expected, output)
696	}
697
698	// Test that flag names are not repeated unless they are an array or slice
699	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--slice", "1", "--slice=2", "--array", "val", "--bslice", "true", "--")
700	if err != nil {
701		t.Errorf("Unexpected error: %v", err)
702	}
703	// Reset the flag for the next command
704	sliceFlag.Changed = false
705	arrayFlag.Changed = false
706	bsliceFlag.Changed = false
707
708	expected = strings.Join([]string{
709		"--array",
710		"--bslice",
711		"--first",
712		"--second",
713		"--slice",
714		":4",
715		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
716
717	if output != expected {
718		t.Errorf("expected: %q, got: %q", expected, output)
719	}
720
721	// Test that flag names are not repeated unless they are an array or slice, using shortname
722	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-")
723	if err != nil {
724		t.Errorf("Unexpected error: %v", err)
725	}
726	// Reset the flag for the next command
727	sliceFlag.Changed = false
728	arrayFlag.Changed = false
729
730	expected = strings.Join([]string{
731		"--array",
732		"-a",
733		"--bslice",
734		"-b",
735		"--first",
736		"-f",
737		"--second",
738		"-s",
739		"--slice",
740		"-l",
741		":4",
742		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
743
744	if output != expected {
745		t.Errorf("expected: %q, got: %q", expected, output)
746	}
747
748	// Test that flag names are not repeated unless they are an array or slice, using shortname with prefix
749	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-a")
750	if err != nil {
751		t.Errorf("Unexpected error: %v", err)
752	}
753	// Reset the flag for the next command
754	sliceFlag.Changed = false
755	arrayFlag.Changed = false
756
757	expected = strings.Join([]string{
758		"-a",
759		":4",
760		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
761
762	if output != expected {
763		t.Errorf("expected: %q, got: %q", expected, output)
764	}
765}
766
767func TestRequiredFlagNameCompletionInGo(t *testing.T) {
768	rootCmd := &Command{
769		Use:       "root",
770		ValidArgs: []string{"realArg"},
771		Run:       emptyRun,
772	}
773	childCmd := &Command{
774		Use: "childCmd",
775		ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
776			return []string{"subArg"}, ShellCompDirectiveNoFileComp
777		},
778		Run: emptyRun,
779	}
780	rootCmd.AddCommand(childCmd)
781
782	rootCmd.Flags().IntP("requiredFlag", "r", -1, "required flag")
783	rootCmd.MarkFlagRequired("requiredFlag")
784	requiredFlag := rootCmd.Flags().Lookup("requiredFlag")
785
786	rootCmd.PersistentFlags().IntP("requiredPersistent", "p", -1, "required persistent")
787	rootCmd.MarkPersistentFlagRequired("requiredPersistent")
788	requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent")
789
790	rootCmd.Flags().StringP("release", "R", "", "Release name")
791
792	childCmd.Flags().BoolP("subRequired", "s", false, "sub required flag")
793	childCmd.MarkFlagRequired("subRequired")
794	childCmd.Flags().BoolP("subNotRequired", "n", false, "sub not required flag")
795
796	// Test that a required flag is suggested even without the - prefix
797	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
798	if err != nil {
799		t.Errorf("Unexpected error: %v", err)
800	}
801
802	expected := strings.Join([]string{
803		"childCmd",
804		"help",
805		"--requiredFlag",
806		"-r",
807		"--requiredPersistent",
808		"-p",
809		"realArg",
810		":4",
811		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
812
813	if output != expected {
814		t.Errorf("expected: %q, got: %q", expected, output)
815	}
816
817	// Test that a required flag is suggested without other flags when using the '-' prefix
818	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-")
819	if err != nil {
820		t.Errorf("Unexpected error: %v", err)
821	}
822
823	expected = strings.Join([]string{
824		"--requiredFlag",
825		"-r",
826		"--requiredPersistent",
827		"-p",
828		":4",
829		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
830
831	if output != expected {
832		t.Errorf("expected: %q, got: %q", expected, output)
833	}
834
835	// Test that if no required flag matches, the normal flags are suggested
836	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--relea")
837	if err != nil {
838		t.Errorf("Unexpected error: %v", err)
839	}
840
841	expected = strings.Join([]string{
842		"--release",
843		":4",
844		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
845
846	if output != expected {
847		t.Errorf("expected: %q, got: %q", expected, output)
848	}
849
850	// Test required flags for sub-commands
851	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "")
852	if err != nil {
853		t.Errorf("Unexpected error: %v", err)
854	}
855
856	expected = strings.Join([]string{
857		"--requiredPersistent",
858		"-p",
859		"--subRequired",
860		"-s",
861		"subArg",
862		":4",
863		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
864
865	if output != expected {
866		t.Errorf("expected: %q, got: %q", expected, output)
867	}
868
869	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-")
870	if err != nil {
871		t.Errorf("Unexpected error: %v", err)
872	}
873
874	expected = strings.Join([]string{
875		"--requiredPersistent",
876		"-p",
877		"--subRequired",
878		"-s",
879		":4",
880		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
881
882	if output != expected {
883		t.Errorf("expected: %q, got: %q", expected, output)
884	}
885
886	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "--subNot")
887	if err != nil {
888		t.Errorf("Unexpected error: %v", err)
889	}
890
891	expected = strings.Join([]string{
892		"--subNotRequired",
893		":4",
894		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
895
896	if output != expected {
897		t.Errorf("expected: %q, got: %q", expected, output)
898	}
899
900	// Test that when a required flag is present, it is not suggested anymore
901	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "")
902	if err != nil {
903		t.Errorf("Unexpected error: %v", err)
904	}
905	// Reset the flag for the next command
906	requiredFlag.Changed = false
907
908	expected = strings.Join([]string{
909		"--requiredPersistent",
910		"-p",
911		"realArg",
912		":4",
913		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
914
915	if output != expected {
916		t.Errorf("expected: %q, got: %q", expected, output)
917	}
918
919	// Test that when a persistent required flag is present, it is not suggested anymore
920	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredPersistent", "1", "")
921	if err != nil {
922		t.Errorf("Unexpected error: %v", err)
923	}
924	// Reset the flag for the next command
925	requiredPersistent.Changed = false
926
927	expected = strings.Join([]string{
928		"childCmd",
929		"help",
930		"--requiredFlag",
931		"-r",
932		"realArg",
933		":4",
934		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
935
936	if output != expected {
937		t.Errorf("expected: %q, got: %q", expected, output)
938	}
939
940	// Test that when all required flags are present, normal completion is done
941	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "--requiredPersistent", "1", "")
942	if err != nil {
943		t.Errorf("Unexpected error: %v", err)
944	}
945	// Reset the flags for the next command
946	requiredFlag.Changed = false
947	requiredPersistent.Changed = false
948
949	expected = strings.Join([]string{
950		"realArg",
951		":4",
952		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
953
954	if output != expected {
955		t.Errorf("expected: %q, got: %q", expected, output)
956	}
957}
958
959func TestFlagFileExtFilterCompletionInGo(t *testing.T) {
960	rootCmd := &Command{
961		Use: "root",
962		Run: emptyRun,
963	}
964
965	// No extensions.  Should be ignored.
966	rootCmd.Flags().StringP("file", "f", "", "file flag")
967	rootCmd.MarkFlagFilename("file")
968
969	// Single extension
970	rootCmd.Flags().StringP("log", "l", "", "log flag")
971	rootCmd.MarkFlagFilename("log", "log")
972
973	// Multiple extensions
974	rootCmd.Flags().StringP("yaml", "y", "", "yaml flag")
975	rootCmd.MarkFlagFilename("yaml", "yaml", "yml")
976
977	// Directly using annotation
978	rootCmd.Flags().StringP("text", "t", "", "text flag")
979	rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"})
980
981	// Test that the completion logic returns the proper info for the completion
982	// script to handle the file filtering
983	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--file", "")
984	if err != nil {
985		t.Errorf("Unexpected error: %v", err)
986	}
987
988	expected := strings.Join([]string{
989		":0",
990		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
991
992	if output != expected {
993		t.Errorf("expected: %q, got: %q", expected, output)
994	}
995
996	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--log", "")
997	if err != nil {
998		t.Errorf("Unexpected error: %v", err)
999	}
1000
1001	expected = strings.Join([]string{
1002		"log",
1003		":8",
1004		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
1005
1006	if output != expected {
1007		t.Errorf("expected: %q, got: %q", expected, output)
1008	}
1009
1010	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml", "")
1011	if err != nil {
1012		t.Errorf("Unexpected error: %v", err)
1013	}
1014
1015	expected = strings.Join([]string{
1016		"yaml", "yml",
1017		":8",
1018		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
1019
1020	if output != expected {
1021		t.Errorf("expected: %q, got: %q", expected, output)
1022	}
1023
1024	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml=")
1025	if err != nil {
1026		t.Errorf("Unexpected error: %v", err)
1027	}
1028
1029	expected = strings.Join([]string{
1030		"yaml", "yml",
1031		":8",
1032		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
1033
1034	if output != expected {
1035		t.Errorf("expected: %q, got: %q", expected, output)
1036	}
1037
1038	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y", "")
1039	if err != nil {
1040		t.Errorf("Unexpected error: %v", err)
1041	}
1042
1043	expected = strings.Join([]string{
1044		"yaml", "yml",
1045		":8",
1046		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
1047
1048	if output != expected {
1049		t.Errorf("expected: %q, got: %q", expected, output)
1050	}
1051
1052	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y=")
1053	if err != nil {
1054		t.Errorf("Unexpected error: %v", err)
1055	}
1056
1057	expected = strings.Join([]string{
1058		"yaml", "yml",
1059		":8",
1060		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
1061
1062	if output != expected {
1063		t.Errorf("expected: %q, got: %q", expected, output)
1064	}
1065
1066	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--text", "")
1067	if err != nil {
1068		t.Errorf("Unexpected error: %v", err)
1069	}
1070
1071	expected = strings.Join([]string{
1072		"txt",
1073		":8",
1074		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
1075
1076	if output != expected {
1077		t.Errorf("expected: %q, got: %q", expected, output)
1078	}
1079}
1080
1081func TestFlagDirFilterCompletionInGo(t *testing.T) {
1082	rootCmd := &Command{
1083		Use: "root",
1084		Run: emptyRun,
1085	}
1086
1087	// Filter directories
1088	rootCmd.Flags().StringP("dir", "d", "", "dir flag")
1089	rootCmd.MarkFlagDirname("dir")
1090
1091	// Filter directories within a directory
1092	rootCmd.Flags().StringP("subdir", "s", "", "subdir")
1093	rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"})
1094
1095	// Multiple directory specification get ignored
1096	rootCmd.Flags().StringP("manydir", "m", "", "manydir")
1097	rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"})
1098
1099	// Test that the completion logic returns the proper info for the completion
1100	// script to handle the directory filtering
1101	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--dir", "")
1102	if err != nil {
1103		t.Errorf("Unexpected error: %v", err)
1104	}
1105
1106	expected := strings.Join([]string{
1107		":16",
1108		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
1109
1110	if output != expected {
1111		t.Errorf("expected: %q, got: %q", expected, output)
1112	}
1113
1114	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-d", "")
1115	if err != nil {
1116		t.Errorf("Unexpected error: %v", err)
1117	}
1118
1119	expected = strings.Join([]string{
1120		":16",
1121		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
1122
1123	if output != expected {
1124		t.Errorf("expected: %q, got: %q", expected, output)
1125	}
1126
1127	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir", "")
1128	if err != nil {
1129		t.Errorf("Unexpected error: %v", err)
1130	}
1131
1132	expected = strings.Join([]string{
1133		"themes",
1134		":16",
1135		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
1136
1137	if output != expected {
1138		t.Errorf("expected: %q, got: %q", expected, output)
1139	}
1140
1141	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir=")
1142	if err != nil {
1143		t.Errorf("Unexpected error: %v", err)
1144	}
1145
1146	expected = strings.Join([]string{
1147		"themes",
1148		":16",
1149		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
1150
1151	if output != expected {
1152		t.Errorf("expected: %q, got: %q", expected, output)
1153	}
1154
1155	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "")
1156	if err != nil {
1157		t.Errorf("Unexpected error: %v", err)
1158	}
1159
1160	expected = strings.Join([]string{
1161		"themes",
1162		":16",
1163		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
1164
1165	if output != expected {
1166		t.Errorf("expected: %q, got: %q", expected, output)
1167	}
1168
1169	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s=")
1170	if err != nil {
1171		t.Errorf("Unexpected error: %v", err)
1172	}
1173
1174	expected = strings.Join([]string{
1175		"themes",
1176		":16",
1177		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
1178
1179	if output != expected {
1180		t.Errorf("expected: %q, got: %q", expected, output)
1181	}
1182
1183	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--manydir", "")
1184	if err != nil {
1185		t.Errorf("Unexpected error: %v", err)
1186	}
1187
1188	expected = strings.Join([]string{
1189		":16",
1190		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
1191
1192	if output != expected {
1193		t.Errorf("expected: %q, got: %q", expected, output)
1194	}
1195}
1196
1197func TestValidArgsFuncSingleCmd(t *testing.T) {
1198	rootCmd := &Command{
1199		Use:               "root",
1200		ValidArgsFunction: validArgsFunc,
1201		Run:               emptyRun,
1202	}
1203
1204	// Test completing an empty string
1205	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
1206	if err != nil {
1207		t.Errorf("Unexpected error: %v", err)
1208	}
1209
1210	expected := strings.Join([]string{
1211		"one",
1212		"two",
1213		":0",
1214		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1215
1216	if output != expected {
1217		t.Errorf("expected: %q, got: %q", expected, output)
1218	}
1219
1220	// Check completing with a prefix
1221	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
1222	if err != nil {
1223		t.Errorf("Unexpected error: %v", err)
1224	}
1225
1226	expected = strings.Join([]string{
1227		"two",
1228		":0",
1229		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1230
1231	if output != expected {
1232		t.Errorf("expected: %q, got: %q", expected, output)
1233	}
1234}
1235
1236func TestValidArgsFuncSingleCmdInvalidArg(t *testing.T) {
1237	rootCmd := &Command{
1238		Use: "root",
1239		// If we don't specify a value for Args, this test fails.
1240		// This is only true for a root command without any subcommands, and is caused
1241		// by the fact that the __complete command becomes a subcommand when there should not be one.
1242		// The problem is in the implementation of legacyArgs().
1243		Args:              MinimumNArgs(1),
1244		ValidArgsFunction: validArgsFunc,
1245		Run:               emptyRun,
1246	}
1247
1248	// Check completing with wrong number of args
1249	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "unexpectedArg", "t")
1250	if err != nil {
1251		t.Errorf("Unexpected error: %v", err)
1252	}
1253
1254	expected := strings.Join([]string{
1255		":4",
1256		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1257
1258	if output != expected {
1259		t.Errorf("expected: %q, got: %q", expected, output)
1260	}
1261}
1262
1263func TestValidArgsFuncChildCmds(t *testing.T) {
1264	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1265	child1Cmd := &Command{
1266		Use:               "child1",
1267		ValidArgsFunction: validArgsFunc,
1268		Run:               emptyRun,
1269	}
1270	child2Cmd := &Command{
1271		Use:               "child2",
1272		ValidArgsFunction: validArgsFunc2,
1273		Run:               emptyRun,
1274	}
1275	rootCmd.AddCommand(child1Cmd, child2Cmd)
1276
1277	// Test completion of first sub-command with empty argument
1278	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "")
1279	if err != nil {
1280		t.Errorf("Unexpected error: %v", err)
1281	}
1282
1283	expected := strings.Join([]string{
1284		"one",
1285		"two",
1286		":0",
1287		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1288
1289	if output != expected {
1290		t.Errorf("expected: %q, got: %q", expected, output)
1291	}
1292
1293	// Test completion of first sub-command with a prefix to complete
1294	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "t")
1295	if err != nil {
1296		t.Errorf("Unexpected error: %v", err)
1297	}
1298
1299	expected = strings.Join([]string{
1300		"two",
1301		":0",
1302		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1303
1304	if output != expected {
1305		t.Errorf("expected: %q, got: %q", expected, output)
1306	}
1307
1308	// Check completing with wrong number of args
1309	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "unexpectedArg", "t")
1310	if err != nil {
1311		t.Errorf("Unexpected error: %v", err)
1312	}
1313
1314	expected = strings.Join([]string{
1315		":4",
1316		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1317
1318	if output != expected {
1319		t.Errorf("expected: %q, got: %q", expected, output)
1320	}
1321
1322	// Test completion of second sub-command with empty argument
1323	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "")
1324	if err != nil {
1325		t.Errorf("Unexpected error: %v", err)
1326	}
1327
1328	expected = strings.Join([]string{
1329		"three",
1330		"four",
1331		":0",
1332		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1333
1334	if output != expected {
1335		t.Errorf("expected: %q, got: %q", expected, output)
1336	}
1337
1338	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "t")
1339	if err != nil {
1340		t.Errorf("Unexpected error: %v", err)
1341	}
1342
1343	expected = strings.Join([]string{
1344		"three",
1345		":0",
1346		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1347
1348	if output != expected {
1349		t.Errorf("expected: %q, got: %q", expected, output)
1350	}
1351
1352	// Check completing with wrong number of args
1353	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "unexpectedArg", "t")
1354	if err != nil {
1355		t.Errorf("Unexpected error: %v", err)
1356	}
1357
1358	expected = strings.Join([]string{
1359		":4",
1360		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1361
1362	if output != expected {
1363		t.Errorf("expected: %q, got: %q", expected, output)
1364	}
1365}
1366
1367func TestValidArgsFuncAliases(t *testing.T) {
1368	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1369	child := &Command{
1370		Use:               "child",
1371		Aliases:           []string{"son", "daughter"},
1372		ValidArgsFunction: validArgsFunc,
1373		Run:               emptyRun,
1374	}
1375	rootCmd.AddCommand(child)
1376
1377	// Test completion of first sub-command with empty argument
1378	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "")
1379	if err != nil {
1380		t.Errorf("Unexpected error: %v", err)
1381	}
1382
1383	expected := strings.Join([]string{
1384		"one",
1385		"two",
1386		":0",
1387		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1388
1389	if output != expected {
1390		t.Errorf("expected: %q, got: %q", expected, output)
1391	}
1392
1393	// Test completion of first sub-command with a prefix to complete
1394	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "daughter", "t")
1395	if err != nil {
1396		t.Errorf("Unexpected error: %v", err)
1397	}
1398
1399	expected = strings.Join([]string{
1400		"two",
1401		":0",
1402		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1403
1404	if output != expected {
1405		t.Errorf("expected: %q, got: %q", expected, output)
1406	}
1407
1408	// Check completing with wrong number of args
1409	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "unexpectedArg", "t")
1410	if err != nil {
1411		t.Errorf("Unexpected error: %v", err)
1412	}
1413
1414	expected = strings.Join([]string{
1415		":4",
1416		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1417
1418	if output != expected {
1419		t.Errorf("expected: %q, got: %q", expected, output)
1420	}
1421}
1422
1423func TestValidArgsFuncInBashScript(t *testing.T) {
1424	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1425	child := &Command{
1426		Use:               "child",
1427		ValidArgsFunction: validArgsFunc,
1428		Run:               emptyRun,
1429	}
1430	rootCmd.AddCommand(child)
1431
1432	buf := new(bytes.Buffer)
1433	rootCmd.GenBashCompletion(buf)
1434	output := buf.String()
1435
1436	check(t, output, "has_completion_function=1")
1437}
1438
1439func TestNoValidArgsFuncInBashScript(t *testing.T) {
1440	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1441	child := &Command{
1442		Use: "child",
1443		Run: emptyRun,
1444	}
1445	rootCmd.AddCommand(child)
1446
1447	buf := new(bytes.Buffer)
1448	rootCmd.GenBashCompletion(buf)
1449	output := buf.String()
1450
1451	checkOmit(t, output, "has_completion_function=1")
1452}
1453
1454func TestCompleteCmdInBashScript(t *testing.T) {
1455	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1456	child := &Command{
1457		Use:               "child",
1458		ValidArgsFunction: validArgsFunc,
1459		Run:               emptyRun,
1460	}
1461	rootCmd.AddCommand(child)
1462
1463	buf := new(bytes.Buffer)
1464	rootCmd.GenBashCompletion(buf)
1465	output := buf.String()
1466
1467	check(t, output, ShellCompNoDescRequestCmd)
1468}
1469
1470func TestCompleteNoDesCmdInZshScript(t *testing.T) {
1471	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1472	child := &Command{
1473		Use:               "child",
1474		ValidArgsFunction: validArgsFunc,
1475		Run:               emptyRun,
1476	}
1477	rootCmd.AddCommand(child)
1478
1479	buf := new(bytes.Buffer)
1480	rootCmd.GenZshCompletionNoDesc(buf)
1481	output := buf.String()
1482
1483	check(t, output, ShellCompNoDescRequestCmd)
1484}
1485
1486func TestCompleteCmdInZshScript(t *testing.T) {
1487	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1488	child := &Command{
1489		Use:               "child",
1490		ValidArgsFunction: validArgsFunc,
1491		Run:               emptyRun,
1492	}
1493	rootCmd.AddCommand(child)
1494
1495	buf := new(bytes.Buffer)
1496	rootCmd.GenZshCompletion(buf)
1497	output := buf.String()
1498
1499	check(t, output, ShellCompRequestCmd)
1500	checkOmit(t, output, ShellCompNoDescRequestCmd)
1501}
1502
1503func TestFlagCompletionInGo(t *testing.T) {
1504	rootCmd := &Command{
1505		Use: "root",
1506		Run: emptyRun,
1507	}
1508	rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
1509	rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
1510		completions := []string{}
1511		for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
1512			if strings.HasPrefix(comp, toComplete) {
1513				completions = append(completions, comp)
1514			}
1515		}
1516		return completions, ShellCompDirectiveDefault
1517	})
1518	rootCmd.Flags().String("filename", "", "Enter a filename")
1519	rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
1520		completions := []string{}
1521		for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
1522			if strings.HasPrefix(comp, toComplete) {
1523				completions = append(completions, comp)
1524			}
1525		}
1526		return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
1527	})
1528
1529	// Test completing an empty string
1530	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "")
1531	if err != nil {
1532		t.Errorf("Unexpected error: %v", err)
1533	}
1534
1535	expected := strings.Join([]string{
1536		"1",
1537		"2",
1538		"10",
1539		":0",
1540		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1541
1542	if output != expected {
1543		t.Errorf("expected: %q, got: %q", expected, output)
1544	}
1545
1546	// Check completing with a prefix
1547	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "1")
1548	if err != nil {
1549		t.Errorf("Unexpected error: %v", err)
1550	}
1551
1552	expected = strings.Join([]string{
1553		"1",
1554		"10",
1555		":0",
1556		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1557
1558	if output != expected {
1559		t.Errorf("expected: %q, got: %q", expected, output)
1560	}
1561
1562	// Test completing an empty string
1563	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "")
1564	if err != nil {
1565		t.Errorf("Unexpected error: %v", err)
1566	}
1567
1568	expected = strings.Join([]string{
1569		"file.yaml",
1570		"myfile.json",
1571		"file.xml",
1572		":6",
1573		"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
1574
1575	if output != expected {
1576		t.Errorf("expected: %q, got: %q", expected, output)
1577	}
1578
1579	// Check completing with a prefix
1580	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "f")
1581	if err != nil {
1582		t.Errorf("Unexpected error: %v", err)
1583	}
1584
1585	expected = strings.Join([]string{
1586		"file.yaml",
1587		"file.xml",
1588		":6",
1589		"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
1590
1591	if output != expected {
1592		t.Errorf("expected: %q, got: %q", expected, output)
1593	}
1594}
1595
1596func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) {
1597	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1598	child1Cmd := &Command{
1599		Use:               "child1",
1600		ValidArgsFunction: validArgsFunc,
1601		Run:               emptyRun,
1602	}
1603	child2Cmd := &Command{
1604		Use:               "child2",
1605		ValidArgsFunction: validArgsFunc2,
1606		Run:               emptyRun,
1607	}
1608	rootCmd.AddCommand(child1Cmd, child2Cmd)
1609
1610	// Test completion of first sub-command with empty argument
1611	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child1", "")
1612	if err != nil {
1613		t.Errorf("Unexpected error: %v", err)
1614	}
1615
1616	expected := strings.Join([]string{
1617		"one\tThe first",
1618		"two\tThe second",
1619		":0",
1620		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1621
1622	if output != expected {
1623		t.Errorf("expected: %q, got: %q", expected, output)
1624	}
1625
1626	// Test completion of first sub-command with a prefix to complete
1627	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "t")
1628	if err != nil {
1629		t.Errorf("Unexpected error: %v", err)
1630	}
1631
1632	expected = strings.Join([]string{
1633		"two\tThe second",
1634		":0",
1635		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1636
1637	if output != expected {
1638		t.Errorf("expected: %q, got: %q", expected, output)
1639	}
1640
1641	// Check completing with wrong number of args
1642	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "unexpectedArg", "t")
1643	if err != nil {
1644		t.Errorf("Unexpected error: %v", err)
1645	}
1646
1647	expected = strings.Join([]string{
1648		":4",
1649		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1650
1651	if output != expected {
1652		t.Errorf("expected: %q, got: %q", expected, output)
1653	}
1654
1655	// Test completion of second sub-command with empty argument
1656	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "")
1657	if err != nil {
1658		t.Errorf("Unexpected error: %v", err)
1659	}
1660
1661	expected = strings.Join([]string{
1662		"three\tThe third",
1663		"four\tThe fourth",
1664		":0",
1665		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1666
1667	if output != expected {
1668		t.Errorf("expected: %q, got: %q", expected, output)
1669	}
1670
1671	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "t")
1672	if err != nil {
1673		t.Errorf("Unexpected error: %v", err)
1674	}
1675
1676	expected = strings.Join([]string{
1677		"three\tThe third",
1678		":0",
1679		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1680
1681	if output != expected {
1682		t.Errorf("expected: %q, got: %q", expected, output)
1683	}
1684
1685	// Check completing with wrong number of args
1686	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "unexpectedArg", "t")
1687	if err != nil {
1688		t.Errorf("Unexpected error: %v", err)
1689	}
1690
1691	expected = strings.Join([]string{
1692		":4",
1693		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1694
1695	if output != expected {
1696		t.Errorf("expected: %q, got: %q", expected, output)
1697	}
1698}
1699
1700func TestFlagCompletionInGoWithDesc(t *testing.T) {
1701	rootCmd := &Command{
1702		Use: "root",
1703		Run: emptyRun,
1704	}
1705	rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
1706	rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
1707		completions := []string{}
1708		for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
1709			if strings.HasPrefix(comp, toComplete) {
1710				completions = append(completions, comp)
1711			}
1712		}
1713		return completions, ShellCompDirectiveDefault
1714	})
1715	rootCmd.Flags().String("filename", "", "Enter a filename")
1716	rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
1717		completions := []string{}
1718		for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
1719			if strings.HasPrefix(comp, toComplete) {
1720				completions = append(completions, comp)
1721			}
1722		}
1723		return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
1724	})
1725
1726	// Test completing an empty string
1727	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "")
1728	if err != nil {
1729		t.Errorf("Unexpected error: %v", err)
1730	}
1731
1732	expected := strings.Join([]string{
1733		"1\tThe first",
1734		"2\tThe second",
1735		"10\tThe tenth",
1736		":0",
1737		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1738
1739	if output != expected {
1740		t.Errorf("expected: %q, got: %q", expected, output)
1741	}
1742
1743	// Check completing with a prefix
1744	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "1")
1745	if err != nil {
1746		t.Errorf("Unexpected error: %v", err)
1747	}
1748
1749	expected = strings.Join([]string{
1750		"1\tThe first",
1751		"10\tThe tenth",
1752		":0",
1753		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
1754
1755	if output != expected {
1756		t.Errorf("expected: %q, got: %q", expected, output)
1757	}
1758
1759	// Test completing an empty string
1760	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "")
1761	if err != nil {
1762		t.Errorf("Unexpected error: %v", err)
1763	}
1764
1765	expected = strings.Join([]string{
1766		"file.yaml\tYAML format",
1767		"myfile.json\tJSON format",
1768		"file.xml\tXML format",
1769		":6",
1770		"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
1771
1772	if output != expected {
1773		t.Errorf("expected: %q, got: %q", expected, output)
1774	}
1775
1776	// Check completing with a prefix
1777	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "f")
1778	if err != nil {
1779		t.Errorf("Unexpected error: %v", err)
1780	}
1781
1782	expected = strings.Join([]string{
1783		"file.yaml\tYAML format",
1784		"file.xml\tXML format",
1785		":6",
1786		"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
1787
1788	if output != expected {
1789		t.Errorf("expected: %q, got: %q", expected, output)
1790	}
1791}
1792
1793func TestValidArgsNotValidArgsFunc(t *testing.T) {
1794	rootCmd := &Command{
1795		Use:       "root",
1796		ValidArgs: []string{"one", "two"},
1797		ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
1798			return []string{"three", "four"}, ShellCompDirectiveNoFileComp
1799		},
1800		Run: emptyRun,
1801	}
1802
1803	// Test that if both ValidArgs and ValidArgsFunction are present
1804	// only ValidArgs is considered
1805	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
1806	if err != nil {
1807		t.Errorf("Unexpected error: %v", err)
1808	}
1809
1810	expected := strings.Join([]string{
1811		"one",
1812		"two",
1813		":4",
1814		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1815
1816	if output != expected {
1817		t.Errorf("expected: %q, got: %q", expected, output)
1818	}
1819
1820	// Check completing with a prefix
1821	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
1822	if err != nil {
1823		t.Errorf("Unexpected error: %v", err)
1824	}
1825
1826	expected = strings.Join([]string{
1827		"two",
1828		":4",
1829		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1830
1831	if output != expected {
1832		t.Errorf("expected: %q, got: %q", expected, output)
1833	}
1834}
1835
1836func TestArgAliasesCompletionInGo(t *testing.T) {
1837	rootCmd := &Command{
1838		Use:        "root",
1839		Args:       OnlyValidArgs,
1840		ValidArgs:  []string{"one", "two", "three"},
1841		ArgAliases: []string{"un", "deux", "trois"},
1842		Run:        emptyRun,
1843	}
1844
1845	// Test that argaliases are not completed when there are validargs that match
1846	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
1847	if err != nil {
1848		t.Errorf("Unexpected error: %v", err)
1849	}
1850
1851	expected := strings.Join([]string{
1852		"one",
1853		"two",
1854		"three",
1855		":4",
1856		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1857
1858	if output != expected {
1859		t.Errorf("expected: %q, got: %q", expected, output)
1860	}
1861
1862	// Test that argaliases are not completed when there are validargs that match using a prefix
1863	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
1864	if err != nil {
1865		t.Errorf("Unexpected error: %v", err)
1866	}
1867
1868	expected = strings.Join([]string{
1869		"two",
1870		"three",
1871		":4",
1872		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1873
1874	if output != expected {
1875		t.Errorf("expected: %q, got: %q", expected, output)
1876	}
1877
1878	// Test that argaliases are completed when there are no validargs that match
1879	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "tr")
1880	if err != nil {
1881		t.Errorf("Unexpected error: %v", err)
1882	}
1883
1884	expected = strings.Join([]string{
1885		"trois",
1886		":4",
1887		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1888
1889	if output != expected {
1890		t.Errorf("expected: %q, got: %q", expected, output)
1891	}
1892}
1893
1894func TestCompleteHelp(t *testing.T) {
1895	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1896	child1Cmd := &Command{
1897		Use: "child1",
1898		Run: emptyRun,
1899	}
1900	child2Cmd := &Command{
1901		Use: "child2",
1902		Run: emptyRun,
1903	}
1904	rootCmd.AddCommand(child1Cmd, child2Cmd)
1905
1906	child3Cmd := &Command{
1907		Use: "child3",
1908		Run: emptyRun,
1909	}
1910	child1Cmd.AddCommand(child3Cmd)
1911
1912	// Test that completion includes the help command
1913	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
1914	if err != nil {
1915		t.Errorf("Unexpected error: %v", err)
1916	}
1917
1918	expected := strings.Join([]string{
1919		"child1",
1920		"child2",
1921		"help",
1922		":4",
1923		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1924
1925	if output != expected {
1926		t.Errorf("expected: %q, got: %q", expected, output)
1927	}
1928
1929	// Test sub-commands are completed on first level of help command
1930	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "")
1931	if err != nil {
1932		t.Errorf("Unexpected error: %v", err)
1933	}
1934
1935	expected = strings.Join([]string{
1936		"child1",
1937		"child2",
1938		"help", // "<program> help help" is a valid command, so should be completed
1939		":4",
1940		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1941
1942	if output != expected {
1943		t.Errorf("expected: %q, got: %q", expected, output)
1944	}
1945
1946	// Test sub-commands are completed on first level of help command
1947	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "child1", "")
1948	if err != nil {
1949		t.Errorf("Unexpected error: %v", err)
1950	}
1951
1952	expected = strings.Join([]string{
1953		"child3",
1954		":4",
1955		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
1956
1957	if output != expected {
1958		t.Errorf("expected: %q, got: %q", expected, output)
1959	}
1960}
1961