1package cobra
2
3import (
4	"bytes"
5	"context"
6	"fmt"
7	"io/ioutil"
8	"os"
9	"reflect"
10	"strings"
11	"testing"
12
13	"github.com/spf13/pflag"
14)
15
16func emptyRun(*Command, []string) {}
17
18func executeCommand(root *Command, args ...string) (output string, err error) {
19	_, output, err = executeCommandC(root, args...)
20	return output, err
21}
22
23func executeCommandWithContext(ctx context.Context, root *Command, args ...string) (output string, err error) {
24	buf := new(bytes.Buffer)
25	root.SetOut(buf)
26	root.SetErr(buf)
27	root.SetArgs(args)
28
29	err = root.ExecuteContext(ctx)
30
31	return buf.String(), err
32}
33
34func executeCommandC(root *Command, args ...string) (c *Command, output string, err error) {
35	buf := new(bytes.Buffer)
36	root.SetOut(buf)
37	root.SetErr(buf)
38	root.SetArgs(args)
39
40	c, err = root.ExecuteC()
41
42	return c, buf.String(), err
43}
44
45func executeCommandWithContextC(ctx context.Context, root *Command, args ...string) (c *Command, output string, err error) {
46	buf := new(bytes.Buffer)
47	root.SetOut(buf)
48	root.SetErr(buf)
49	root.SetArgs(args)
50
51	c, err = root.ExecuteContextC(ctx)
52
53	return c, buf.String(), err
54}
55
56func resetCommandLineFlagSet() {
57	pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
58}
59
60func checkStringContains(t *testing.T, got, expected string) {
61	if !strings.Contains(got, expected) {
62		t.Errorf("Expected to contain: \n %v\nGot:\n %v\n", expected, got)
63	}
64}
65
66func checkStringOmits(t *testing.T, got, expected string) {
67	if strings.Contains(got, expected) {
68		t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got)
69	}
70}
71
72const onetwo = "one two"
73
74func TestSingleCommand(t *testing.T) {
75	var rootCmdArgs []string
76	rootCmd := &Command{
77		Use:  "root",
78		Args: ExactArgs(2),
79		Run:  func(_ *Command, args []string) { rootCmdArgs = args },
80	}
81	aCmd := &Command{Use: "a", Args: NoArgs, Run: emptyRun}
82	bCmd := &Command{Use: "b", Args: NoArgs, Run: emptyRun}
83	rootCmd.AddCommand(aCmd, bCmd)
84
85	output, err := executeCommand(rootCmd, "one", "two")
86	if output != "" {
87		t.Errorf("Unexpected output: %v", output)
88	}
89	if err != nil {
90		t.Errorf("Unexpected error: %v", err)
91	}
92
93	got := strings.Join(rootCmdArgs, " ")
94	if got != onetwo {
95		t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
96	}
97}
98
99func TestChildCommand(t *testing.T) {
100	var child1CmdArgs []string
101	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
102	child1Cmd := &Command{
103		Use:  "child1",
104		Args: ExactArgs(2),
105		Run:  func(_ *Command, args []string) { child1CmdArgs = args },
106	}
107	child2Cmd := &Command{Use: "child2", Args: NoArgs, Run: emptyRun}
108	rootCmd.AddCommand(child1Cmd, child2Cmd)
109
110	output, err := executeCommand(rootCmd, "child1", "one", "two")
111	if output != "" {
112		t.Errorf("Unexpected output: %v", output)
113	}
114	if err != nil {
115		t.Errorf("Unexpected error: %v", err)
116	}
117
118	got := strings.Join(child1CmdArgs, " ")
119	if got != onetwo {
120		t.Errorf("child1CmdArgs expected: %q, got: %q", onetwo, got)
121	}
122}
123
124func TestCallCommandWithoutSubcommands(t *testing.T) {
125	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
126	_, err := executeCommand(rootCmd)
127	if err != nil {
128		t.Errorf("Calling command without subcommands should not have error: %v", err)
129	}
130}
131
132func TestRootExecuteUnknownCommand(t *testing.T) {
133	rootCmd := &Command{Use: "root", Run: emptyRun}
134	rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
135
136	output, _ := executeCommand(rootCmd, "unknown")
137
138	expected := "Error: unknown command \"unknown\" for \"root\"\nRun 'root --help' for usage.\n"
139
140	if output != expected {
141		t.Errorf("Expected:\n %q\nGot:\n %q\n", expected, output)
142	}
143}
144
145func TestSubcommandExecuteC(t *testing.T) {
146	rootCmd := &Command{Use: "root", Run: emptyRun}
147	childCmd := &Command{Use: "child", Run: emptyRun}
148	rootCmd.AddCommand(childCmd)
149
150	c, output, err := executeCommandC(rootCmd, "child")
151	if output != "" {
152		t.Errorf("Unexpected output: %v", output)
153	}
154	if err != nil {
155		t.Errorf("Unexpected error: %v", err)
156	}
157
158	if c.Name() != "child" {
159		t.Errorf(`invalid command returned from ExecuteC: expected "child"', got: %q`, c.Name())
160	}
161}
162
163func TestExecuteContext(t *testing.T) {
164	ctx := context.TODO()
165
166	ctxRun := func(cmd *Command, args []string) {
167		if cmd.Context() != ctx {
168			t.Errorf("Command %q must have context when called with ExecuteContext", cmd.Use)
169		}
170	}
171
172	rootCmd := &Command{Use: "root", Run: ctxRun, PreRun: ctxRun}
173	childCmd := &Command{Use: "child", Run: ctxRun, PreRun: ctxRun}
174	granchildCmd := &Command{Use: "grandchild", Run: ctxRun, PreRun: ctxRun}
175
176	childCmd.AddCommand(granchildCmd)
177	rootCmd.AddCommand(childCmd)
178
179	if _, err := executeCommandWithContext(ctx, rootCmd, ""); err != nil {
180		t.Errorf("Root command must not fail: %+v", err)
181	}
182
183	if _, err := executeCommandWithContext(ctx, rootCmd, "child"); err != nil {
184		t.Errorf("Subcommand must not fail: %+v", err)
185	}
186
187	if _, err := executeCommandWithContext(ctx, rootCmd, "child", "grandchild"); err != nil {
188		t.Errorf("Command child must not fail: %+v", err)
189	}
190}
191
192func TestExecuteContextC(t *testing.T) {
193	ctx := context.TODO()
194
195	ctxRun := func(cmd *Command, args []string) {
196		if cmd.Context() != ctx {
197			t.Errorf("Command %q must have context when called with ExecuteContext", cmd.Use)
198		}
199	}
200
201	rootCmd := &Command{Use: "root", Run: ctxRun, PreRun: ctxRun}
202	childCmd := &Command{Use: "child", Run: ctxRun, PreRun: ctxRun}
203	granchildCmd := &Command{Use: "grandchild", Run: ctxRun, PreRun: ctxRun}
204
205	childCmd.AddCommand(granchildCmd)
206	rootCmd.AddCommand(childCmd)
207
208	if _, _, err := executeCommandWithContextC(ctx, rootCmd, ""); err != nil {
209		t.Errorf("Root command must not fail: %+v", err)
210	}
211
212	if _, _, err := executeCommandWithContextC(ctx, rootCmd, "child"); err != nil {
213		t.Errorf("Subcommand must not fail: %+v", err)
214	}
215
216	if _, _, err := executeCommandWithContextC(ctx, rootCmd, "child", "grandchild"); err != nil {
217		t.Errorf("Command child must not fail: %+v", err)
218	}
219}
220
221func TestExecute_NoContext(t *testing.T) {
222	run := func(cmd *Command, args []string) {
223		if cmd.Context() != context.Background() {
224			t.Errorf("Command %s must have background context", cmd.Use)
225		}
226	}
227
228	rootCmd := &Command{Use: "root", Run: run, PreRun: run}
229	childCmd := &Command{Use: "child", Run: run, PreRun: run}
230	granchildCmd := &Command{Use: "grandchild", Run: run, PreRun: run}
231
232	childCmd.AddCommand(granchildCmd)
233	rootCmd.AddCommand(childCmd)
234
235	if _, err := executeCommand(rootCmd, ""); err != nil {
236		t.Errorf("Root command must not fail: %+v", err)
237	}
238
239	if _, err := executeCommand(rootCmd, "child"); err != nil {
240		t.Errorf("Subcommand must not fail: %+v", err)
241	}
242
243	if _, err := executeCommand(rootCmd, "child", "grandchild"); err != nil {
244		t.Errorf("Command child must not fail: %+v", err)
245	}
246}
247
248func TestRootUnknownCommandSilenced(t *testing.T) {
249	rootCmd := &Command{Use: "root", Run: emptyRun}
250	rootCmd.SilenceErrors = true
251	rootCmd.SilenceUsage = true
252	rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
253
254	output, _ := executeCommand(rootCmd, "unknown")
255	if output != "" {
256		t.Errorf("Expected blank output, because of silenced usage.\nGot:\n %q\n", output)
257	}
258}
259
260func TestCommandAlias(t *testing.T) {
261	var timesCmdArgs []string
262	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
263	echoCmd := &Command{
264		Use:     "echo",
265		Aliases: []string{"say", "tell"},
266		Args:    NoArgs,
267		Run:     emptyRun,
268	}
269	timesCmd := &Command{
270		Use:  "times",
271		Args: ExactArgs(2),
272		Run:  func(_ *Command, args []string) { timesCmdArgs = args },
273	}
274	echoCmd.AddCommand(timesCmd)
275	rootCmd.AddCommand(echoCmd)
276
277	output, err := executeCommand(rootCmd, "tell", "times", "one", "two")
278	if output != "" {
279		t.Errorf("Unexpected output: %v", output)
280	}
281	if err != nil {
282		t.Errorf("Unexpected error: %v", err)
283	}
284
285	got := strings.Join(timesCmdArgs, " ")
286	if got != onetwo {
287		t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
288	}
289}
290
291func TestEnablePrefixMatching(t *testing.T) {
292	EnablePrefixMatching = true
293
294	var aCmdArgs []string
295	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
296	aCmd := &Command{
297		Use:  "aCmd",
298		Args: ExactArgs(2),
299		Run:  func(_ *Command, args []string) { aCmdArgs = args },
300	}
301	bCmd := &Command{Use: "bCmd", Args: NoArgs, Run: emptyRun}
302	rootCmd.AddCommand(aCmd, bCmd)
303
304	output, err := executeCommand(rootCmd, "a", "one", "two")
305	if output != "" {
306		t.Errorf("Unexpected output: %v", output)
307	}
308	if err != nil {
309		t.Errorf("Unexpected error: %v", err)
310	}
311
312	got := strings.Join(aCmdArgs, " ")
313	if got != onetwo {
314		t.Errorf("aCmdArgs expected: %q, got: %q", onetwo, got)
315	}
316
317	EnablePrefixMatching = false
318}
319
320func TestAliasPrefixMatching(t *testing.T) {
321	EnablePrefixMatching = true
322
323	var timesCmdArgs []string
324	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
325	echoCmd := &Command{
326		Use:     "echo",
327		Aliases: []string{"say", "tell"},
328		Args:    NoArgs,
329		Run:     emptyRun,
330	}
331	timesCmd := &Command{
332		Use:  "times",
333		Args: ExactArgs(2),
334		Run:  func(_ *Command, args []string) { timesCmdArgs = args },
335	}
336	echoCmd.AddCommand(timesCmd)
337	rootCmd.AddCommand(echoCmd)
338
339	output, err := executeCommand(rootCmd, "sa", "times", "one", "two")
340	if output != "" {
341		t.Errorf("Unexpected output: %v", output)
342	}
343	if err != nil {
344		t.Errorf("Unexpected error: %v", err)
345	}
346
347	got := strings.Join(timesCmdArgs, " ")
348	if got != onetwo {
349		t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
350	}
351
352	EnablePrefixMatching = false
353}
354
355// TestChildSameName checks the correct behaviour of cobra in cases,
356// when an application with name "foo" and with subcommand "foo"
357// is executed with args "foo foo".
358func TestChildSameName(t *testing.T) {
359	var fooCmdArgs []string
360	rootCmd := &Command{Use: "foo", Args: NoArgs, Run: emptyRun}
361	fooCmd := &Command{
362		Use:  "foo",
363		Args: ExactArgs(2),
364		Run:  func(_ *Command, args []string) { fooCmdArgs = args },
365	}
366	barCmd := &Command{Use: "bar", Args: NoArgs, Run: emptyRun}
367	rootCmd.AddCommand(fooCmd, barCmd)
368
369	output, err := executeCommand(rootCmd, "foo", "one", "two")
370	if output != "" {
371		t.Errorf("Unexpected output: %v", output)
372	}
373	if err != nil {
374		t.Errorf("Unexpected error: %v", err)
375	}
376
377	got := strings.Join(fooCmdArgs, " ")
378	if got != onetwo {
379		t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
380	}
381}
382
383// TestGrandChildSameName checks the correct behaviour of cobra in cases,
384// when user has a root command and a grand child
385// with the same name.
386func TestGrandChildSameName(t *testing.T) {
387	var fooCmdArgs []string
388	rootCmd := &Command{Use: "foo", Args: NoArgs, Run: emptyRun}
389	barCmd := &Command{Use: "bar", Args: NoArgs, Run: emptyRun}
390	fooCmd := &Command{
391		Use:  "foo",
392		Args: ExactArgs(2),
393		Run:  func(_ *Command, args []string) { fooCmdArgs = args },
394	}
395	barCmd.AddCommand(fooCmd)
396	rootCmd.AddCommand(barCmd)
397
398	output, err := executeCommand(rootCmd, "bar", "foo", "one", "two")
399	if output != "" {
400		t.Errorf("Unexpected output: %v", output)
401	}
402	if err != nil {
403		t.Errorf("Unexpected error: %v", err)
404	}
405
406	got := strings.Join(fooCmdArgs, " ")
407	if got != onetwo {
408		t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
409	}
410}
411
412func TestFlagLong(t *testing.T) {
413	var cArgs []string
414	c := &Command{
415		Use:  "c",
416		Args: ArbitraryArgs,
417		Run:  func(_ *Command, args []string) { cArgs = args },
418	}
419
420	var intFlagValue int
421	var stringFlagValue string
422	c.Flags().IntVar(&intFlagValue, "intf", -1, "")
423	c.Flags().StringVar(&stringFlagValue, "sf", "", "")
424
425	output, err := executeCommand(c, "--intf=7", "--sf=abc", "one", "--", "two")
426	if output != "" {
427		t.Errorf("Unexpected output: %v", err)
428	}
429	if err != nil {
430		t.Errorf("Unexpected error: %v", err)
431	}
432
433	if c.ArgsLenAtDash() != 1 {
434		t.Errorf("Expected ArgsLenAtDash: %v but got %v", 1, c.ArgsLenAtDash())
435	}
436	if intFlagValue != 7 {
437		t.Errorf("Expected intFlagValue: %v, got %v", 7, intFlagValue)
438	}
439	if stringFlagValue != "abc" {
440		t.Errorf("Expected stringFlagValue: %q, got %q", "abc", stringFlagValue)
441	}
442
443	got := strings.Join(cArgs, " ")
444	if got != onetwo {
445		t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
446	}
447}
448
449func TestFlagShort(t *testing.T) {
450	var cArgs []string
451	c := &Command{
452		Use:  "c",
453		Args: ArbitraryArgs,
454		Run:  func(_ *Command, args []string) { cArgs = args },
455	}
456
457	var intFlagValue int
458	var stringFlagValue string
459	c.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
460	c.Flags().StringVarP(&stringFlagValue, "sf", "s", "", "")
461
462	output, err := executeCommand(c, "-i", "7", "-sabc", "one", "two")
463	if output != "" {
464		t.Errorf("Unexpected output: %v", err)
465	}
466	if err != nil {
467		t.Errorf("Unexpected error: %v", err)
468	}
469
470	if intFlagValue != 7 {
471		t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
472	}
473	if stringFlagValue != "abc" {
474		t.Errorf("Expected stringFlagValue: %q, got %q", "abc", stringFlagValue)
475	}
476
477	got := strings.Join(cArgs, " ")
478	if got != onetwo {
479		t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
480	}
481}
482
483func TestChildFlag(t *testing.T) {
484	rootCmd := &Command{Use: "root", Run: emptyRun}
485	childCmd := &Command{Use: "child", Run: emptyRun}
486	rootCmd.AddCommand(childCmd)
487
488	var intFlagValue int
489	childCmd.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
490
491	output, err := executeCommand(rootCmd, "child", "-i7")
492	if output != "" {
493		t.Errorf("Unexpected output: %v", err)
494	}
495	if err != nil {
496		t.Errorf("Unexpected error: %v", err)
497	}
498
499	if intFlagValue != 7 {
500		t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
501	}
502}
503
504func TestChildFlagWithParentLocalFlag(t *testing.T) {
505	rootCmd := &Command{Use: "root", Run: emptyRun}
506	childCmd := &Command{Use: "child", Run: emptyRun}
507	rootCmd.AddCommand(childCmd)
508
509	var intFlagValue int
510	rootCmd.Flags().StringP("sf", "s", "", "")
511	childCmd.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
512
513	_, err := executeCommand(rootCmd, "child", "-i7", "-sabc")
514	if err == nil {
515		t.Errorf("Invalid flag should generate error")
516	}
517
518	checkStringContains(t, err.Error(), "unknown shorthand")
519
520	if intFlagValue != 7 {
521		t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
522	}
523}
524
525func TestFlagInvalidInput(t *testing.T) {
526	rootCmd := &Command{Use: "root", Run: emptyRun}
527	rootCmd.Flags().IntP("intf", "i", -1, "")
528
529	_, err := executeCommand(rootCmd, "-iabc")
530	if err == nil {
531		t.Errorf("Invalid flag value should generate error")
532	}
533
534	checkStringContains(t, err.Error(), "invalid syntax")
535}
536
537func TestFlagBeforeCommand(t *testing.T) {
538	rootCmd := &Command{Use: "root", Run: emptyRun}
539	childCmd := &Command{Use: "child", Run: emptyRun}
540	rootCmd.AddCommand(childCmd)
541
542	var flagValue int
543	childCmd.Flags().IntVarP(&flagValue, "intf", "i", -1, "")
544
545	// With short flag.
546	_, err := executeCommand(rootCmd, "-i7", "child")
547	if err != nil {
548		t.Errorf("Unexpected error: %v", err)
549	}
550	if flagValue != 7 {
551		t.Errorf("Expected flag value: %v, got %v", 7, flagValue)
552	}
553
554	// With long flag.
555	_, err = executeCommand(rootCmd, "--intf=8", "child")
556	if err != nil {
557		t.Errorf("Unexpected error: %v", err)
558	}
559	if flagValue != 8 {
560		t.Errorf("Expected flag value: %v, got %v", 9, flagValue)
561	}
562}
563
564func TestStripFlags(t *testing.T) {
565	tests := []struct {
566		input  []string
567		output []string
568	}{
569		{
570			[]string{"foo", "bar"},
571			[]string{"foo", "bar"},
572		},
573		{
574			[]string{"foo", "--str", "-s"},
575			[]string{"foo"},
576		},
577		{
578			[]string{"-s", "foo", "--str", "bar"},
579			[]string{},
580		},
581		{
582			[]string{"-i10", "echo"},
583			[]string{"echo"},
584		},
585		{
586			[]string{"-i=10", "echo"},
587			[]string{"echo"},
588		},
589		{
590			[]string{"--int=100", "echo"},
591			[]string{"echo"},
592		},
593		{
594			[]string{"-ib", "echo", "-sfoo", "baz"},
595			[]string{"echo", "baz"},
596		},
597		{
598			[]string{"-i=baz", "bar", "-i", "foo", "blah"},
599			[]string{"bar", "blah"},
600		},
601		{
602			[]string{"--int=baz", "-sbar", "-i", "foo", "blah"},
603			[]string{"blah"},
604		},
605		{
606			[]string{"--bool", "bar", "-i", "foo", "blah"},
607			[]string{"bar", "blah"},
608		},
609		{
610			[]string{"-b", "bar", "-i", "foo", "blah"},
611			[]string{"bar", "blah"},
612		},
613		{
614			[]string{"--persist", "bar"},
615			[]string{"bar"},
616		},
617		{
618			[]string{"-p", "bar"},
619			[]string{"bar"},
620		},
621	}
622
623	c := &Command{Use: "c", Run: emptyRun}
624	c.PersistentFlags().BoolP("persist", "p", false, "")
625	c.Flags().IntP("int", "i", -1, "")
626	c.Flags().StringP("str", "s", "", "")
627	c.Flags().BoolP("bool", "b", false, "")
628
629	for i, test := range tests {
630		got := stripFlags(test.input, c)
631		if !reflect.DeepEqual(test.output, got) {
632			t.Errorf("(%v) Expected: %v, got: %v", i, test.output, got)
633		}
634	}
635}
636
637func TestDisableFlagParsing(t *testing.T) {
638	var cArgs []string
639	c := &Command{
640		Use:                "c",
641		DisableFlagParsing: true,
642		Run: func(_ *Command, args []string) {
643			cArgs = args
644		},
645	}
646
647	args := []string{"cmd", "-v", "-race", "-file", "foo.go"}
648	output, err := executeCommand(c, args...)
649	if output != "" {
650		t.Errorf("Unexpected output: %v", output)
651	}
652	if err != nil {
653		t.Errorf("Unexpected error: %v", err)
654	}
655
656	if !reflect.DeepEqual(args, cArgs) {
657		t.Errorf("Expected: %v, got: %v", args, cArgs)
658	}
659}
660
661func TestPersistentFlagsOnSameCommand(t *testing.T) {
662	var rootCmdArgs []string
663	rootCmd := &Command{
664		Use:  "root",
665		Args: ArbitraryArgs,
666		Run:  func(_ *Command, args []string) { rootCmdArgs = args },
667	}
668
669	var flagValue int
670	rootCmd.PersistentFlags().IntVarP(&flagValue, "intf", "i", -1, "")
671
672	output, err := executeCommand(rootCmd, "-i7", "one", "two")
673	if output != "" {
674		t.Errorf("Unexpected output: %v", output)
675	}
676	if err != nil {
677		t.Errorf("Unexpected error: %v", err)
678	}
679
680	got := strings.Join(rootCmdArgs, " ")
681	if got != onetwo {
682		t.Errorf("rootCmdArgs expected: %q, got %q", onetwo, got)
683	}
684	if flagValue != 7 {
685		t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
686	}
687}
688
689// TestEmptyInputs checks,
690// if flags correctly parsed with blank strings in args.
691func TestEmptyInputs(t *testing.T) {
692	c := &Command{Use: "c", Run: emptyRun}
693
694	var flagValue int
695	c.Flags().IntVarP(&flagValue, "intf", "i", -1, "")
696
697	output, err := executeCommand(c, "", "-i7", "")
698	if output != "" {
699		t.Errorf("Unexpected output: %v", output)
700	}
701	if err != nil {
702		t.Errorf("Unexpected error: %v", err)
703	}
704
705	if flagValue != 7 {
706		t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
707	}
708}
709
710func TestOverwrittenFlag(t *testing.T) {
711	// TODO: This test fails, but should work.
712	t.Skip()
713
714	parent := &Command{Use: "parent", Run: emptyRun}
715	child := &Command{Use: "child", Run: emptyRun}
716
717	parent.PersistentFlags().Bool("boolf", false, "")
718	parent.PersistentFlags().Int("intf", -1, "")
719	child.Flags().String("strf", "", "")
720	child.Flags().Int("intf", -1, "")
721
722	parent.AddCommand(child)
723
724	childInherited := child.InheritedFlags()
725	childLocal := child.LocalFlags()
726
727	if childLocal.Lookup("strf") == nil {
728		t.Error(`LocalFlags expected to contain "strf", got "nil"`)
729	}
730	if childInherited.Lookup("boolf") == nil {
731		t.Error(`InheritedFlags expected to contain "boolf", got "nil"`)
732	}
733
734	if childInherited.Lookup("intf") != nil {
735		t.Errorf(`InheritedFlags should not contain overwritten flag "intf"`)
736	}
737	if childLocal.Lookup("intf") == nil {
738		t.Error(`LocalFlags expected to contain "intf", got "nil"`)
739	}
740}
741
742func TestPersistentFlagsOnChild(t *testing.T) {
743	var childCmdArgs []string
744	rootCmd := &Command{Use: "root", Run: emptyRun}
745	childCmd := &Command{
746		Use:  "child",
747		Args: ArbitraryArgs,
748		Run:  func(_ *Command, args []string) { childCmdArgs = args },
749	}
750	rootCmd.AddCommand(childCmd)
751
752	var parentFlagValue int
753	var childFlagValue int
754	rootCmd.PersistentFlags().IntVarP(&parentFlagValue, "parentf", "p", -1, "")
755	childCmd.Flags().IntVarP(&childFlagValue, "childf", "c", -1, "")
756
757	output, err := executeCommand(rootCmd, "child", "-c7", "-p8", "one", "two")
758	if output != "" {
759		t.Errorf("Unexpected output: %v", output)
760	}
761	if err != nil {
762		t.Errorf("Unexpected error: %v", err)
763	}
764
765	got := strings.Join(childCmdArgs, " ")
766	if got != onetwo {
767		t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
768	}
769	if parentFlagValue != 8 {
770		t.Errorf("parentFlagValue expected: %v, got %v", 8, parentFlagValue)
771	}
772	if childFlagValue != 7 {
773		t.Errorf("childFlagValue expected: %v, got %v", 7, childFlagValue)
774	}
775}
776
777func TestRequiredFlags(t *testing.T) {
778	c := &Command{Use: "c", Run: emptyRun}
779	c.Flags().String("foo1", "", "")
780	assertNoErr(t, c.MarkFlagRequired("foo1"))
781	c.Flags().String("foo2", "", "")
782	assertNoErr(t, c.MarkFlagRequired("foo2"))
783	c.Flags().String("bar", "", "")
784
785	expected := fmt.Sprintf("required flag(s) %q, %q not set", "foo1", "foo2")
786
787	_, err := executeCommand(c)
788	got := err.Error()
789
790	if got != expected {
791		t.Errorf("Expected error: %q, got: %q", expected, got)
792	}
793}
794
795func TestPersistentRequiredFlags(t *testing.T) {
796	parent := &Command{Use: "parent", Run: emptyRun}
797	parent.PersistentFlags().String("foo1", "", "")
798	assertNoErr(t, parent.MarkPersistentFlagRequired("foo1"))
799	parent.PersistentFlags().String("foo2", "", "")
800	assertNoErr(t, parent.MarkPersistentFlagRequired("foo2"))
801	parent.Flags().String("foo3", "", "")
802
803	child := &Command{Use: "child", Run: emptyRun}
804	child.Flags().String("bar1", "", "")
805	assertNoErr(t, child.MarkFlagRequired("bar1"))
806	child.Flags().String("bar2", "", "")
807	assertNoErr(t, child.MarkFlagRequired("bar2"))
808	child.Flags().String("bar3", "", "")
809
810	parent.AddCommand(child)
811
812	expected := fmt.Sprintf("required flag(s) %q, %q, %q, %q not set", "bar1", "bar2", "foo1", "foo2")
813
814	_, err := executeCommand(parent, "child")
815	if err.Error() != expected {
816		t.Errorf("Expected %q, got %q", expected, err.Error())
817	}
818}
819
820func TestPersistentRequiredFlagsWithDisableFlagParsing(t *testing.T) {
821	// Make sure a required persistent flag does not break
822	// commands that disable flag parsing
823
824	parent := &Command{Use: "parent", Run: emptyRun}
825	parent.PersistentFlags().Bool("foo", false, "")
826	flag := parent.PersistentFlags().Lookup("foo")
827	assertNoErr(t, parent.MarkPersistentFlagRequired("foo"))
828
829	child := &Command{Use: "child", Run: emptyRun}
830	child.DisableFlagParsing = true
831
832	parent.AddCommand(child)
833
834	if _, err := executeCommand(parent, "--foo", "child"); err != nil {
835		t.Errorf("Unexpected error: %v", err)
836	}
837
838	// Reset the flag or else it will remember the state from the previous command
839	flag.Changed = false
840	if _, err := executeCommand(parent, "child", "--foo"); err != nil {
841		t.Errorf("Unexpected error: %v", err)
842	}
843
844	// Reset the flag or else it will remember the state from the previous command
845	flag.Changed = false
846	if _, err := executeCommand(parent, "child"); err != nil {
847		t.Errorf("Unexpected error: %v", err)
848	}
849}
850
851func TestInitHelpFlagMergesFlags(t *testing.T) {
852	usage := "custom flag"
853	rootCmd := &Command{Use: "root"}
854	rootCmd.PersistentFlags().Bool("help", false, "custom flag")
855	childCmd := &Command{Use: "child"}
856	rootCmd.AddCommand(childCmd)
857
858	childCmd.InitDefaultHelpFlag()
859	got := childCmd.Flags().Lookup("help").Usage
860	if got != usage {
861		t.Errorf("Expected the help flag from the root command with usage: %v\nGot the default with usage: %v", usage, got)
862	}
863}
864
865func TestHelpCommandExecuted(t *testing.T) {
866	rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
867	rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
868
869	output, err := executeCommand(rootCmd, "help")
870	if err != nil {
871		t.Errorf("Unexpected error: %v", err)
872	}
873
874	checkStringContains(t, output, rootCmd.Long)
875}
876
877func TestHelpCommandExecutedOnChild(t *testing.T) {
878	rootCmd := &Command{Use: "root", Run: emptyRun}
879	childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun}
880	rootCmd.AddCommand(childCmd)
881
882	output, err := executeCommand(rootCmd, "help", "child")
883	if err != nil {
884		t.Errorf("Unexpected error: %v", err)
885	}
886
887	checkStringContains(t, output, childCmd.Long)
888}
889
890func TestSetHelpCommand(t *testing.T) {
891	c := &Command{Use: "c", Run: emptyRun}
892	c.AddCommand(&Command{Use: "empty", Run: emptyRun})
893
894	expected := "WORKS"
895	c.SetHelpCommand(&Command{
896		Use:   "help [command]",
897		Short: "Help about any command",
898		Long: `Help provides help for any command in the application.
899	Simply type ` + c.Name() + ` help [path to command] for full details.`,
900		Run: func(c *Command, _ []string) { c.Print(expected) },
901	})
902
903	got, err := executeCommand(c, "help")
904	if err != nil {
905		t.Errorf("Unexpected error: %v", err)
906	}
907
908	if got != expected {
909		t.Errorf("Expected to contain %q, got %q", expected, got)
910	}
911}
912
913func TestHelpFlagExecuted(t *testing.T) {
914	rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
915
916	output, err := executeCommand(rootCmd, "--help")
917	if err != nil {
918		t.Errorf("Unexpected error: %v", err)
919	}
920
921	checkStringContains(t, output, rootCmd.Long)
922}
923
924func TestHelpFlagExecutedOnChild(t *testing.T) {
925	rootCmd := &Command{Use: "root", Run: emptyRun}
926	childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun}
927	rootCmd.AddCommand(childCmd)
928
929	output, err := executeCommand(rootCmd, "child", "--help")
930	if err != nil {
931		t.Errorf("Unexpected error: %v", err)
932	}
933
934	checkStringContains(t, output, childCmd.Long)
935}
936
937// TestHelpFlagInHelp checks,
938// if '--help' flag is shown in help for child (executing `parent help child`),
939// that has no other flags.
940// Related to https://github.com/spf13/cobra/issues/302.
941func TestHelpFlagInHelp(t *testing.T) {
942	parentCmd := &Command{Use: "parent", Run: func(*Command, []string) {}}
943
944	childCmd := &Command{Use: "child", Run: func(*Command, []string) {}}
945	parentCmd.AddCommand(childCmd)
946
947	output, err := executeCommand(parentCmd, "help", "child")
948	if err != nil {
949		t.Errorf("Unexpected error: %v", err)
950	}
951
952	checkStringContains(t, output, "[flags]")
953}
954
955func TestFlagsInUsage(t *testing.T) {
956	rootCmd := &Command{Use: "root", Args: NoArgs, Run: func(*Command, []string) {}}
957	output, err := executeCommand(rootCmd, "--help")
958	if err != nil {
959		t.Errorf("Unexpected error: %v", err)
960	}
961
962	checkStringContains(t, output, "[flags]")
963}
964
965func TestHelpExecutedOnNonRunnableChild(t *testing.T) {
966	rootCmd := &Command{Use: "root", Run: emptyRun}
967	childCmd := &Command{Use: "child", Long: "Long description"}
968	rootCmd.AddCommand(childCmd)
969
970	output, err := executeCommand(rootCmd, "child")
971	if err != nil {
972		t.Errorf("Unexpected error: %v", err)
973	}
974
975	checkStringContains(t, output, childCmd.Long)
976}
977
978func TestVersionFlagExecuted(t *testing.T) {
979	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
980
981	output, err := executeCommand(rootCmd, "--version", "arg1")
982	if err != nil {
983		t.Errorf("Unexpected error: %v", err)
984	}
985
986	checkStringContains(t, output, "root version 1.0.0")
987}
988
989func TestVersionFlagExecutedWithNoName(t *testing.T) {
990	rootCmd := &Command{Version: "1.0.0", Run: emptyRun}
991
992	output, err := executeCommand(rootCmd, "--version", "arg1")
993	if err != nil {
994		t.Errorf("Unexpected error: %v", err)
995	}
996
997	checkStringContains(t, output, "version 1.0.0")
998}
999
1000func TestShortAndLongVersionFlagInHelp(t *testing.T) {
1001	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1002
1003	output, err := executeCommand(rootCmd, "--help")
1004	if err != nil {
1005		t.Errorf("Unexpected error: %v", err)
1006	}
1007
1008	checkStringContains(t, output, "-v, --version")
1009}
1010
1011func TestLongVersionFlagOnlyInHelpWhenShortPredefined(t *testing.T) {
1012	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1013	rootCmd.Flags().StringP("foo", "v", "", "not a version flag")
1014
1015	output, err := executeCommand(rootCmd, "--help")
1016	if err != nil {
1017		t.Errorf("Unexpected error: %v", err)
1018	}
1019
1020	checkStringOmits(t, output, "-v, --version")
1021	checkStringContains(t, output, "--version")
1022}
1023
1024func TestShorthandVersionFlagExecuted(t *testing.T) {
1025	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1026
1027	output, err := executeCommand(rootCmd, "-v", "arg1")
1028	if err != nil {
1029		t.Errorf("Unexpected error: %v", err)
1030	}
1031
1032	checkStringContains(t, output, "root version 1.0.0")
1033}
1034
1035func TestVersionTemplate(t *testing.T) {
1036	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1037	rootCmd.SetVersionTemplate(`customized version: {{.Version}}`)
1038
1039	output, err := executeCommand(rootCmd, "--version", "arg1")
1040	if err != nil {
1041		t.Errorf("Unexpected error: %v", err)
1042	}
1043
1044	checkStringContains(t, output, "customized version: 1.0.0")
1045}
1046
1047func TestShorthandVersionTemplate(t *testing.T) {
1048	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1049	rootCmd.SetVersionTemplate(`customized version: {{.Version}}`)
1050
1051	output, err := executeCommand(rootCmd, "-v", "arg1")
1052	if err != nil {
1053		t.Errorf("Unexpected error: %v", err)
1054	}
1055
1056	checkStringContains(t, output, "customized version: 1.0.0")
1057}
1058
1059func TestVersionFlagExecutedOnSubcommand(t *testing.T) {
1060	rootCmd := &Command{Use: "root", Version: "1.0.0"}
1061	rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
1062
1063	output, err := executeCommand(rootCmd, "--version", "sub")
1064	if err != nil {
1065		t.Errorf("Unexpected error: %v", err)
1066	}
1067
1068	checkStringContains(t, output, "root version 1.0.0")
1069}
1070
1071func TestShorthandVersionFlagExecutedOnSubcommand(t *testing.T) {
1072	rootCmd := &Command{Use: "root", Version: "1.0.0"}
1073	rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
1074
1075	output, err := executeCommand(rootCmd, "-v", "sub")
1076	if err != nil {
1077		t.Errorf("Unexpected error: %v", err)
1078	}
1079
1080	checkStringContains(t, output, "root version 1.0.0")
1081}
1082
1083func TestVersionFlagOnlyAddedToRoot(t *testing.T) {
1084	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1085	rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
1086
1087	_, err := executeCommand(rootCmd, "sub", "--version")
1088	if err == nil {
1089		t.Errorf("Expected error")
1090	}
1091
1092	checkStringContains(t, err.Error(), "unknown flag: --version")
1093}
1094
1095func TestShortVersionFlagOnlyAddedToRoot(t *testing.T) {
1096	rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1097	rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
1098
1099	_, err := executeCommand(rootCmd, "sub", "-v")
1100	if err == nil {
1101		t.Errorf("Expected error")
1102	}
1103
1104	checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
1105}
1106
1107func TestVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) {
1108	rootCmd := &Command{Use: "root", Run: emptyRun}
1109
1110	_, err := executeCommand(rootCmd, "--version")
1111	if err == nil {
1112		t.Errorf("Expected error")
1113	}
1114	checkStringContains(t, err.Error(), "unknown flag: --version")
1115}
1116
1117func TestShorthandVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) {
1118	rootCmd := &Command{Use: "root", Run: emptyRun}
1119
1120	_, err := executeCommand(rootCmd, "-v")
1121	if err == nil {
1122		t.Errorf("Expected error")
1123	}
1124	checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
1125}
1126
1127func TestShorthandVersionFlagOnlyAddedIfShorthandNotDefined(t *testing.T) {
1128	rootCmd := &Command{Use: "root", Run: emptyRun, Version: "1.2.3"}
1129	rootCmd.Flags().StringP("notversion", "v", "", "not a version flag")
1130
1131	_, err := executeCommand(rootCmd, "-v")
1132	if err == nil {
1133		t.Errorf("Expected error")
1134	}
1135	check(t, rootCmd.Flags().ShorthandLookup("v").Name, "notversion")
1136	checkStringContains(t, err.Error(), "flag needs an argument: 'v' in -v")
1137}
1138
1139func TestShorthandVersionFlagOnlyAddedIfVersionNotDefined(t *testing.T) {
1140	rootCmd := &Command{Use: "root", Run: emptyRun, Version: "1.2.3"}
1141	rootCmd.Flags().Bool("version", false, "a different kind of version flag")
1142
1143	_, err := executeCommand(rootCmd, "-v")
1144	if err == nil {
1145		t.Errorf("Expected error")
1146	}
1147	checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
1148}
1149
1150func TestUsageIsNotPrintedTwice(t *testing.T) {
1151	var cmd = &Command{Use: "root"}
1152	var sub = &Command{Use: "sub"}
1153	cmd.AddCommand(sub)
1154
1155	output, _ := executeCommand(cmd, "")
1156	if strings.Count(output, "Usage:") != 1 {
1157		t.Error("Usage output is not printed exactly once")
1158	}
1159}
1160
1161func TestVisitParents(t *testing.T) {
1162	c := &Command{Use: "app"}
1163	sub := &Command{Use: "sub"}
1164	dsub := &Command{Use: "dsub"}
1165	sub.AddCommand(dsub)
1166	c.AddCommand(sub)
1167
1168	total := 0
1169	add := func(x *Command) {
1170		total++
1171	}
1172	sub.VisitParents(add)
1173	if total != 1 {
1174		t.Errorf("Should have visited 1 parent but visited %d", total)
1175	}
1176
1177	total = 0
1178	dsub.VisitParents(add)
1179	if total != 2 {
1180		t.Errorf("Should have visited 2 parents but visited %d", total)
1181	}
1182
1183	total = 0
1184	c.VisitParents(add)
1185	if total != 0 {
1186		t.Errorf("Should have visited no parents but visited %d", total)
1187	}
1188}
1189
1190func TestSuggestions(t *testing.T) {
1191	rootCmd := &Command{Use: "root", Run: emptyRun}
1192	timesCmd := &Command{
1193		Use:        "times",
1194		SuggestFor: []string{"counts"},
1195		Run:        emptyRun,
1196	}
1197	rootCmd.AddCommand(timesCmd)
1198
1199	templateWithSuggestions := "Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n"
1200	templateWithoutSuggestions := "Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n"
1201
1202	tests := map[string]string{
1203		"time":     "times",
1204		"tiems":    "times",
1205		"tims":     "times",
1206		"timeS":    "times",
1207		"rimes":    "times",
1208		"ti":       "times",
1209		"t":        "times",
1210		"timely":   "times",
1211		"ri":       "",
1212		"timezone": "",
1213		"foo":      "",
1214		"counts":   "times",
1215	}
1216
1217	for typo, suggestion := range tests {
1218		for _, suggestionsDisabled := range []bool{true, false} {
1219			rootCmd.DisableSuggestions = suggestionsDisabled
1220
1221			var expected string
1222			output, _ := executeCommand(rootCmd, typo)
1223
1224			if suggestion == "" || suggestionsDisabled {
1225				expected = fmt.Sprintf(templateWithoutSuggestions, typo)
1226			} else {
1227				expected = fmt.Sprintf(templateWithSuggestions, typo, suggestion)
1228			}
1229
1230			if output != expected {
1231				t.Errorf("Unexpected response.\nExpected:\n %q\nGot:\n %q\n", expected, output)
1232			}
1233		}
1234	}
1235}
1236
1237func TestRemoveCommand(t *testing.T) {
1238	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1239	childCmd := &Command{Use: "child", Run: emptyRun}
1240	rootCmd.AddCommand(childCmd)
1241	rootCmd.RemoveCommand(childCmd)
1242
1243	_, err := executeCommand(rootCmd, "child")
1244	if err == nil {
1245		t.Error("Expected error on calling removed command. Got nil.")
1246	}
1247}
1248
1249func TestReplaceCommandWithRemove(t *testing.T) {
1250	childUsed := 0
1251	rootCmd := &Command{Use: "root", Run: emptyRun}
1252	child1Cmd := &Command{
1253		Use: "child",
1254		Run: func(*Command, []string) { childUsed = 1 },
1255	}
1256	child2Cmd := &Command{
1257		Use: "child",
1258		Run: func(*Command, []string) { childUsed = 2 },
1259	}
1260	rootCmd.AddCommand(child1Cmd)
1261	rootCmd.RemoveCommand(child1Cmd)
1262	rootCmd.AddCommand(child2Cmd)
1263
1264	output, err := executeCommand(rootCmd, "child")
1265	if output != "" {
1266		t.Errorf("Unexpected output: %v", output)
1267	}
1268	if err != nil {
1269		t.Errorf("Unexpected error: %v", err)
1270	}
1271
1272	if childUsed == 1 {
1273		t.Error("Removed command shouldn't be called")
1274	}
1275	if childUsed != 2 {
1276		t.Error("Replacing command should have been called but didn't")
1277	}
1278}
1279
1280func TestDeprecatedCommand(t *testing.T) {
1281	rootCmd := &Command{Use: "root", Run: emptyRun}
1282	deprecatedCmd := &Command{
1283		Use:        "deprecated",
1284		Deprecated: "This command is deprecated",
1285		Run:        emptyRun,
1286	}
1287	rootCmd.AddCommand(deprecatedCmd)
1288
1289	output, err := executeCommand(rootCmd, "deprecated")
1290	if err != nil {
1291		t.Errorf("Unexpected error: %v", err)
1292	}
1293
1294	checkStringContains(t, output, deprecatedCmd.Deprecated)
1295}
1296
1297func TestHooks(t *testing.T) {
1298	var (
1299		persPreArgs  string
1300		preArgs      string
1301		runArgs      string
1302		postArgs     string
1303		persPostArgs string
1304	)
1305
1306	c := &Command{
1307		Use: "c",
1308		PersistentPreRun: func(_ *Command, args []string) {
1309			persPreArgs = strings.Join(args, " ")
1310		},
1311		PreRun: func(_ *Command, args []string) {
1312			preArgs = strings.Join(args, " ")
1313		},
1314		Run: func(_ *Command, args []string) {
1315			runArgs = strings.Join(args, " ")
1316		},
1317		PostRun: func(_ *Command, args []string) {
1318			postArgs = strings.Join(args, " ")
1319		},
1320		PersistentPostRun: func(_ *Command, args []string) {
1321			persPostArgs = strings.Join(args, " ")
1322		},
1323	}
1324
1325	output, err := executeCommand(c, "one", "two")
1326	if output != "" {
1327		t.Errorf("Unexpected output: %v", output)
1328	}
1329	if err != nil {
1330		t.Errorf("Unexpected error: %v", err)
1331	}
1332
1333	for _, v := range []struct {
1334		name string
1335		got  string
1336	}{
1337		{"persPreArgs", persPreArgs},
1338		{"preArgs", preArgs},
1339		{"runArgs", runArgs},
1340		{"postArgs", postArgs},
1341		{"persPostArgs", persPostArgs},
1342	} {
1343		if v.got != onetwo {
1344			t.Errorf("Expected %s %q, got %q", v.name, onetwo, v.got)
1345		}
1346	}
1347}
1348
1349func TestPersistentHooks(t *testing.T) {
1350	var (
1351		parentPersPreArgs  string
1352		parentPreArgs      string
1353		parentRunArgs      string
1354		parentPostArgs     string
1355		parentPersPostArgs string
1356	)
1357
1358	var (
1359		childPersPreArgs  string
1360		childPreArgs      string
1361		childRunArgs      string
1362		childPostArgs     string
1363		childPersPostArgs string
1364	)
1365
1366	parentCmd := &Command{
1367		Use: "parent",
1368		PersistentPreRun: func(_ *Command, args []string) {
1369			parentPersPreArgs = strings.Join(args, " ")
1370		},
1371		PreRun: func(_ *Command, args []string) {
1372			parentPreArgs = strings.Join(args, " ")
1373		},
1374		Run: func(_ *Command, args []string) {
1375			parentRunArgs = strings.Join(args, " ")
1376		},
1377		PostRun: func(_ *Command, args []string) {
1378			parentPostArgs = strings.Join(args, " ")
1379		},
1380		PersistentPostRun: func(_ *Command, args []string) {
1381			parentPersPostArgs = strings.Join(args, " ")
1382		},
1383	}
1384
1385	childCmd := &Command{
1386		Use: "child",
1387		PersistentPreRun: func(_ *Command, args []string) {
1388			childPersPreArgs = strings.Join(args, " ")
1389		},
1390		PreRun: func(_ *Command, args []string) {
1391			childPreArgs = strings.Join(args, " ")
1392		},
1393		Run: func(_ *Command, args []string) {
1394			childRunArgs = strings.Join(args, " ")
1395		},
1396		PostRun: func(_ *Command, args []string) {
1397			childPostArgs = strings.Join(args, " ")
1398		},
1399		PersistentPostRun: func(_ *Command, args []string) {
1400			childPersPostArgs = strings.Join(args, " ")
1401		},
1402	}
1403	parentCmd.AddCommand(childCmd)
1404
1405	output, err := executeCommand(parentCmd, "child", "one", "two")
1406	if output != "" {
1407		t.Errorf("Unexpected output: %v", output)
1408	}
1409	if err != nil {
1410		t.Errorf("Unexpected error: %v", err)
1411	}
1412
1413	for _, v := range []struct {
1414		name string
1415		got  string
1416	}{
1417		// TODO: currently PersistenPreRun* defined in parent does not
1418		// run if the matchin child subcommand has PersistenPreRun.
1419		// If the behavior changes (https://github.com/spf13/cobra/issues/252)
1420		// this test must be fixed.
1421		{"parentPersPreArgs", parentPersPreArgs},
1422		{"parentPreArgs", parentPreArgs},
1423		{"parentRunArgs", parentRunArgs},
1424		{"parentPostArgs", parentPostArgs},
1425		// TODO: currently PersistenPostRun* defined in parent does not
1426		// run if the matchin child subcommand has PersistenPostRun.
1427		// If the behavior changes (https://github.com/spf13/cobra/issues/252)
1428		// this test must be fixed.
1429		{"parentPersPostArgs", parentPersPostArgs},
1430	} {
1431		if v.got != "" {
1432			t.Errorf("Expected blank %s, got %q", v.name, v.got)
1433		}
1434	}
1435
1436	for _, v := range []struct {
1437		name string
1438		got  string
1439	}{
1440		{"childPersPreArgs", childPersPreArgs},
1441		{"childPreArgs", childPreArgs},
1442		{"childRunArgs", childRunArgs},
1443		{"childPostArgs", childPostArgs},
1444		{"childPersPostArgs", childPersPostArgs},
1445	} {
1446		if v.got != onetwo {
1447			t.Errorf("Expected %s %q, got %q", v.name, onetwo, v.got)
1448		}
1449	}
1450}
1451
1452// Related to https://github.com/spf13/cobra/issues/521.
1453func TestGlobalNormFuncPropagation(t *testing.T) {
1454	normFunc := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
1455		return pflag.NormalizedName(name)
1456	}
1457
1458	rootCmd := &Command{Use: "root", Run: emptyRun}
1459	childCmd := &Command{Use: "child", Run: emptyRun}
1460	rootCmd.AddCommand(childCmd)
1461
1462	rootCmd.SetGlobalNormalizationFunc(normFunc)
1463	if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() {
1464		t.Error("rootCmd seems to have a wrong normalization function")
1465	}
1466
1467	if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(childCmd.GlobalNormalizationFunc()).Pointer() {
1468		t.Error("childCmd should have had the normalization function of rootCmd")
1469	}
1470}
1471
1472// Related to https://github.com/spf13/cobra/issues/521.
1473func TestNormPassedOnLocal(t *testing.T) {
1474	toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
1475		return pflag.NormalizedName(strings.ToUpper(name))
1476	}
1477
1478	c := &Command{}
1479	c.Flags().Bool("flagname", true, "this is a dummy flag")
1480	c.SetGlobalNormalizationFunc(toUpper)
1481	if c.LocalFlags().Lookup("flagname") != c.LocalFlags().Lookup("FLAGNAME") {
1482		t.Error("Normalization function should be passed on to Local flag set")
1483	}
1484}
1485
1486// Related to https://github.com/spf13/cobra/issues/521.
1487func TestNormPassedOnInherited(t *testing.T) {
1488	toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
1489		return pflag.NormalizedName(strings.ToUpper(name))
1490	}
1491
1492	c := &Command{}
1493	c.SetGlobalNormalizationFunc(toUpper)
1494
1495	child1 := &Command{}
1496	c.AddCommand(child1)
1497
1498	c.PersistentFlags().Bool("flagname", true, "")
1499
1500	child2 := &Command{}
1501	c.AddCommand(child2)
1502
1503	inherited := child1.InheritedFlags()
1504	if inherited.Lookup("flagname") == nil || inherited.Lookup("flagname") != inherited.Lookup("FLAGNAME") {
1505		t.Error("Normalization function should be passed on to inherited flag set in command added before flag")
1506	}
1507
1508	inherited = child2.InheritedFlags()
1509	if inherited.Lookup("flagname") == nil || inherited.Lookup("flagname") != inherited.Lookup("FLAGNAME") {
1510		t.Error("Normalization function should be passed on to inherited flag set in command added after flag")
1511	}
1512}
1513
1514// Related to https://github.com/spf13/cobra/issues/521.
1515func TestConsistentNormalizedName(t *testing.T) {
1516	toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
1517		return pflag.NormalizedName(strings.ToUpper(name))
1518	}
1519	n := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
1520		return pflag.NormalizedName(name)
1521	}
1522
1523	c := &Command{}
1524	c.Flags().Bool("flagname", true, "")
1525	c.SetGlobalNormalizationFunc(toUpper)
1526	c.SetGlobalNormalizationFunc(n)
1527
1528	if c.LocalFlags().Lookup("flagname") == c.LocalFlags().Lookup("FLAGNAME") {
1529		t.Error("Normalizing flag names should not result in duplicate flags")
1530	}
1531}
1532
1533func TestFlagOnPflagCommandLine(t *testing.T) {
1534	flagName := "flagOnCommandLine"
1535	pflag.String(flagName, "", "about my flag")
1536
1537	c := &Command{Use: "c", Run: emptyRun}
1538	c.AddCommand(&Command{Use: "child", Run: emptyRun})
1539
1540	output, _ := executeCommand(c, "--help")
1541	checkStringContains(t, output, flagName)
1542
1543	resetCommandLineFlagSet()
1544}
1545
1546// TestHiddenCommandExecutes checks,
1547// if hidden commands run as intended.
1548func TestHiddenCommandExecutes(t *testing.T) {
1549	executed := false
1550	c := &Command{
1551		Use:    "c",
1552		Hidden: true,
1553		Run:    func(*Command, []string) { executed = true },
1554	}
1555
1556	output, err := executeCommand(c)
1557	if output != "" {
1558		t.Errorf("Unexpected output: %v", output)
1559	}
1560	if err != nil {
1561		t.Errorf("Unexpected error: %v", err)
1562	}
1563
1564	if !executed {
1565		t.Error("Hidden command should have been executed")
1566	}
1567}
1568
1569// test to ensure hidden commands do not show up in usage/help text
1570func TestHiddenCommandIsHidden(t *testing.T) {
1571	c := &Command{Use: "c", Hidden: true, Run: emptyRun}
1572	if c.IsAvailableCommand() {
1573		t.Errorf("Hidden command should be unavailable")
1574	}
1575}
1576
1577func TestCommandsAreSorted(t *testing.T) {
1578	EnableCommandSorting = true
1579
1580	originalNames := []string{"middle", "zlast", "afirst"}
1581	expectedNames := []string{"afirst", "middle", "zlast"}
1582
1583	var rootCmd = &Command{Use: "root"}
1584
1585	for _, name := range originalNames {
1586		rootCmd.AddCommand(&Command{Use: name})
1587	}
1588
1589	for i, c := range rootCmd.Commands() {
1590		got := c.Name()
1591		if expectedNames[i] != got {
1592			t.Errorf("Expected: %s, got: %s", expectedNames[i], got)
1593		}
1594	}
1595
1596	EnableCommandSorting = true
1597}
1598
1599func TestEnableCommandSortingIsDisabled(t *testing.T) {
1600	EnableCommandSorting = false
1601
1602	originalNames := []string{"middle", "zlast", "afirst"}
1603
1604	var rootCmd = &Command{Use: "root"}
1605
1606	for _, name := range originalNames {
1607		rootCmd.AddCommand(&Command{Use: name})
1608	}
1609
1610	for i, c := range rootCmd.Commands() {
1611		got := c.Name()
1612		if originalNames[i] != got {
1613			t.Errorf("expected: %s, got: %s", originalNames[i], got)
1614		}
1615	}
1616
1617	EnableCommandSorting = true
1618}
1619
1620func TestSetOutput(t *testing.T) {
1621	c := &Command{}
1622	c.SetOutput(nil)
1623	if out := c.OutOrStdout(); out != os.Stdout {
1624		t.Errorf("Expected setting output to nil to revert back to stdout")
1625	}
1626}
1627
1628func TestSetOut(t *testing.T) {
1629	c := &Command{}
1630	c.SetOut(nil)
1631	if out := c.OutOrStdout(); out != os.Stdout {
1632		t.Errorf("Expected setting output to nil to revert back to stdout")
1633	}
1634}
1635
1636func TestSetErr(t *testing.T) {
1637	c := &Command{}
1638	c.SetErr(nil)
1639	if out := c.ErrOrStderr(); out != os.Stderr {
1640		t.Errorf("Expected setting error to nil to revert back to stderr")
1641	}
1642}
1643
1644func TestSetIn(t *testing.T) {
1645	c := &Command{}
1646	c.SetIn(nil)
1647	if out := c.InOrStdin(); out != os.Stdin {
1648		t.Errorf("Expected setting input to nil to revert back to stdin")
1649	}
1650}
1651
1652func TestUsageStringRedirected(t *testing.T) {
1653	c := &Command{}
1654
1655	c.usageFunc = func(cmd *Command) error {
1656		cmd.Print("[stdout1]")
1657		cmd.PrintErr("[stderr2]")
1658		cmd.Print("[stdout3]")
1659		return nil
1660	}
1661
1662	expected := "[stdout1][stderr2][stdout3]"
1663	if got := c.UsageString(); got != expected {
1664		t.Errorf("Expected usage string to consider both stdout and stderr")
1665	}
1666}
1667
1668func TestCommandPrintRedirection(t *testing.T) {
1669	errBuff, outBuff := bytes.NewBuffer(nil), bytes.NewBuffer(nil)
1670	root := &Command{
1671		Run: func(cmd *Command, args []string) {
1672
1673			cmd.PrintErr("PrintErr")
1674			cmd.PrintErrln("PrintErr", "line")
1675			cmd.PrintErrf("PrintEr%s", "r")
1676
1677			cmd.Print("Print")
1678			cmd.Println("Print", "line")
1679			cmd.Printf("Prin%s", "t")
1680		},
1681	}
1682
1683	root.SetErr(errBuff)
1684	root.SetOut(outBuff)
1685
1686	if err := root.Execute(); err != nil {
1687		t.Error(err)
1688	}
1689
1690	gotErrBytes, err := ioutil.ReadAll(errBuff)
1691	if err != nil {
1692		t.Error(err)
1693	}
1694
1695	gotOutBytes, err := ioutil.ReadAll(outBuff)
1696	if err != nil {
1697		t.Error(err)
1698	}
1699
1700	if wantErr := []byte("PrintErrPrintErr line\nPrintErr"); !bytes.Equal(gotErrBytes, wantErr) {
1701		t.Errorf("got: '%s' want: '%s'", gotErrBytes, wantErr)
1702	}
1703
1704	if wantOut := []byte("PrintPrint line\nPrint"); !bytes.Equal(gotOutBytes, wantOut) {
1705		t.Errorf("got: '%s' want: '%s'", gotOutBytes, wantOut)
1706	}
1707}
1708
1709func TestFlagErrorFunc(t *testing.T) {
1710	c := &Command{Use: "c", Run: emptyRun}
1711
1712	expectedFmt := "This is expected: %v"
1713	c.SetFlagErrorFunc(func(_ *Command, err error) error {
1714		return fmt.Errorf(expectedFmt, err)
1715	})
1716
1717	_, err := executeCommand(c, "--unknown-flag")
1718
1719	got := err.Error()
1720	expected := fmt.Sprintf(expectedFmt, "unknown flag: --unknown-flag")
1721	if got != expected {
1722		t.Errorf("Expected %v, got %v", expected, got)
1723	}
1724}
1725
1726// TestSortedFlags checks,
1727// if cmd.LocalFlags() is unsorted when cmd.Flags().SortFlags set to false.
1728// Related to https://github.com/spf13/cobra/issues/404.
1729func TestSortedFlags(t *testing.T) {
1730	c := &Command{}
1731	c.Flags().SortFlags = false
1732	names := []string{"C", "B", "A", "D"}
1733	for _, name := range names {
1734		c.Flags().Bool(name, false, "")
1735	}
1736
1737	i := 0
1738	c.LocalFlags().VisitAll(func(f *pflag.Flag) {
1739		if i == len(names) {
1740			return
1741		}
1742		if stringInSlice(f.Name, names) {
1743			if names[i] != f.Name {
1744				t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name)
1745			}
1746			i++
1747		}
1748	})
1749}
1750
1751// TestMergeCommandLineToFlags checks,
1752// if pflag.CommandLine is correctly merged to c.Flags() after first call
1753// of c.mergePersistentFlags.
1754// Related to https://github.com/spf13/cobra/issues/443.
1755func TestMergeCommandLineToFlags(t *testing.T) {
1756	pflag.Bool("boolflag", false, "")
1757	c := &Command{Use: "c", Run: emptyRun}
1758	c.mergePersistentFlags()
1759	if c.Flags().Lookup("boolflag") == nil {
1760		t.Fatal("Expecting to have flag from CommandLine in c.Flags()")
1761	}
1762
1763	resetCommandLineFlagSet()
1764}
1765
1766// TestUseDeprecatedFlags checks,
1767// if cobra.Execute() prints a message, if a deprecated flag is used.
1768// Related to https://github.com/spf13/cobra/issues/463.
1769func TestUseDeprecatedFlags(t *testing.T) {
1770	c := &Command{Use: "c", Run: emptyRun}
1771	c.Flags().BoolP("deprecated", "d", false, "deprecated flag")
1772	assertNoErr(t, c.Flags().MarkDeprecated("deprecated", "This flag is deprecated"))
1773
1774	output, err := executeCommand(c, "c", "-d")
1775	if err != nil {
1776		t.Error("Unexpected error:", err)
1777	}
1778	checkStringContains(t, output, "This flag is deprecated")
1779}
1780
1781func TestTraverseWithParentFlags(t *testing.T) {
1782	rootCmd := &Command{Use: "root", TraverseChildren: true}
1783	rootCmd.Flags().String("str", "", "")
1784	rootCmd.Flags().BoolP("bool", "b", false, "")
1785
1786	childCmd := &Command{Use: "child"}
1787	childCmd.Flags().Int("int", -1, "")
1788
1789	rootCmd.AddCommand(childCmd)
1790
1791	c, args, err := rootCmd.Traverse([]string{"-b", "--str", "ok", "child", "--int"})
1792	if err != nil {
1793		t.Errorf("Unexpected error: %v", err)
1794	}
1795	if len(args) != 1 && args[0] != "--add" {
1796		t.Errorf("Wrong args: %v", args)
1797	}
1798	if c.Name() != childCmd.Name() {
1799		t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name())
1800	}
1801}
1802
1803func TestTraverseNoParentFlags(t *testing.T) {
1804	rootCmd := &Command{Use: "root", TraverseChildren: true}
1805	rootCmd.Flags().String("foo", "", "foo things")
1806
1807	childCmd := &Command{Use: "child"}
1808	childCmd.Flags().String("str", "", "")
1809	rootCmd.AddCommand(childCmd)
1810
1811	c, args, err := rootCmd.Traverse([]string{"child"})
1812	if err != nil {
1813		t.Errorf("Unexpected error: %v", err)
1814	}
1815	if len(args) != 0 {
1816		t.Errorf("Wrong args %v", args)
1817	}
1818	if c.Name() != childCmd.Name() {
1819		t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name())
1820	}
1821}
1822
1823func TestTraverseWithBadParentFlags(t *testing.T) {
1824	rootCmd := &Command{Use: "root", TraverseChildren: true}
1825
1826	childCmd := &Command{Use: "child"}
1827	childCmd.Flags().String("str", "", "")
1828	rootCmd.AddCommand(childCmd)
1829
1830	expected := "unknown flag: --str"
1831
1832	c, _, err := rootCmd.Traverse([]string{"--str", "ok", "child"})
1833	if err == nil || !strings.Contains(err.Error(), expected) {
1834		t.Errorf("Expected error, %q, got %q", expected, err)
1835	}
1836	if c != nil {
1837		t.Errorf("Expected nil command")
1838	}
1839}
1840
1841func TestTraverseWithBadChildFlag(t *testing.T) {
1842	rootCmd := &Command{Use: "root", TraverseChildren: true}
1843	rootCmd.Flags().String("str", "", "")
1844
1845	childCmd := &Command{Use: "child"}
1846	rootCmd.AddCommand(childCmd)
1847
1848	// Expect no error because the last commands args shouldn't be parsed in
1849	// Traverse.
1850	c, args, err := rootCmd.Traverse([]string{"child", "--str"})
1851	if err != nil {
1852		t.Errorf("Unexpected error: %v", err)
1853	}
1854	if len(args) != 1 && args[0] != "--str" {
1855		t.Errorf("Wrong args: %v", args)
1856	}
1857	if c.Name() != childCmd.Name() {
1858		t.Errorf("Expected command %q, got: %q", childCmd.Name(), c.Name())
1859	}
1860}
1861
1862func TestTraverseWithTwoSubcommands(t *testing.T) {
1863	rootCmd := &Command{Use: "root", TraverseChildren: true}
1864
1865	subCmd := &Command{Use: "sub", TraverseChildren: true}
1866	rootCmd.AddCommand(subCmd)
1867
1868	subsubCmd := &Command{
1869		Use: "subsub",
1870	}
1871	subCmd.AddCommand(subsubCmd)
1872
1873	c, _, err := rootCmd.Traverse([]string{"sub", "subsub"})
1874	if err != nil {
1875		t.Fatalf("Unexpected error: %v", err)
1876	}
1877	if c.Name() != subsubCmd.Name() {
1878		t.Fatalf("Expected command: %q, got %q", subsubCmd.Name(), c.Name())
1879	}
1880}
1881
1882// TestUpdateName checks if c.Name() updates on changed c.Use.
1883// Related to https://github.com/spf13/cobra/pull/422#discussion_r143918343.
1884func TestUpdateName(t *testing.T) {
1885	c := &Command{Use: "name xyz"}
1886	originalName := c.Name()
1887
1888	c.Use = "changedName abc"
1889	if originalName == c.Name() || c.Name() != "changedName" {
1890		t.Error("c.Name() should be updated on changed c.Use")
1891	}
1892}
1893
1894type calledAsTestcase struct {
1895	args []string
1896	call string
1897	want string
1898	epm  bool
1899}
1900
1901func (tc *calledAsTestcase) test(t *testing.T) {
1902	defer func(ov bool) { EnablePrefixMatching = ov }(EnablePrefixMatching)
1903	EnablePrefixMatching = tc.epm
1904
1905	var called *Command
1906	run := func(c *Command, _ []string) { t.Logf("called: %q", c.Name()); called = c }
1907
1908	parent := &Command{Use: "parent", Run: run}
1909	child1 := &Command{Use: "child1", Run: run, Aliases: []string{"this"}}
1910	child2 := &Command{Use: "child2", Run: run, Aliases: []string{"that"}}
1911
1912	parent.AddCommand(child1)
1913	parent.AddCommand(child2)
1914	parent.SetArgs(tc.args)
1915
1916	output := new(bytes.Buffer)
1917	parent.SetOut(output)
1918	parent.SetErr(output)
1919
1920	_ = parent.Execute()
1921
1922	if called == nil {
1923		if tc.call != "" {
1924			t.Errorf("missing expected call to command: %s", tc.call)
1925		}
1926		return
1927	}
1928
1929	if called.Name() != tc.call {
1930		t.Errorf("called command == %q; Wanted %q", called.Name(), tc.call)
1931	} else if got := called.CalledAs(); got != tc.want {
1932		t.Errorf("%s.CalledAs() == %q; Wanted: %q", tc.call, got, tc.want)
1933	}
1934}
1935
1936func TestCalledAs(t *testing.T) {
1937	tests := map[string]calledAsTestcase{
1938		"find/no-args":            {nil, "parent", "parent", false},
1939		"find/real-name":          {[]string{"child1"}, "child1", "child1", false},
1940		"find/full-alias":         {[]string{"that"}, "child2", "that", false},
1941		"find/part-no-prefix":     {[]string{"thi"}, "", "", false},
1942		"find/part-alias":         {[]string{"thi"}, "child1", "this", true},
1943		"find/conflict":           {[]string{"th"}, "", "", true},
1944		"traverse/no-args":        {nil, "parent", "parent", false},
1945		"traverse/real-name":      {[]string{"child1"}, "child1", "child1", false},
1946		"traverse/full-alias":     {[]string{"that"}, "child2", "that", false},
1947		"traverse/part-no-prefix": {[]string{"thi"}, "", "", false},
1948		"traverse/part-alias":     {[]string{"thi"}, "child1", "this", true},
1949		"traverse/conflict":       {[]string{"th"}, "", "", true},
1950	}
1951
1952	for name, tc := range tests {
1953		t.Run(name, tc.test)
1954	}
1955}
1956
1957func TestFParseErrWhitelistBackwardCompatibility(t *testing.T) {
1958	c := &Command{Use: "c", Run: emptyRun}
1959	c.Flags().BoolP("boola", "a", false, "a boolean flag")
1960
1961	output, err := executeCommand(c, "c", "-a", "--unknown", "flag")
1962	if err == nil {
1963		t.Error("expected unknown flag error")
1964	}
1965	checkStringContains(t, output, "unknown flag: --unknown")
1966}
1967
1968func TestFParseErrWhitelistSameCommand(t *testing.T) {
1969	c := &Command{
1970		Use: "c",
1971		Run: emptyRun,
1972		FParseErrWhitelist: FParseErrWhitelist{
1973			UnknownFlags: true,
1974		},
1975	}
1976	c.Flags().BoolP("boola", "a", false, "a boolean flag")
1977
1978	_, err := executeCommand(c, "c", "-a", "--unknown", "flag")
1979	if err != nil {
1980		t.Error("unexpected error: ", err)
1981	}
1982}
1983
1984func TestFParseErrWhitelistParentCommand(t *testing.T) {
1985	root := &Command{
1986		Use: "root",
1987		Run: emptyRun,
1988		FParseErrWhitelist: FParseErrWhitelist{
1989			UnknownFlags: true,
1990		},
1991	}
1992
1993	c := &Command{
1994		Use: "child",
1995		Run: emptyRun,
1996	}
1997	c.Flags().BoolP("boola", "a", false, "a boolean flag")
1998
1999	root.AddCommand(c)
2000
2001	output, err := executeCommand(root, "child", "-a", "--unknown", "flag")
2002	if err == nil {
2003		t.Error("expected unknown flag error")
2004	}
2005	checkStringContains(t, output, "unknown flag: --unknown")
2006}
2007
2008func TestFParseErrWhitelistChildCommand(t *testing.T) {
2009	root := &Command{
2010		Use: "root",
2011		Run: emptyRun,
2012	}
2013
2014	c := &Command{
2015		Use: "child",
2016		Run: emptyRun,
2017		FParseErrWhitelist: FParseErrWhitelist{
2018			UnknownFlags: true,
2019		},
2020	}
2021	c.Flags().BoolP("boola", "a", false, "a boolean flag")
2022
2023	root.AddCommand(c)
2024
2025	_, err := executeCommand(root, "child", "-a", "--unknown", "flag")
2026	if err != nil {
2027		t.Error("unexpected error: ", err.Error())
2028	}
2029}
2030
2031func TestFParseErrWhitelistSiblingCommand(t *testing.T) {
2032	root := &Command{
2033		Use: "root",
2034		Run: emptyRun,
2035	}
2036
2037	c := &Command{
2038		Use: "child",
2039		Run: emptyRun,
2040		FParseErrWhitelist: FParseErrWhitelist{
2041			UnknownFlags: true,
2042		},
2043	}
2044	c.Flags().BoolP("boola", "a", false, "a boolean flag")
2045
2046	s := &Command{
2047		Use: "sibling",
2048		Run: emptyRun,
2049	}
2050	s.Flags().BoolP("boolb", "b", false, "a boolean flag")
2051
2052	root.AddCommand(c)
2053	root.AddCommand(s)
2054
2055	output, err := executeCommand(root, "sibling", "-b", "--unknown", "flag")
2056	if err == nil {
2057		t.Error("expected unknown flag error")
2058	}
2059	checkStringContains(t, output, "unknown flag: --unknown")
2060}
2061