1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package cobra
16
17 import (
18 "bytes"
19 "context"
20 "fmt"
21 "io/ioutil"
22 "os"
23 "reflect"
24 "strings"
25 "testing"
26
27 "github.com/spf13/pflag"
28 )
29
30 func emptyRun(*Command, []string) {}
31
32 func executeCommand(root *Command, args ...string) (output string, err error) {
33 _, output, err = executeCommandC(root, args...)
34 return output, err
35 }
36
37 func executeCommandWithContext(ctx context.Context, root *Command, args ...string) (output string, err error) {
38 buf := new(bytes.Buffer)
39 root.SetOut(buf)
40 root.SetErr(buf)
41 root.SetArgs(args)
42
43 err = root.ExecuteContext(ctx)
44
45 return buf.String(), err
46 }
47
48 func executeCommandC(root *Command, args ...string) (c *Command, output string, err error) {
49 buf := new(bytes.Buffer)
50 root.SetOut(buf)
51 root.SetErr(buf)
52 root.SetArgs(args)
53
54 c, err = root.ExecuteC()
55
56 return c, buf.String(), err
57 }
58
59 func executeCommandWithContextC(ctx context.Context, root *Command, args ...string) (c *Command, output string, err error) {
60 buf := new(bytes.Buffer)
61 root.SetOut(buf)
62 root.SetErr(buf)
63 root.SetArgs(args)
64
65 c, err = root.ExecuteContextC(ctx)
66
67 return c, buf.String(), err
68 }
69
70 func resetCommandLineFlagSet() {
71 pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
72 }
73
74 func checkStringContains(t *testing.T, got, expected string) {
75 if !strings.Contains(got, expected) {
76 t.Errorf("Expected to contain: \n %v\nGot:\n %v\n", expected, got)
77 }
78 }
79
80 func checkStringOmits(t *testing.T, got, expected string) {
81 if strings.Contains(got, expected) {
82 t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got)
83 }
84 }
85
86 const onetwo = "one two"
87
88 func TestSingleCommand(t *testing.T) {
89 var rootCmdArgs []string
90 rootCmd := &Command{
91 Use: "root",
92 Args: ExactArgs(2),
93 Run: func(_ *Command, args []string) { rootCmdArgs = args },
94 }
95 aCmd := &Command{Use: "a", Args: NoArgs, Run: emptyRun}
96 bCmd := &Command{Use: "b", Args: NoArgs, Run: emptyRun}
97 rootCmd.AddCommand(aCmd, bCmd)
98
99 output, err := executeCommand(rootCmd, "one", "two")
100 if output != "" {
101 t.Errorf("Unexpected output: %v", output)
102 }
103 if err != nil {
104 t.Errorf("Unexpected error: %v", err)
105 }
106
107 got := strings.Join(rootCmdArgs, " ")
108 if got != onetwo {
109 t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
110 }
111 }
112
113 func TestChildCommand(t *testing.T) {
114 var child1CmdArgs []string
115 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
116 child1Cmd := &Command{
117 Use: "child1",
118 Args: ExactArgs(2),
119 Run: func(_ *Command, args []string) { child1CmdArgs = args },
120 }
121 child2Cmd := &Command{Use: "child2", Args: NoArgs, Run: emptyRun}
122 rootCmd.AddCommand(child1Cmd, child2Cmd)
123
124 output, err := executeCommand(rootCmd, "child1", "one", "two")
125 if output != "" {
126 t.Errorf("Unexpected output: %v", output)
127 }
128 if err != nil {
129 t.Errorf("Unexpected error: %v", err)
130 }
131
132 got := strings.Join(child1CmdArgs, " ")
133 if got != onetwo {
134 t.Errorf("child1CmdArgs expected: %q, got: %q", onetwo, got)
135 }
136 }
137
138 func TestCallCommandWithoutSubcommands(t *testing.T) {
139 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
140 _, err := executeCommand(rootCmd)
141 if err != nil {
142 t.Errorf("Calling command without subcommands should not have error: %v", err)
143 }
144 }
145
146 func TestRootExecuteUnknownCommand(t *testing.T) {
147 rootCmd := &Command{Use: "root", Run: emptyRun}
148 rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
149
150 output, _ := executeCommand(rootCmd, "unknown")
151
152 expected := "Error: unknown command \"unknown\" for \"root\"\nRun 'root --help' for usage.\n"
153
154 if output != expected {
155 t.Errorf("Expected:\n %q\nGot:\n %q\n", expected, output)
156 }
157 }
158
159 func TestSubcommandExecuteC(t *testing.T) {
160 rootCmd := &Command{Use: "root", Run: emptyRun}
161 childCmd := &Command{Use: "child", Run: emptyRun}
162 rootCmd.AddCommand(childCmd)
163
164 c, output, err := executeCommandC(rootCmd, "child")
165 if output != "" {
166 t.Errorf("Unexpected output: %v", output)
167 }
168 if err != nil {
169 t.Errorf("Unexpected error: %v", err)
170 }
171
172 if c.Name() != "child" {
173 t.Errorf(`invalid command returned from ExecuteC: expected "child"', got: %q`, c.Name())
174 }
175 }
176
177 func TestExecuteContext(t *testing.T) {
178 ctx := context.TODO()
179
180 ctxRun := func(cmd *Command, args []string) {
181 if cmd.Context() != ctx {
182 t.Errorf("Command %q must have context when called with ExecuteContext", cmd.Use)
183 }
184 }
185
186 rootCmd := &Command{Use: "root", Run: ctxRun, PreRun: ctxRun}
187 childCmd := &Command{Use: "child", Run: ctxRun, PreRun: ctxRun}
188 granchildCmd := &Command{Use: "grandchild", Run: ctxRun, PreRun: ctxRun}
189
190 childCmd.AddCommand(granchildCmd)
191 rootCmd.AddCommand(childCmd)
192
193 if _, err := executeCommandWithContext(ctx, rootCmd, ""); err != nil {
194 t.Errorf("Root command must not fail: %+v", err)
195 }
196
197 if _, err := executeCommandWithContext(ctx, rootCmd, "child"); err != nil {
198 t.Errorf("Subcommand must not fail: %+v", err)
199 }
200
201 if _, err := executeCommandWithContext(ctx, rootCmd, "child", "grandchild"); err != nil {
202 t.Errorf("Command child must not fail: %+v", err)
203 }
204 }
205
206 func TestExecuteContextC(t *testing.T) {
207 ctx := context.TODO()
208
209 ctxRun := func(cmd *Command, args []string) {
210 if cmd.Context() != ctx {
211 t.Errorf("Command %q must have context when called with ExecuteContext", cmd.Use)
212 }
213 }
214
215 rootCmd := &Command{Use: "root", Run: ctxRun, PreRun: ctxRun}
216 childCmd := &Command{Use: "child", Run: ctxRun, PreRun: ctxRun}
217 granchildCmd := &Command{Use: "grandchild", Run: ctxRun, PreRun: ctxRun}
218
219 childCmd.AddCommand(granchildCmd)
220 rootCmd.AddCommand(childCmd)
221
222 if _, _, err := executeCommandWithContextC(ctx, rootCmd, ""); err != nil {
223 t.Errorf("Root command must not fail: %+v", err)
224 }
225
226 if _, _, err := executeCommandWithContextC(ctx, rootCmd, "child"); err != nil {
227 t.Errorf("Subcommand must not fail: %+v", err)
228 }
229
230 if _, _, err := executeCommandWithContextC(ctx, rootCmd, "child", "grandchild"); err != nil {
231 t.Errorf("Command child must not fail: %+v", err)
232 }
233 }
234
235 func TestExecute_NoContext(t *testing.T) {
236 run := func(cmd *Command, args []string) {
237 if cmd.Context() != context.Background() {
238 t.Errorf("Command %s must have background context", cmd.Use)
239 }
240 }
241
242 rootCmd := &Command{Use: "root", Run: run, PreRun: run}
243 childCmd := &Command{Use: "child", Run: run, PreRun: run}
244 granchildCmd := &Command{Use: "grandchild", Run: run, PreRun: run}
245
246 childCmd.AddCommand(granchildCmd)
247 rootCmd.AddCommand(childCmd)
248
249 if _, err := executeCommand(rootCmd, ""); err != nil {
250 t.Errorf("Root command must not fail: %+v", err)
251 }
252
253 if _, err := executeCommand(rootCmd, "child"); err != nil {
254 t.Errorf("Subcommand must not fail: %+v", err)
255 }
256
257 if _, err := executeCommand(rootCmd, "child", "grandchild"); err != nil {
258 t.Errorf("Command child must not fail: %+v", err)
259 }
260 }
261
262 func TestRootUnknownCommandSilenced(t *testing.T) {
263 rootCmd := &Command{Use: "root", Run: emptyRun}
264 rootCmd.SilenceErrors = true
265 rootCmd.SilenceUsage = true
266 rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
267
268 output, _ := executeCommand(rootCmd, "unknown")
269 if output != "" {
270 t.Errorf("Expected blank output, because of silenced usage.\nGot:\n %q\n", output)
271 }
272 }
273
274 func TestCommandAlias(t *testing.T) {
275 var timesCmdArgs []string
276 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
277 echoCmd := &Command{
278 Use: "echo",
279 Aliases: []string{"say", "tell"},
280 Args: NoArgs,
281 Run: emptyRun,
282 }
283 timesCmd := &Command{
284 Use: "times",
285 Args: ExactArgs(2),
286 Run: func(_ *Command, args []string) { timesCmdArgs = args },
287 }
288 echoCmd.AddCommand(timesCmd)
289 rootCmd.AddCommand(echoCmd)
290
291 output, err := executeCommand(rootCmd, "tell", "times", "one", "two")
292 if output != "" {
293 t.Errorf("Unexpected output: %v", output)
294 }
295 if err != nil {
296 t.Errorf("Unexpected error: %v", err)
297 }
298
299 got := strings.Join(timesCmdArgs, " ")
300 if got != onetwo {
301 t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
302 }
303 }
304
305 func TestEnablePrefixMatching(t *testing.T) {
306 EnablePrefixMatching = true
307
308 var aCmdArgs []string
309 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
310 aCmd := &Command{
311 Use: "aCmd",
312 Args: ExactArgs(2),
313 Run: func(_ *Command, args []string) { aCmdArgs = args },
314 }
315 bCmd := &Command{Use: "bCmd", Args: NoArgs, Run: emptyRun}
316 rootCmd.AddCommand(aCmd, bCmd)
317
318 output, err := executeCommand(rootCmd, "a", "one", "two")
319 if output != "" {
320 t.Errorf("Unexpected output: %v", output)
321 }
322 if err != nil {
323 t.Errorf("Unexpected error: %v", err)
324 }
325
326 got := strings.Join(aCmdArgs, " ")
327 if got != onetwo {
328 t.Errorf("aCmdArgs expected: %q, got: %q", onetwo, got)
329 }
330
331 EnablePrefixMatching = defaultPrefixMatching
332 }
333
334 func TestAliasPrefixMatching(t *testing.T) {
335 EnablePrefixMatching = true
336
337 var timesCmdArgs []string
338 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
339 echoCmd := &Command{
340 Use: "echo",
341 Aliases: []string{"say", "tell"},
342 Args: NoArgs,
343 Run: emptyRun,
344 }
345 timesCmd := &Command{
346 Use: "times",
347 Args: ExactArgs(2),
348 Run: func(_ *Command, args []string) { timesCmdArgs = args },
349 }
350 echoCmd.AddCommand(timesCmd)
351 rootCmd.AddCommand(echoCmd)
352
353 output, err := executeCommand(rootCmd, "sa", "times", "one", "two")
354 if output != "" {
355 t.Errorf("Unexpected output: %v", output)
356 }
357 if err != nil {
358 t.Errorf("Unexpected error: %v", err)
359 }
360
361 got := strings.Join(timesCmdArgs, " ")
362 if got != onetwo {
363 t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
364 }
365
366 EnablePrefixMatching = defaultPrefixMatching
367 }
368
369
370
371
372 func TestPlugin(t *testing.T) {
373 rootCmd := &Command{
374 Use: "plugin",
375 Args: NoArgs,
376 Annotations: map[string]string{
377 CommandDisplayNameAnnotation: "kubectl plugin",
378 },
379 }
380
381 subCmd := &Command{Use: "sub [flags]", Args: NoArgs, Run: emptyRun}
382 rootCmd.AddCommand(subCmd)
383
384 rootHelp, err := executeCommand(rootCmd, "-h")
385 if err != nil {
386 t.Errorf("Unexpected error: %v", err)
387 }
388
389 checkStringContains(t, rootHelp, "kubectl plugin [command]")
390
391 childHelp, err := executeCommand(rootCmd, "sub", "-h")
392 if err != nil {
393 t.Errorf("Unexpected error: %v", err)
394 }
395
396 checkStringContains(t, childHelp, "kubectl plugin sub [flags]")
397 }
398
399
400
401
402 func TestChildSameName(t *testing.T) {
403 var fooCmdArgs []string
404 rootCmd := &Command{Use: "foo", Args: NoArgs, Run: emptyRun}
405 fooCmd := &Command{
406 Use: "foo",
407 Args: ExactArgs(2),
408 Run: func(_ *Command, args []string) { fooCmdArgs = args },
409 }
410 barCmd := &Command{Use: "bar", Args: NoArgs, Run: emptyRun}
411 rootCmd.AddCommand(fooCmd, barCmd)
412
413 output, err := executeCommand(rootCmd, "foo", "one", "two")
414 if output != "" {
415 t.Errorf("Unexpected output: %v", output)
416 }
417 if err != nil {
418 t.Errorf("Unexpected error: %v", err)
419 }
420
421 got := strings.Join(fooCmdArgs, " ")
422 if got != onetwo {
423 t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
424 }
425 }
426
427
428
429
430 func TestGrandChildSameName(t *testing.T) {
431 var fooCmdArgs []string
432 rootCmd := &Command{Use: "foo", Args: NoArgs, Run: emptyRun}
433 barCmd := &Command{Use: "bar", Args: NoArgs, Run: emptyRun}
434 fooCmd := &Command{
435 Use: "foo",
436 Args: ExactArgs(2),
437 Run: func(_ *Command, args []string) { fooCmdArgs = args },
438 }
439 barCmd.AddCommand(fooCmd)
440 rootCmd.AddCommand(barCmd)
441
442 output, err := executeCommand(rootCmd, "bar", "foo", "one", "two")
443 if output != "" {
444 t.Errorf("Unexpected output: %v", output)
445 }
446 if err != nil {
447 t.Errorf("Unexpected error: %v", err)
448 }
449
450 got := strings.Join(fooCmdArgs, " ")
451 if got != onetwo {
452 t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
453 }
454 }
455
456 func TestFlagLong(t *testing.T) {
457 var cArgs []string
458 c := &Command{
459 Use: "c",
460 Args: ArbitraryArgs,
461 Run: func(_ *Command, args []string) { cArgs = args },
462 }
463
464 var intFlagValue int
465 var stringFlagValue string
466 c.Flags().IntVar(&intFlagValue, "intf", -1, "")
467 c.Flags().StringVar(&stringFlagValue, "sf", "", "")
468
469 output, err := executeCommand(c, "--intf=7", "--sf=abc", "one", "--", "two")
470 if output != "" {
471 t.Errorf("Unexpected output: %v", output)
472 }
473 if err != nil {
474 t.Errorf("Unexpected error: %v", err)
475 }
476
477 if c.ArgsLenAtDash() != 1 {
478 t.Errorf("Expected ArgsLenAtDash: %v but got %v", 1, c.ArgsLenAtDash())
479 }
480 if intFlagValue != 7 {
481 t.Errorf("Expected intFlagValue: %v, got %v", 7, intFlagValue)
482 }
483 if stringFlagValue != "abc" {
484 t.Errorf("Expected stringFlagValue: %q, got %q", "abc", stringFlagValue)
485 }
486
487 got := strings.Join(cArgs, " ")
488 if got != onetwo {
489 t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
490 }
491 }
492
493 func TestFlagShort(t *testing.T) {
494 var cArgs []string
495 c := &Command{
496 Use: "c",
497 Args: ArbitraryArgs,
498 Run: func(_ *Command, args []string) { cArgs = args },
499 }
500
501 var intFlagValue int
502 var stringFlagValue string
503 c.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
504 c.Flags().StringVarP(&stringFlagValue, "sf", "s", "", "")
505
506 output, err := executeCommand(c, "-i", "7", "-sabc", "one", "two")
507 if output != "" {
508 t.Errorf("Unexpected output: %v", output)
509 }
510 if err != nil {
511 t.Errorf("Unexpected error: %v", err)
512 }
513
514 if intFlagValue != 7 {
515 t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
516 }
517 if stringFlagValue != "abc" {
518 t.Errorf("Expected stringFlagValue: %q, got %q", "abc", stringFlagValue)
519 }
520
521 got := strings.Join(cArgs, " ")
522 if got != onetwo {
523 t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
524 }
525 }
526
527 func TestChildFlag(t *testing.T) {
528 rootCmd := &Command{Use: "root", Run: emptyRun}
529 childCmd := &Command{Use: "child", Run: emptyRun}
530 rootCmd.AddCommand(childCmd)
531
532 var intFlagValue int
533 childCmd.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
534
535 output, err := executeCommand(rootCmd, "child", "-i7")
536 if output != "" {
537 t.Errorf("Unexpected output: %v", output)
538 }
539 if err != nil {
540 t.Errorf("Unexpected error: %v", err)
541 }
542
543 if intFlagValue != 7 {
544 t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
545 }
546 }
547
548 func TestChildFlagWithParentLocalFlag(t *testing.T) {
549 rootCmd := &Command{Use: "root", Run: emptyRun}
550 childCmd := &Command{Use: "child", Run: emptyRun}
551 rootCmd.AddCommand(childCmd)
552
553 var intFlagValue int
554 rootCmd.Flags().StringP("sf", "s", "", "")
555 childCmd.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
556
557 _, err := executeCommand(rootCmd, "child", "-i7", "-sabc")
558 if err == nil {
559 t.Errorf("Invalid flag should generate error")
560 }
561
562 checkStringContains(t, err.Error(), "unknown shorthand")
563
564 if intFlagValue != 7 {
565 t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
566 }
567 }
568
569 func TestFlagInvalidInput(t *testing.T) {
570 rootCmd := &Command{Use: "root", Run: emptyRun}
571 rootCmd.Flags().IntP("intf", "i", -1, "")
572
573 _, err := executeCommand(rootCmd, "-iabc")
574 if err == nil {
575 t.Errorf("Invalid flag value should generate error")
576 }
577
578 checkStringContains(t, err.Error(), "invalid syntax")
579 }
580
581 func TestFlagBeforeCommand(t *testing.T) {
582 rootCmd := &Command{Use: "root", Run: emptyRun}
583 childCmd := &Command{Use: "child", Run: emptyRun}
584 rootCmd.AddCommand(childCmd)
585
586 var flagValue int
587 childCmd.Flags().IntVarP(&flagValue, "intf", "i", -1, "")
588
589
590 _, err := executeCommand(rootCmd, "-i7", "child")
591 if err != nil {
592 t.Errorf("Unexpected error: %v", err)
593 }
594 if flagValue != 7 {
595 t.Errorf("Expected flag value: %v, got %v", 7, flagValue)
596 }
597
598
599 _, err = executeCommand(rootCmd, "--intf=8", "child")
600 if err != nil {
601 t.Errorf("Unexpected error: %v", err)
602 }
603 if flagValue != 8 {
604 t.Errorf("Expected flag value: %v, got %v", 9, flagValue)
605 }
606 }
607
608 func TestStripFlags(t *testing.T) {
609 tests := []struct {
610 input []string
611 output []string
612 }{
613 {
614 []string{"foo", "bar"},
615 []string{"foo", "bar"},
616 },
617 {
618 []string{"foo", "--str", "-s"},
619 []string{"foo"},
620 },
621 {
622 []string{"-s", "foo", "--str", "bar"},
623 []string{},
624 },
625 {
626 []string{"-i10", "echo"},
627 []string{"echo"},
628 },
629 {
630 []string{"-i=10", "echo"},
631 []string{"echo"},
632 },
633 {
634 []string{"--int=100", "echo"},
635 []string{"echo"},
636 },
637 {
638 []string{"-ib", "echo", "-sfoo", "baz"},
639 []string{"echo", "baz"},
640 },
641 {
642 []string{"-i=baz", "bar", "-i", "foo", "blah"},
643 []string{"bar", "blah"},
644 },
645 {
646 []string{"--int=baz", "-sbar", "-i", "foo", "blah"},
647 []string{"blah"},
648 },
649 {
650 []string{"--bool", "bar", "-i", "foo", "blah"},
651 []string{"bar", "blah"},
652 },
653 {
654 []string{"-b", "bar", "-i", "foo", "blah"},
655 []string{"bar", "blah"},
656 },
657 {
658 []string{"--persist", "bar"},
659 []string{"bar"},
660 },
661 {
662 []string{"-p", "bar"},
663 []string{"bar"},
664 },
665 }
666
667 c := &Command{Use: "c", Run: emptyRun}
668 c.PersistentFlags().BoolP("persist", "p", false, "")
669 c.Flags().IntP("int", "i", -1, "")
670 c.Flags().StringP("str", "s", "", "")
671 c.Flags().BoolP("bool", "b", false, "")
672
673 for i, test := range tests {
674 got := stripFlags(test.input, c)
675 if !reflect.DeepEqual(test.output, got) {
676 t.Errorf("(%v) Expected: %v, got: %v", i, test.output, got)
677 }
678 }
679 }
680
681 func TestDisableFlagParsing(t *testing.T) {
682 var cArgs []string
683 c := &Command{
684 Use: "c",
685 DisableFlagParsing: true,
686 Run: func(_ *Command, args []string) {
687 cArgs = args
688 },
689 }
690
691 args := []string{"cmd", "-v", "-race", "-file", "foo.go"}
692 output, err := executeCommand(c, args...)
693 if output != "" {
694 t.Errorf("Unexpected output: %v", output)
695 }
696 if err != nil {
697 t.Errorf("Unexpected error: %v", err)
698 }
699
700 if !reflect.DeepEqual(args, cArgs) {
701 t.Errorf("Expected: %v, got: %v", args, cArgs)
702 }
703 }
704
705 func TestPersistentFlagsOnSameCommand(t *testing.T) {
706 var rootCmdArgs []string
707 rootCmd := &Command{
708 Use: "root",
709 Args: ArbitraryArgs,
710 Run: func(_ *Command, args []string) { rootCmdArgs = args },
711 }
712
713 var flagValue int
714 rootCmd.PersistentFlags().IntVarP(&flagValue, "intf", "i", -1, "")
715
716 output, err := executeCommand(rootCmd, "-i7", "one", "two")
717 if output != "" {
718 t.Errorf("Unexpected output: %v", output)
719 }
720 if err != nil {
721 t.Errorf("Unexpected error: %v", err)
722 }
723
724 got := strings.Join(rootCmdArgs, " ")
725 if got != onetwo {
726 t.Errorf("rootCmdArgs expected: %q, got %q", onetwo, got)
727 }
728 if flagValue != 7 {
729 t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
730 }
731 }
732
733
734
735 func TestEmptyInputs(t *testing.T) {
736 c := &Command{Use: "c", Run: emptyRun}
737
738 var flagValue int
739 c.Flags().IntVarP(&flagValue, "intf", "i", -1, "")
740
741 output, err := executeCommand(c, "", "-i7", "")
742 if output != "" {
743 t.Errorf("Unexpected output: %v", output)
744 }
745 if err != nil {
746 t.Errorf("Unexpected error: %v", err)
747 }
748
749 if flagValue != 7 {
750 t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
751 }
752 }
753
754 func TestChildFlagShadowsParentPersistentFlag(t *testing.T) {
755 parent := &Command{Use: "parent", Run: emptyRun}
756 child := &Command{Use: "child", Run: emptyRun}
757
758 parent.PersistentFlags().Bool("boolf", false, "")
759 parent.PersistentFlags().Int("intf", -1, "")
760 child.Flags().String("strf", "", "")
761 child.Flags().Int("intf", -1, "")
762
763 parent.AddCommand(child)
764
765 childInherited := child.InheritedFlags()
766 childLocal := child.LocalFlags()
767
768 if childLocal.Lookup("strf") == nil {
769 t.Error(`LocalFlags expected to contain "strf", got "nil"`)
770 }
771 if childInherited.Lookup("boolf") == nil {
772 t.Error(`InheritedFlags expected to contain "boolf", got "nil"`)
773 }
774
775 if childInherited.Lookup("intf") != nil {
776 t.Errorf(`InheritedFlags should not contain shadowed flag "intf"`)
777 }
778 if childLocal.Lookup("intf") == nil {
779 t.Error(`LocalFlags expected to contain "intf", got "nil"`)
780 }
781 }
782
783 func TestPersistentFlagsOnChild(t *testing.T) {
784 var childCmdArgs []string
785 rootCmd := &Command{Use: "root", Run: emptyRun}
786 childCmd := &Command{
787 Use: "child",
788 Args: ArbitraryArgs,
789 Run: func(_ *Command, args []string) { childCmdArgs = args },
790 }
791 rootCmd.AddCommand(childCmd)
792
793 var parentFlagValue int
794 var childFlagValue int
795 rootCmd.PersistentFlags().IntVarP(&parentFlagValue, "parentf", "p", -1, "")
796 childCmd.Flags().IntVarP(&childFlagValue, "childf", "c", -1, "")
797
798 output, err := executeCommand(rootCmd, "child", "-c7", "-p8", "one", "two")
799 if output != "" {
800 t.Errorf("Unexpected output: %v", output)
801 }
802 if err != nil {
803 t.Errorf("Unexpected error: %v", err)
804 }
805
806 got := strings.Join(childCmdArgs, " ")
807 if got != onetwo {
808 t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
809 }
810 if parentFlagValue != 8 {
811 t.Errorf("parentFlagValue expected: %v, got %v", 8, parentFlagValue)
812 }
813 if childFlagValue != 7 {
814 t.Errorf("childFlagValue expected: %v, got %v", 7, childFlagValue)
815 }
816 }
817
818 func TestRequiredFlags(t *testing.T) {
819 c := &Command{Use: "c", Run: emptyRun}
820 c.Flags().String("foo1", "", "")
821 assertNoErr(t, c.MarkFlagRequired("foo1"))
822 c.Flags().String("foo2", "", "")
823 assertNoErr(t, c.MarkFlagRequired("foo2"))
824 c.Flags().String("bar", "", "")
825
826 expected := fmt.Sprintf("required flag(s) %q, %q not set", "foo1", "foo2")
827
828 _, err := executeCommand(c)
829 got := err.Error()
830
831 if got != expected {
832 t.Errorf("Expected error: %q, got: %q", expected, got)
833 }
834 }
835
836 func TestPersistentRequiredFlags(t *testing.T) {
837 parent := &Command{Use: "parent", Run: emptyRun}
838 parent.PersistentFlags().String("foo1", "", "")
839 assertNoErr(t, parent.MarkPersistentFlagRequired("foo1"))
840 parent.PersistentFlags().String("foo2", "", "")
841 assertNoErr(t, parent.MarkPersistentFlagRequired("foo2"))
842 parent.Flags().String("foo3", "", "")
843
844 child := &Command{Use: "child", Run: emptyRun}
845 child.Flags().String("bar1", "", "")
846 assertNoErr(t, child.MarkFlagRequired("bar1"))
847 child.Flags().String("bar2", "", "")
848 assertNoErr(t, child.MarkFlagRequired("bar2"))
849 child.Flags().String("bar3", "", "")
850
851 parent.AddCommand(child)
852
853 expected := fmt.Sprintf("required flag(s) %q, %q, %q, %q not set", "bar1", "bar2", "foo1", "foo2")
854
855 _, err := executeCommand(parent, "child")
856 if err.Error() != expected {
857 t.Errorf("Expected %q, got %q", expected, err.Error())
858 }
859 }
860
861 func TestPersistentRequiredFlagsWithDisableFlagParsing(t *testing.T) {
862
863
864
865 parent := &Command{Use: "parent", Run: emptyRun}
866 parent.PersistentFlags().Bool("foo", false, "")
867 flag := parent.PersistentFlags().Lookup("foo")
868 assertNoErr(t, parent.MarkPersistentFlagRequired("foo"))
869
870 child := &Command{Use: "child", Run: emptyRun}
871 child.DisableFlagParsing = true
872
873 parent.AddCommand(child)
874
875 if _, err := executeCommand(parent, "--foo", "child"); err != nil {
876 t.Errorf("Unexpected error: %v", err)
877 }
878
879
880 flag.Changed = false
881 if _, err := executeCommand(parent, "child", "--foo"); err != nil {
882 t.Errorf("Unexpected error: %v", err)
883 }
884
885
886 flag.Changed = false
887 if _, err := executeCommand(parent, "child"); err != nil {
888 t.Errorf("Unexpected error: %v", err)
889 }
890 }
891
892 func TestInitHelpFlagMergesFlags(t *testing.T) {
893 usage := "custom flag"
894 rootCmd := &Command{Use: "root"}
895 rootCmd.PersistentFlags().Bool("help", false, "custom flag")
896 childCmd := &Command{Use: "child"}
897 rootCmd.AddCommand(childCmd)
898
899 childCmd.InitDefaultHelpFlag()
900 got := childCmd.Flags().Lookup("help").Usage
901 if got != usage {
902 t.Errorf("Expected the help flag from the root command with usage: %v\nGot the default with usage: %v", usage, got)
903 }
904 }
905
906 func TestHelpCommandExecuted(t *testing.T) {
907 rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
908 rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
909
910 output, err := executeCommand(rootCmd, "help")
911 if err != nil {
912 t.Errorf("Unexpected error: %v", err)
913 }
914
915 checkStringContains(t, output, rootCmd.Long)
916 }
917
918 func TestHelpCommandExecutedOnChild(t *testing.T) {
919 rootCmd := &Command{Use: "root", Run: emptyRun}
920 childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun}
921 rootCmd.AddCommand(childCmd)
922
923 output, err := executeCommand(rootCmd, "help", "child")
924 if err != nil {
925 t.Errorf("Unexpected error: %v", err)
926 }
927
928 checkStringContains(t, output, childCmd.Long)
929 }
930
931 func TestHelpCommandExecutedOnChildWithFlagThatShadowsParentFlag(t *testing.T) {
932 parent := &Command{Use: "parent", Run: emptyRun}
933 child := &Command{Use: "child", Run: emptyRun}
934 parent.AddCommand(child)
935
936 parent.PersistentFlags().Bool("foo", false, "parent foo usage")
937 parent.PersistentFlags().Bool("bar", false, "parent bar usage")
938 child.Flags().Bool("foo", false, "child foo usage")
939 child.Flags().Bool("baz", false, "child baz usage")
940
941 got, err := executeCommand(parent, "help", "child")
942 if err != nil {
943 t.Errorf("Unexpected error: %v", err)
944 }
945
946 expected := `Usage:
947 parent child [flags]
948
949 Flags:
950 --baz child baz usage
951 --foo child foo usage
952 -h, --help help for child
953
954 Global Flags:
955 --bar parent bar usage
956 `
957
958 if got != expected {
959 t.Errorf("Help text mismatch.\nExpected:\n%s\n\nGot:\n%s\n", expected, got)
960 }
961 }
962
963 func TestSetHelpCommand(t *testing.T) {
964 c := &Command{Use: "c", Run: emptyRun}
965 c.AddCommand(&Command{Use: "empty", Run: emptyRun})
966
967 expected := "WORKS"
968 c.SetHelpCommand(&Command{
969 Use: "help [command]",
970 Short: "Help about any command",
971 Long: `Help provides help for any command in the application.
972 Simply type ` + c.Name() + ` help [path to command] for full details.`,
973 Run: func(c *Command, _ []string) { c.Print(expected) },
974 })
975
976 got, err := executeCommand(c, "help")
977 if err != nil {
978 t.Errorf("Unexpected error: %v", err)
979 }
980
981 if got != expected {
982 t.Errorf("Expected to contain %q, got %q", expected, got)
983 }
984 }
985
986 func TestHelpFlagExecuted(t *testing.T) {
987 rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
988
989 output, err := executeCommand(rootCmd, "--help")
990 if err != nil {
991 t.Errorf("Unexpected error: %v", err)
992 }
993
994 checkStringContains(t, output, rootCmd.Long)
995 }
996
997 func TestHelpFlagExecutedOnChild(t *testing.T) {
998 rootCmd := &Command{Use: "root", Run: emptyRun}
999 childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun}
1000 rootCmd.AddCommand(childCmd)
1001
1002 output, err := executeCommand(rootCmd, "child", "--help")
1003 if err != nil {
1004 t.Errorf("Unexpected error: %v", err)
1005 }
1006
1007 checkStringContains(t, output, childCmd.Long)
1008 }
1009
1010
1011
1012
1013
1014 func TestHelpFlagInHelp(t *testing.T) {
1015 parentCmd := &Command{Use: "parent", Run: func(*Command, []string) {}}
1016
1017 childCmd := &Command{Use: "child", Run: func(*Command, []string) {}}
1018 parentCmd.AddCommand(childCmd)
1019
1020 output, err := executeCommand(parentCmd, "help", "child")
1021 if err != nil {
1022 t.Errorf("Unexpected error: %v", err)
1023 }
1024
1025 checkStringContains(t, output, "[flags]")
1026 }
1027
1028 func TestFlagsInUsage(t *testing.T) {
1029 rootCmd := &Command{Use: "root", Args: NoArgs, Run: func(*Command, []string) {}}
1030 output, err := executeCommand(rootCmd, "--help")
1031 if err != nil {
1032 t.Errorf("Unexpected error: %v", err)
1033 }
1034
1035 checkStringContains(t, output, "[flags]")
1036 }
1037
1038 func TestHelpExecutedOnNonRunnableChild(t *testing.T) {
1039 rootCmd := &Command{Use: "root", Run: emptyRun}
1040 childCmd := &Command{Use: "child", Long: "Long description"}
1041 rootCmd.AddCommand(childCmd)
1042
1043 output, err := executeCommand(rootCmd, "child")
1044 if err != nil {
1045 t.Errorf("Unexpected error: %v", err)
1046 }
1047
1048 checkStringContains(t, output, childCmd.Long)
1049 }
1050
1051 func TestVersionFlagExecuted(t *testing.T) {
1052 rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1053
1054 output, err := executeCommand(rootCmd, "--version", "arg1")
1055 if err != nil {
1056 t.Errorf("Unexpected error: %v", err)
1057 }
1058
1059 checkStringContains(t, output, "root version 1.0.0")
1060 }
1061
1062 func TestVersionFlagExecutedWithNoName(t *testing.T) {
1063 rootCmd := &Command{Version: "1.0.0", Run: emptyRun}
1064
1065 output, err := executeCommand(rootCmd, "--version", "arg1")
1066 if err != nil {
1067 t.Errorf("Unexpected error: %v", err)
1068 }
1069
1070 checkStringContains(t, output, "version 1.0.0")
1071 }
1072
1073 func TestShortAndLongVersionFlagInHelp(t *testing.T) {
1074 rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1075
1076 output, err := executeCommand(rootCmd, "--help")
1077 if err != nil {
1078 t.Errorf("Unexpected error: %v", err)
1079 }
1080
1081 checkStringContains(t, output, "-v, --version")
1082 }
1083
1084 func TestLongVersionFlagOnlyInHelpWhenShortPredefined(t *testing.T) {
1085 rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1086 rootCmd.Flags().StringP("foo", "v", "", "not a version flag")
1087
1088 output, err := executeCommand(rootCmd, "--help")
1089 if err != nil {
1090 t.Errorf("Unexpected error: %v", err)
1091 }
1092
1093 checkStringOmits(t, output, "-v, --version")
1094 checkStringContains(t, output, "--version")
1095 }
1096
1097 func TestShorthandVersionFlagExecuted(t *testing.T) {
1098 rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1099
1100 output, err := executeCommand(rootCmd, "-v", "arg1")
1101 if err != nil {
1102 t.Errorf("Unexpected error: %v", err)
1103 }
1104
1105 checkStringContains(t, output, "root version 1.0.0")
1106 }
1107
1108 func TestVersionTemplate(t *testing.T) {
1109 rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1110 rootCmd.SetVersionTemplate(`customized version: {{.Version}}`)
1111
1112 output, err := executeCommand(rootCmd, "--version", "arg1")
1113 if err != nil {
1114 t.Errorf("Unexpected error: %v", err)
1115 }
1116
1117 checkStringContains(t, output, "customized version: 1.0.0")
1118 }
1119
1120 func TestShorthandVersionTemplate(t *testing.T) {
1121 rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1122 rootCmd.SetVersionTemplate(`customized version: {{.Version}}`)
1123
1124 output, err := executeCommand(rootCmd, "-v", "arg1")
1125 if err != nil {
1126 t.Errorf("Unexpected error: %v", err)
1127 }
1128
1129 checkStringContains(t, output, "customized version: 1.0.0")
1130 }
1131
1132 func TestRootErrPrefixExecutedOnSubcommand(t *testing.T) {
1133 rootCmd := &Command{Use: "root", Run: emptyRun}
1134 rootCmd.SetErrPrefix("root error prefix:")
1135 rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
1136
1137 output, err := executeCommand(rootCmd, "sub", "--unknown-flag")
1138 if err == nil {
1139 t.Errorf("Expected error")
1140 }
1141
1142 checkStringContains(t, output, "root error prefix: unknown flag: --unknown-flag")
1143 }
1144
1145 func TestRootAndSubErrPrefix(t *testing.T) {
1146 rootCmd := &Command{Use: "root", Run: emptyRun}
1147 subCmd := &Command{Use: "sub", Run: emptyRun}
1148 rootCmd.AddCommand(subCmd)
1149 rootCmd.SetErrPrefix("root error prefix:")
1150 subCmd.SetErrPrefix("sub error prefix:")
1151
1152 if output, err := executeCommand(rootCmd, "--unknown-root-flag"); err == nil {
1153 t.Errorf("Expected error")
1154 } else {
1155 checkStringContains(t, output, "root error prefix: unknown flag: --unknown-root-flag")
1156 }
1157
1158 if output, err := executeCommand(rootCmd, "sub", "--unknown-sub-flag"); err == nil {
1159 t.Errorf("Expected error")
1160 } else {
1161 checkStringContains(t, output, "sub error prefix: unknown flag: --unknown-sub-flag")
1162 }
1163 }
1164
1165 func TestVersionFlagExecutedOnSubcommand(t *testing.T) {
1166 rootCmd := &Command{Use: "root", Version: "1.0.0"}
1167 rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
1168
1169 output, err := executeCommand(rootCmd, "--version", "sub")
1170 if err != nil {
1171 t.Errorf("Unexpected error: %v", err)
1172 }
1173
1174 checkStringContains(t, output, "root version 1.0.0")
1175 }
1176
1177 func TestShorthandVersionFlagExecutedOnSubcommand(t *testing.T) {
1178 rootCmd := &Command{Use: "root", Version: "1.0.0"}
1179 rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
1180
1181 output, err := executeCommand(rootCmd, "-v", "sub")
1182 if err != nil {
1183 t.Errorf("Unexpected error: %v", err)
1184 }
1185
1186 checkStringContains(t, output, "root version 1.0.0")
1187 }
1188
1189 func TestVersionFlagOnlyAddedToRoot(t *testing.T) {
1190 rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1191 rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
1192
1193 _, err := executeCommand(rootCmd, "sub", "--version")
1194 if err == nil {
1195 t.Errorf("Expected error")
1196 }
1197
1198 checkStringContains(t, err.Error(), "unknown flag: --version")
1199 }
1200
1201 func TestShortVersionFlagOnlyAddedToRoot(t *testing.T) {
1202 rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
1203 rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
1204
1205 _, err := executeCommand(rootCmd, "sub", "-v")
1206 if err == nil {
1207 t.Errorf("Expected error")
1208 }
1209
1210 checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
1211 }
1212
1213 func TestVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) {
1214 rootCmd := &Command{Use: "root", Run: emptyRun}
1215
1216 _, err := executeCommand(rootCmd, "--version")
1217 if err == nil {
1218 t.Errorf("Expected error")
1219 }
1220 checkStringContains(t, err.Error(), "unknown flag: --version")
1221 }
1222
1223 func TestShorthandVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) {
1224 rootCmd := &Command{Use: "root", Run: emptyRun}
1225
1226 _, err := executeCommand(rootCmd, "-v")
1227 if err == nil {
1228 t.Errorf("Expected error")
1229 }
1230 checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
1231 }
1232
1233 func TestShorthandVersionFlagOnlyAddedIfShorthandNotDefined(t *testing.T) {
1234 rootCmd := &Command{Use: "root", Run: emptyRun, Version: "1.2.3"}
1235 rootCmd.Flags().StringP("notversion", "v", "", "not a version flag")
1236
1237 _, err := executeCommand(rootCmd, "-v")
1238 if err == nil {
1239 t.Errorf("Expected error")
1240 }
1241 check(t, rootCmd.Flags().ShorthandLookup("v").Name, "notversion")
1242 checkStringContains(t, err.Error(), "flag needs an argument: 'v' in -v")
1243 }
1244
1245 func TestShorthandVersionFlagOnlyAddedIfVersionNotDefined(t *testing.T) {
1246 rootCmd := &Command{Use: "root", Run: emptyRun, Version: "1.2.3"}
1247 rootCmd.Flags().Bool("version", false, "a different kind of version flag")
1248
1249 _, err := executeCommand(rootCmd, "-v")
1250 if err == nil {
1251 t.Errorf("Expected error")
1252 }
1253 checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
1254 }
1255
1256 func TestUsageIsNotPrintedTwice(t *testing.T) {
1257 var cmd = &Command{Use: "root"}
1258 var sub = &Command{Use: "sub"}
1259 cmd.AddCommand(sub)
1260
1261 output, _ := executeCommand(cmd, "")
1262 if strings.Count(output, "Usage:") != 1 {
1263 t.Error("Usage output is not printed exactly once")
1264 }
1265 }
1266
1267 func TestVisitParents(t *testing.T) {
1268 c := &Command{Use: "app"}
1269 sub := &Command{Use: "sub"}
1270 dsub := &Command{Use: "dsub"}
1271 sub.AddCommand(dsub)
1272 c.AddCommand(sub)
1273
1274 total := 0
1275 add := func(x *Command) {
1276 total++
1277 }
1278 sub.VisitParents(add)
1279 if total != 1 {
1280 t.Errorf("Should have visited 1 parent but visited %d", total)
1281 }
1282
1283 total = 0
1284 dsub.VisitParents(add)
1285 if total != 2 {
1286 t.Errorf("Should have visited 2 parents but visited %d", total)
1287 }
1288
1289 total = 0
1290 c.VisitParents(add)
1291 if total != 0 {
1292 t.Errorf("Should have visited no parents but visited %d", total)
1293 }
1294 }
1295
1296 func TestSuggestions(t *testing.T) {
1297 rootCmd := &Command{Use: "root", Run: emptyRun}
1298 timesCmd := &Command{
1299 Use: "times",
1300 SuggestFor: []string{"counts"},
1301 Run: emptyRun,
1302 }
1303 rootCmd.AddCommand(timesCmd)
1304
1305 templateWithSuggestions := "Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n"
1306 templateWithoutSuggestions := "Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n"
1307
1308 tests := map[string]string{
1309 "time": "times",
1310 "tiems": "times",
1311 "tims": "times",
1312 "timeS": "times",
1313 "rimes": "times",
1314 "ti": "times",
1315 "t": "times",
1316 "timely": "times",
1317 "ri": "",
1318 "timezone": "",
1319 "foo": "",
1320 "counts": "times",
1321 }
1322
1323 for typo, suggestion := range tests {
1324 for _, suggestionsDisabled := range []bool{true, false} {
1325 rootCmd.DisableSuggestions = suggestionsDisabled
1326
1327 var expected string
1328 output, _ := executeCommand(rootCmd, typo)
1329
1330 if suggestion == "" || suggestionsDisabled {
1331 expected = fmt.Sprintf(templateWithoutSuggestions, typo)
1332 } else {
1333 expected = fmt.Sprintf(templateWithSuggestions, typo, suggestion)
1334 }
1335
1336 if output != expected {
1337 t.Errorf("Unexpected response.\nExpected:\n %q\nGot:\n %q\n", expected, output)
1338 }
1339 }
1340 }
1341 }
1342
1343 func TestCaseInsensitive(t *testing.T) {
1344 rootCmd := &Command{Use: "root", Run: emptyRun}
1345 childCmd := &Command{Use: "child", Run: emptyRun, Aliases: []string{"alternative"}}
1346 granchildCmd := &Command{Use: "GRANDCHILD", Run: emptyRun, Aliases: []string{"ALIAS"}}
1347
1348 childCmd.AddCommand(granchildCmd)
1349 rootCmd.AddCommand(childCmd)
1350
1351 tests := []struct {
1352 args []string
1353 failWithoutEnabling bool
1354 }{
1355 {
1356 args: []string{"child"},
1357 failWithoutEnabling: false,
1358 },
1359 {
1360 args: []string{"CHILD"},
1361 failWithoutEnabling: true,
1362 },
1363 {
1364 args: []string{"chILD"},
1365 failWithoutEnabling: true,
1366 },
1367 {
1368 args: []string{"CHIld"},
1369 failWithoutEnabling: true,
1370 },
1371 {
1372 args: []string{"alternative"},
1373 failWithoutEnabling: false,
1374 },
1375 {
1376 args: []string{"ALTERNATIVE"},
1377 failWithoutEnabling: true,
1378 },
1379 {
1380 args: []string{"ALTernatIVE"},
1381 failWithoutEnabling: true,
1382 },
1383 {
1384 args: []string{"alternatiVE"},
1385 failWithoutEnabling: true,
1386 },
1387 {
1388 args: []string{"child", "GRANDCHILD"},
1389 failWithoutEnabling: false,
1390 },
1391 {
1392 args: []string{"child", "grandchild"},
1393 failWithoutEnabling: true,
1394 },
1395 {
1396 args: []string{"CHIld", "GRANdchild"},
1397 failWithoutEnabling: true,
1398 },
1399 {
1400 args: []string{"alternative", "ALIAS"},
1401 failWithoutEnabling: false,
1402 },
1403 {
1404 args: []string{"alternative", "alias"},
1405 failWithoutEnabling: true,
1406 },
1407 {
1408 args: []string{"CHILD", "alias"},
1409 failWithoutEnabling: true,
1410 },
1411 {
1412 args: []string{"CHIld", "aliAS"},
1413 failWithoutEnabling: true,
1414 },
1415 }
1416
1417 for _, test := range tests {
1418 for _, enableCaseInsensitivity := range []bool{true, false} {
1419 EnableCaseInsensitive = enableCaseInsensitivity
1420
1421 output, err := executeCommand(rootCmd, test.args...)
1422 expectedFailure := test.failWithoutEnabling && !enableCaseInsensitivity
1423
1424 if !expectedFailure && output != "" {
1425 t.Errorf("Unexpected output: %v", output)
1426 }
1427 if !expectedFailure && err != nil {
1428 t.Errorf("Unexpected error: %v", err)
1429 }
1430 }
1431 }
1432
1433 EnableCaseInsensitive = defaultCaseInsensitive
1434 }
1435
1436
1437
1438 func TestCaseSensitivityBackwardCompatibility(t *testing.T) {
1439 rootCmd := &Command{Use: "root", Run: emptyRun}
1440 childCmd := &Command{Use: "child", Run: emptyRun}
1441
1442 rootCmd.AddCommand(childCmd)
1443 _, err := executeCommand(rootCmd, strings.ToUpper(childCmd.Use))
1444 if err == nil {
1445 t.Error("Expected error on calling a command in upper case while command names are case sensitive. Got nil.")
1446 }
1447
1448 }
1449
1450 func TestRemoveCommand(t *testing.T) {
1451 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
1452 childCmd := &Command{Use: "child", Run: emptyRun}
1453 rootCmd.AddCommand(childCmd)
1454 rootCmd.RemoveCommand(childCmd)
1455
1456 _, err := executeCommand(rootCmd, "child")
1457 if err == nil {
1458 t.Error("Expected error on calling removed command. Got nil.")
1459 }
1460 }
1461
1462 func TestReplaceCommandWithRemove(t *testing.T) {
1463 childUsed := 0
1464 rootCmd := &Command{Use: "root", Run: emptyRun}
1465 child1Cmd := &Command{
1466 Use: "child",
1467 Run: func(*Command, []string) { childUsed = 1 },
1468 }
1469 child2Cmd := &Command{
1470 Use: "child",
1471 Run: func(*Command, []string) { childUsed = 2 },
1472 }
1473 rootCmd.AddCommand(child1Cmd)
1474 rootCmd.RemoveCommand(child1Cmd)
1475 rootCmd.AddCommand(child2Cmd)
1476
1477 output, err := executeCommand(rootCmd, "child")
1478 if output != "" {
1479 t.Errorf("Unexpected output: %v", output)
1480 }
1481 if err != nil {
1482 t.Errorf("Unexpected error: %v", err)
1483 }
1484
1485 if childUsed == 1 {
1486 t.Error("Removed command shouldn't be called")
1487 }
1488 if childUsed != 2 {
1489 t.Error("Replacing command should have been called but didn't")
1490 }
1491 }
1492
1493 func TestDeprecatedCommand(t *testing.T) {
1494 rootCmd := &Command{Use: "root", Run: emptyRun}
1495 deprecatedCmd := &Command{
1496 Use: "deprecated",
1497 Deprecated: "This command is deprecated",
1498 Run: emptyRun,
1499 }
1500 rootCmd.AddCommand(deprecatedCmd)
1501
1502 output, err := executeCommand(rootCmd, "deprecated")
1503 if err != nil {
1504 t.Errorf("Unexpected error: %v", err)
1505 }
1506
1507 checkStringContains(t, output, deprecatedCmd.Deprecated)
1508 }
1509
1510 func TestHooks(t *testing.T) {
1511 var (
1512 persPreArgs string
1513 preArgs string
1514 runArgs string
1515 postArgs string
1516 persPostArgs string
1517 )
1518
1519 c := &Command{
1520 Use: "c",
1521 PersistentPreRun: func(_ *Command, args []string) {
1522 persPreArgs = strings.Join(args, " ")
1523 },
1524 PreRun: func(_ *Command, args []string) {
1525 preArgs = strings.Join(args, " ")
1526 },
1527 Run: func(_ *Command, args []string) {
1528 runArgs = strings.Join(args, " ")
1529 },
1530 PostRun: func(_ *Command, args []string) {
1531 postArgs = strings.Join(args, " ")
1532 },
1533 PersistentPostRun: func(_ *Command, args []string) {
1534 persPostArgs = strings.Join(args, " ")
1535 },
1536 }
1537
1538 output, err := executeCommand(c, "one", "two")
1539 if output != "" {
1540 t.Errorf("Unexpected output: %v", output)
1541 }
1542 if err != nil {
1543 t.Errorf("Unexpected error: %v", err)
1544 }
1545
1546 for _, v := range []struct {
1547 name string
1548 got string
1549 }{
1550 {"persPreArgs", persPreArgs},
1551 {"preArgs", preArgs},
1552 {"runArgs", runArgs},
1553 {"postArgs", postArgs},
1554 {"persPostArgs", persPostArgs},
1555 } {
1556 if v.got != onetwo {
1557 t.Errorf("Expected %s %q, got %q", v.name, onetwo, v.got)
1558 }
1559 }
1560 }
1561
1562 func TestPersistentHooks(t *testing.T) {
1563 EnableTraverseRunHooks = true
1564 testPersistentHooks(t, []string{
1565 "parent PersistentPreRun",
1566 "child PersistentPreRun",
1567 "child PreRun",
1568 "child Run",
1569 "child PostRun",
1570 "child PersistentPostRun",
1571 "parent PersistentPostRun",
1572 })
1573
1574 EnableTraverseRunHooks = false
1575 testPersistentHooks(t, []string{
1576 "child PersistentPreRun",
1577 "child PreRun",
1578 "child Run",
1579 "child PostRun",
1580 "child PersistentPostRun",
1581 })
1582 }
1583
1584 func testPersistentHooks(t *testing.T, expectedHookRunOrder []string) {
1585 var hookRunOrder []string
1586
1587 validateHook := func(args []string, hookName string) {
1588 hookRunOrder = append(hookRunOrder, hookName)
1589 got := strings.Join(args, " ")
1590 if onetwo != got {
1591 t.Errorf("Expected %s %q, got %q", hookName, onetwo, got)
1592 }
1593 }
1594
1595 parentCmd := &Command{
1596 Use: "parent",
1597 PersistentPreRun: func(_ *Command, args []string) {
1598 validateHook(args, "parent PersistentPreRun")
1599 },
1600 PreRun: func(_ *Command, args []string) {
1601 validateHook(args, "parent PreRun")
1602 },
1603 Run: func(_ *Command, args []string) {
1604 validateHook(args, "parent Run")
1605 },
1606 PostRun: func(_ *Command, args []string) {
1607 validateHook(args, "parent PostRun")
1608 },
1609 PersistentPostRun: func(_ *Command, args []string) {
1610 validateHook(args, "parent PersistentPostRun")
1611 },
1612 }
1613
1614 childCmd := &Command{
1615 Use: "child",
1616 PersistentPreRun: func(_ *Command, args []string) {
1617 validateHook(args, "child PersistentPreRun")
1618 },
1619 PreRun: func(_ *Command, args []string) {
1620 validateHook(args, "child PreRun")
1621 },
1622 Run: func(_ *Command, args []string) {
1623 validateHook(args, "child Run")
1624 },
1625 PostRun: func(_ *Command, args []string) {
1626 validateHook(args, "child PostRun")
1627 },
1628 PersistentPostRun: func(_ *Command, args []string) {
1629 validateHook(args, "child PersistentPostRun")
1630 },
1631 }
1632 parentCmd.AddCommand(childCmd)
1633
1634 output, err := executeCommand(parentCmd, "child", "one", "two")
1635 if output != "" {
1636 t.Errorf("Unexpected output: %v", output)
1637 }
1638 if err != nil {
1639 t.Errorf("Unexpected error: %v", err)
1640 }
1641
1642 for idx, exp := range expectedHookRunOrder {
1643 if len(hookRunOrder) > idx {
1644 if act := hookRunOrder[idx]; act != exp {
1645 t.Errorf("Expected %q at %d, got %q", exp, idx, act)
1646 }
1647 } else {
1648 t.Errorf("Expected %q at %d, got nothing", exp, idx)
1649 }
1650 }
1651 }
1652
1653
1654 func TestGlobalNormFuncPropagation(t *testing.T) {
1655 normFunc := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
1656 return pflag.NormalizedName(name)
1657 }
1658
1659 rootCmd := &Command{Use: "root", Run: emptyRun}
1660 childCmd := &Command{Use: "child", Run: emptyRun}
1661 rootCmd.AddCommand(childCmd)
1662
1663 rootCmd.SetGlobalNormalizationFunc(normFunc)
1664 if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() {
1665 t.Error("rootCmd seems to have a wrong normalization function")
1666 }
1667
1668 if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(childCmd.GlobalNormalizationFunc()).Pointer() {
1669 t.Error("childCmd should have had the normalization function of rootCmd")
1670 }
1671 }
1672
1673
1674 func TestNormPassedOnLocal(t *testing.T) {
1675 toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
1676 return pflag.NormalizedName(strings.ToUpper(name))
1677 }
1678
1679 c := &Command{}
1680 c.Flags().Bool("flagname", true, "this is a dummy flag")
1681 c.SetGlobalNormalizationFunc(toUpper)
1682 if c.LocalFlags().Lookup("flagname") != c.LocalFlags().Lookup("FLAGNAME") {
1683 t.Error("Normalization function should be passed on to Local flag set")
1684 }
1685 }
1686
1687
1688 func TestNormPassedOnInherited(t *testing.T) {
1689 toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
1690 return pflag.NormalizedName(strings.ToUpper(name))
1691 }
1692
1693 c := &Command{}
1694 c.SetGlobalNormalizationFunc(toUpper)
1695
1696 child1 := &Command{}
1697 c.AddCommand(child1)
1698
1699 c.PersistentFlags().Bool("flagname", true, "")
1700
1701 child2 := &Command{}
1702 c.AddCommand(child2)
1703
1704 inherited := child1.InheritedFlags()
1705 if inherited.Lookup("flagname") == nil || inherited.Lookup("flagname") != inherited.Lookup("FLAGNAME") {
1706 t.Error("Normalization function should be passed on to inherited flag set in command added before flag")
1707 }
1708
1709 inherited = child2.InheritedFlags()
1710 if inherited.Lookup("flagname") == nil || inherited.Lookup("flagname") != inherited.Lookup("FLAGNAME") {
1711 t.Error("Normalization function should be passed on to inherited flag set in command added after flag")
1712 }
1713 }
1714
1715
1716 func TestConsistentNormalizedName(t *testing.T) {
1717 toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
1718 return pflag.NormalizedName(strings.ToUpper(name))
1719 }
1720 n := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
1721 return pflag.NormalizedName(name)
1722 }
1723
1724 c := &Command{}
1725 c.Flags().Bool("flagname", true, "")
1726 c.SetGlobalNormalizationFunc(toUpper)
1727 c.SetGlobalNormalizationFunc(n)
1728
1729 if c.LocalFlags().Lookup("flagname") == c.LocalFlags().Lookup("FLAGNAME") {
1730 t.Error("Normalizing flag names should not result in duplicate flags")
1731 }
1732 }
1733
1734 func TestFlagOnPflagCommandLine(t *testing.T) {
1735 flagName := "flagOnCommandLine"
1736 pflag.String(flagName, "", "about my flag")
1737
1738 c := &Command{Use: "c", Run: emptyRun}
1739 c.AddCommand(&Command{Use: "child", Run: emptyRun})
1740
1741 output, _ := executeCommand(c, "--help")
1742 checkStringContains(t, output, flagName)
1743
1744 resetCommandLineFlagSet()
1745 }
1746
1747
1748
1749 func TestHiddenCommandExecutes(t *testing.T) {
1750 executed := false
1751 c := &Command{
1752 Use: "c",
1753 Hidden: true,
1754 Run: func(*Command, []string) { executed = true },
1755 }
1756
1757 output, err := executeCommand(c)
1758 if output != "" {
1759 t.Errorf("Unexpected output: %v", output)
1760 }
1761 if err != nil {
1762 t.Errorf("Unexpected error: %v", err)
1763 }
1764
1765 if !executed {
1766 t.Error("Hidden command should have been executed")
1767 }
1768 }
1769
1770
1771 func TestHiddenCommandIsHidden(t *testing.T) {
1772 c := &Command{Use: "c", Hidden: true, Run: emptyRun}
1773 if c.IsAvailableCommand() {
1774 t.Errorf("Hidden command should be unavailable")
1775 }
1776 }
1777
1778 func TestCommandsAreSorted(t *testing.T) {
1779 EnableCommandSorting = true
1780
1781 originalNames := []string{"middle", "zlast", "afirst"}
1782 expectedNames := []string{"afirst", "middle", "zlast"}
1783
1784 var rootCmd = &Command{Use: "root"}
1785
1786 for _, name := range originalNames {
1787 rootCmd.AddCommand(&Command{Use: name})
1788 }
1789
1790 for i, c := range rootCmd.Commands() {
1791 got := c.Name()
1792 if expectedNames[i] != got {
1793 t.Errorf("Expected: %s, got: %s", expectedNames[i], got)
1794 }
1795 }
1796
1797 EnableCommandSorting = defaultCommandSorting
1798 }
1799
1800 func TestEnableCommandSortingIsDisabled(t *testing.T) {
1801 EnableCommandSorting = false
1802
1803 originalNames := []string{"middle", "zlast", "afirst"}
1804
1805 var rootCmd = &Command{Use: "root"}
1806
1807 for _, name := range originalNames {
1808 rootCmd.AddCommand(&Command{Use: name})
1809 }
1810
1811 for i, c := range rootCmd.Commands() {
1812 got := c.Name()
1813 if originalNames[i] != got {
1814 t.Errorf("expected: %s, got: %s", originalNames[i], got)
1815 }
1816 }
1817
1818 EnableCommandSorting = defaultCommandSorting
1819 }
1820
1821 func TestUsageWithGroup(t *testing.T) {
1822 var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
1823 rootCmd.CompletionOptions.DisableDefaultCmd = true
1824
1825 rootCmd.AddGroup(&Group{ID: "group1", Title: "group1"})
1826 rootCmd.AddGroup(&Group{ID: "group2", Title: "group2"})
1827
1828 rootCmd.AddCommand(&Command{Use: "cmd1", GroupID: "group1", Run: emptyRun})
1829 rootCmd.AddCommand(&Command{Use: "cmd2", GroupID: "group2", Run: emptyRun})
1830
1831 output, err := executeCommand(rootCmd, "--help")
1832 if err != nil {
1833 t.Errorf("Unexpected error: %v", err)
1834 }
1835
1836
1837 checkStringContains(t, output, "\nAdditional Commands:\n help")
1838 checkStringContains(t, output, "\ngroup1\n cmd1")
1839 checkStringContains(t, output, "\ngroup2\n cmd2")
1840 }
1841
1842 func TestUsageHelpGroup(t *testing.T) {
1843 var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
1844 rootCmd.CompletionOptions.DisableDefaultCmd = true
1845
1846 rootCmd.AddGroup(&Group{ID: "group", Title: "group"})
1847 rootCmd.AddCommand(&Command{Use: "xxx", GroupID: "group", Run: emptyRun})
1848 rootCmd.SetHelpCommandGroupID("group")
1849
1850 output, err := executeCommand(rootCmd, "--help")
1851 if err != nil {
1852 t.Errorf("Unexpected error: %v", err)
1853 }
1854
1855
1856 checkStringOmits(t, output, "\nAdditional Commands:\n help")
1857 checkStringContains(t, output, "\ngroup\n help")
1858 }
1859
1860 func TestUsageCompletionGroup(t *testing.T) {
1861 var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
1862
1863 rootCmd.AddGroup(&Group{ID: "group", Title: "group"})
1864 rootCmd.AddGroup(&Group{ID: "help", Title: "help"})
1865
1866 rootCmd.AddCommand(&Command{Use: "xxx", GroupID: "group", Run: emptyRun})
1867 rootCmd.SetHelpCommandGroupID("help")
1868 rootCmd.SetCompletionCommandGroupID("group")
1869
1870 output, err := executeCommand(rootCmd, "--help")
1871 if err != nil {
1872 t.Errorf("Unexpected error: %v", err)
1873 }
1874
1875
1876 checkStringOmits(t, output, "\nAdditional Commands:\n completion")
1877 checkStringContains(t, output, "\ngroup\n completion")
1878 }
1879
1880 func TestUngroupedCommand(t *testing.T) {
1881 var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
1882
1883 rootCmd.AddGroup(&Group{ID: "group", Title: "group"})
1884 rootCmd.AddGroup(&Group{ID: "help", Title: "help"})
1885
1886 rootCmd.AddCommand(&Command{Use: "xxx", GroupID: "group", Run: emptyRun})
1887 rootCmd.SetHelpCommandGroupID("help")
1888 rootCmd.SetCompletionCommandGroupID("group")
1889
1890
1891 rootCmd.AddCommand(&Command{Use: "yyy", Run: emptyRun})
1892
1893 output, err := executeCommand(rootCmd, "--help")
1894 if err != nil {
1895 t.Errorf("Unexpected error: %v", err)
1896 }
1897
1898
1899 checkStringContains(t, output, "\nAdditional Commands:\n yyy")
1900 }
1901
1902 func TestAddGroup(t *testing.T) {
1903 var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
1904
1905 rootCmd.AddGroup(&Group{ID: "group", Title: "Test group"})
1906 rootCmd.AddCommand(&Command{Use: "cmd", GroupID: "group", Run: emptyRun})
1907
1908 output, err := executeCommand(rootCmd, "--help")
1909 if err != nil {
1910 t.Errorf("Unexpected error: %v", err)
1911 }
1912
1913 checkStringContains(t, output, "\nTest group\n cmd")
1914 }
1915
1916 func TestWrongGroupFirstLevel(t *testing.T) {
1917 var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
1918
1919 rootCmd.AddGroup(&Group{ID: "group", Title: "Test group"})
1920
1921 rootCmd.AddCommand(&Command{Use: "cmd", GroupID: "wrong", Run: emptyRun})
1922
1923 defer func() {
1924 if recover() == nil {
1925 t.Errorf("The code should have panicked due to a missing group")
1926 }
1927 }()
1928 _, err := executeCommand(rootCmd, "--help")
1929 if err != nil {
1930 t.Errorf("Unexpected error: %v", err)
1931 }
1932 }
1933
1934 func TestWrongGroupNestedLevel(t *testing.T) {
1935 var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
1936 var childCmd = &Command{Use: "child", Run: emptyRun}
1937 rootCmd.AddCommand(childCmd)
1938
1939 childCmd.AddGroup(&Group{ID: "group", Title: "Test group"})
1940
1941 childCmd.AddCommand(&Command{Use: "cmd", GroupID: "wrong", Run: emptyRun})
1942
1943 defer func() {
1944 if recover() == nil {
1945 t.Errorf("The code should have panicked due to a missing group")
1946 }
1947 }()
1948 _, err := executeCommand(rootCmd, "child", "--help")
1949 if err != nil {
1950 t.Errorf("Unexpected error: %v", err)
1951 }
1952 }
1953
1954 func TestWrongGroupForHelp(t *testing.T) {
1955 var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
1956 var childCmd = &Command{Use: "child", Run: emptyRun}
1957 rootCmd.AddCommand(childCmd)
1958
1959 rootCmd.AddGroup(&Group{ID: "group", Title: "Test group"})
1960
1961 rootCmd.SetHelpCommandGroupID("wrong")
1962
1963 defer func() {
1964 if recover() == nil {
1965 t.Errorf("The code should have panicked due to a missing group")
1966 }
1967 }()
1968 _, err := executeCommand(rootCmd, "--help")
1969 if err != nil {
1970 t.Errorf("Unexpected error: %v", err)
1971 }
1972 }
1973
1974 func TestWrongGroupForCompletion(t *testing.T) {
1975 var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
1976 var childCmd = &Command{Use: "child", Run: emptyRun}
1977 rootCmd.AddCommand(childCmd)
1978
1979 rootCmd.AddGroup(&Group{ID: "group", Title: "Test group"})
1980
1981 rootCmd.SetCompletionCommandGroupID("wrong")
1982
1983 defer func() {
1984 if recover() == nil {
1985 t.Errorf("The code should have panicked due to a missing group")
1986 }
1987 }()
1988 _, err := executeCommand(rootCmd, "--help")
1989 if err != nil {
1990 t.Errorf("Unexpected error: %v", err)
1991 }
1992 }
1993
1994 func TestSetOutput(t *testing.T) {
1995 c := &Command{}
1996 c.SetOutput(nil)
1997 if out := c.OutOrStdout(); out != os.Stdout {
1998 t.Errorf("Expected setting output to nil to revert back to stdout")
1999 }
2000 }
2001
2002 func TestSetOut(t *testing.T) {
2003 c := &Command{}
2004 c.SetOut(nil)
2005 if out := c.OutOrStdout(); out != os.Stdout {
2006 t.Errorf("Expected setting output to nil to revert back to stdout")
2007 }
2008 }
2009
2010 func TestSetErr(t *testing.T) {
2011 c := &Command{}
2012 c.SetErr(nil)
2013 if out := c.ErrOrStderr(); out != os.Stderr {
2014 t.Errorf("Expected setting error to nil to revert back to stderr")
2015 }
2016 }
2017
2018 func TestSetIn(t *testing.T) {
2019 c := &Command{}
2020 c.SetIn(nil)
2021 if out := c.InOrStdin(); out != os.Stdin {
2022 t.Errorf("Expected setting input to nil to revert back to stdin")
2023 }
2024 }
2025
2026 func TestUsageStringRedirected(t *testing.T) {
2027 c := &Command{}
2028
2029 c.usageFunc = func(cmd *Command) error {
2030 cmd.Print("[stdout1]")
2031 cmd.PrintErr("[stderr2]")
2032 cmd.Print("[stdout3]")
2033 return nil
2034 }
2035
2036 expected := "[stdout1][stderr2][stdout3]"
2037 if got := c.UsageString(); got != expected {
2038 t.Errorf("Expected usage string to consider both stdout and stderr")
2039 }
2040 }
2041
2042 func TestCommandPrintRedirection(t *testing.T) {
2043 errBuff, outBuff := bytes.NewBuffer(nil), bytes.NewBuffer(nil)
2044 root := &Command{
2045 Run: func(cmd *Command, args []string) {
2046
2047 cmd.PrintErr("PrintErr")
2048 cmd.PrintErrln("PrintErr", "line")
2049 cmd.PrintErrf("PrintEr%s", "r")
2050
2051 cmd.Print("Print")
2052 cmd.Println("Print", "line")
2053 cmd.Printf("Prin%s", "t")
2054 },
2055 }
2056
2057 root.SetErr(errBuff)
2058 root.SetOut(outBuff)
2059
2060 if err := root.Execute(); err != nil {
2061 t.Error(err)
2062 }
2063
2064 gotErrBytes, err := ioutil.ReadAll(errBuff)
2065 if err != nil {
2066 t.Error(err)
2067 }
2068
2069 gotOutBytes, err := ioutil.ReadAll(outBuff)
2070 if err != nil {
2071 t.Error(err)
2072 }
2073
2074 if wantErr := []byte("PrintErrPrintErr line\nPrintErr"); !bytes.Equal(gotErrBytes, wantErr) {
2075 t.Errorf("got: '%s' want: '%s'", gotErrBytes, wantErr)
2076 }
2077
2078 if wantOut := []byte("PrintPrint line\nPrint"); !bytes.Equal(gotOutBytes, wantOut) {
2079 t.Errorf("got: '%s' want: '%s'", gotOutBytes, wantOut)
2080 }
2081 }
2082
2083 func TestFlagErrorFunc(t *testing.T) {
2084 c := &Command{Use: "c", Run: emptyRun}
2085
2086 expectedFmt := "This is expected: %v"
2087 c.SetFlagErrorFunc(func(_ *Command, err error) error {
2088 return fmt.Errorf(expectedFmt, err)
2089 })
2090
2091 _, err := executeCommand(c, "--unknown-flag")
2092
2093 got := err.Error()
2094 expected := fmt.Sprintf(expectedFmt, "unknown flag: --unknown-flag")
2095 if got != expected {
2096 t.Errorf("Expected %v, got %v", expected, got)
2097 }
2098 }
2099
2100 func TestFlagErrorFuncHelp(t *testing.T) {
2101 c := &Command{Use: "c", Run: emptyRun}
2102 c.PersistentFlags().Bool("help", false, "help for c")
2103 c.SetFlagErrorFunc(func(_ *Command, err error) error {
2104 return fmt.Errorf("wrap error: %w", err)
2105 })
2106
2107 out, err := executeCommand(c, "--help")
2108 if err != nil {
2109 t.Errorf("--help should not fail: %v", err)
2110 }
2111
2112 expected := `Usage:
2113 c [flags]
2114
2115 Flags:
2116 --help help for c
2117 `
2118 if out != expected {
2119 t.Errorf("Expected: %v, got: %v", expected, out)
2120 }
2121
2122 out, err = executeCommand(c, "-h")
2123 if err != nil {
2124 t.Errorf("-h should not fail: %v", err)
2125 }
2126
2127 if out != expected {
2128 t.Errorf("Expected: %v, got: %v", expected, out)
2129 }
2130 }
2131
2132
2133
2134
2135 func TestSortedFlags(t *testing.T) {
2136 c := &Command{}
2137 c.Flags().SortFlags = false
2138 names := []string{"C", "B", "A", "D"}
2139 for _, name := range names {
2140 c.Flags().Bool(name, false, "")
2141 }
2142
2143 i := 0
2144 c.LocalFlags().VisitAll(func(f *pflag.Flag) {
2145 if i == len(names) {
2146 return
2147 }
2148 if stringInSlice(f.Name, names) {
2149 if names[i] != f.Name {
2150 t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name)
2151 }
2152 i++
2153 }
2154 })
2155 }
2156
2157
2158
2159
2160
2161 func TestMergeCommandLineToFlags(t *testing.T) {
2162 pflag.Bool("boolflag", false, "")
2163 c := &Command{Use: "c", Run: emptyRun}
2164 c.mergePersistentFlags()
2165 if c.Flags().Lookup("boolflag") == nil {
2166 t.Fatal("Expecting to have flag from CommandLine in c.Flags()")
2167 }
2168
2169 resetCommandLineFlagSet()
2170 }
2171
2172
2173
2174
2175 func TestUseDeprecatedFlags(t *testing.T) {
2176 c := &Command{Use: "c", Run: emptyRun}
2177 c.Flags().BoolP("deprecated", "d", false, "deprecated flag")
2178 assertNoErr(t, c.Flags().MarkDeprecated("deprecated", "This flag is deprecated"))
2179
2180 output, err := executeCommand(c, "c", "-d")
2181 if err != nil {
2182 t.Error("Unexpected error:", err)
2183 }
2184 checkStringContains(t, output, "This flag is deprecated")
2185 }
2186
2187 func TestTraverseWithParentFlags(t *testing.T) {
2188 rootCmd := &Command{Use: "root", TraverseChildren: true}
2189 rootCmd.Flags().String("str", "", "")
2190 rootCmd.Flags().BoolP("bool", "b", false, "")
2191
2192 childCmd := &Command{Use: "child"}
2193 childCmd.Flags().Int("int", -1, "")
2194
2195 rootCmd.AddCommand(childCmd)
2196
2197 c, args, err := rootCmd.Traverse([]string{"-b", "--str", "ok", "child", "--int"})
2198 if err != nil {
2199 t.Errorf("Unexpected error: %v", err)
2200 }
2201 if len(args) != 1 && args[0] != "--add" {
2202 t.Errorf("Wrong args: %v", args)
2203 }
2204 if c.Name() != childCmd.Name() {
2205 t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name())
2206 }
2207 }
2208
2209 func TestTraverseNoParentFlags(t *testing.T) {
2210 rootCmd := &Command{Use: "root", TraverseChildren: true}
2211 rootCmd.Flags().String("foo", "", "foo things")
2212
2213 childCmd := &Command{Use: "child"}
2214 childCmd.Flags().String("str", "", "")
2215 rootCmd.AddCommand(childCmd)
2216
2217 c, args, err := rootCmd.Traverse([]string{"child"})
2218 if err != nil {
2219 t.Errorf("Unexpected error: %v", err)
2220 }
2221 if len(args) != 0 {
2222 t.Errorf("Wrong args %v", args)
2223 }
2224 if c.Name() != childCmd.Name() {
2225 t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name())
2226 }
2227 }
2228
2229 func TestTraverseWithBadParentFlags(t *testing.T) {
2230 rootCmd := &Command{Use: "root", TraverseChildren: true}
2231
2232 childCmd := &Command{Use: "child"}
2233 childCmd.Flags().String("str", "", "")
2234 rootCmd.AddCommand(childCmd)
2235
2236 expected := "unknown flag: --str"
2237
2238 c, _, err := rootCmd.Traverse([]string{"--str", "ok", "child"})
2239 if err == nil || !strings.Contains(err.Error(), expected) {
2240 t.Errorf("Expected error, %q, got %q", expected, err)
2241 }
2242 if c != nil {
2243 t.Errorf("Expected nil command")
2244 }
2245 }
2246
2247 func TestTraverseWithBadChildFlag(t *testing.T) {
2248 rootCmd := &Command{Use: "root", TraverseChildren: true}
2249 rootCmd.Flags().String("str", "", "")
2250
2251 childCmd := &Command{Use: "child"}
2252 rootCmd.AddCommand(childCmd)
2253
2254
2255
2256 c, args, err := rootCmd.Traverse([]string{"child", "--str"})
2257 if err != nil {
2258 t.Errorf("Unexpected error: %v", err)
2259 }
2260 if len(args) != 1 && args[0] != "--str" {
2261 t.Errorf("Wrong args: %v", args)
2262 }
2263 if c.Name() != childCmd.Name() {
2264 t.Errorf("Expected command %q, got: %q", childCmd.Name(), c.Name())
2265 }
2266 }
2267
2268 func TestTraverseWithTwoSubcommands(t *testing.T) {
2269 rootCmd := &Command{Use: "root", TraverseChildren: true}
2270
2271 subCmd := &Command{Use: "sub", TraverseChildren: true}
2272 rootCmd.AddCommand(subCmd)
2273
2274 subsubCmd := &Command{
2275 Use: "subsub",
2276 }
2277 subCmd.AddCommand(subsubCmd)
2278
2279 c, _, err := rootCmd.Traverse([]string{"sub", "subsub"})
2280 if err != nil {
2281 t.Fatalf("Unexpected error: %v", err)
2282 }
2283 if c.Name() != subsubCmd.Name() {
2284 t.Fatalf("Expected command: %q, got %q", subsubCmd.Name(), c.Name())
2285 }
2286 }
2287
2288
2289
2290 func TestUpdateName(t *testing.T) {
2291 c := &Command{Use: "name xyz"}
2292 originalName := c.Name()
2293
2294 c.Use = "changedName abc"
2295 if originalName == c.Name() || c.Name() != "changedName" {
2296 t.Error("c.Name() should be updated on changed c.Use")
2297 }
2298 }
2299
2300 type calledAsTestcase struct {
2301 args []string
2302 call string
2303 want string
2304 epm bool
2305 }
2306
2307 func (tc *calledAsTestcase) test(t *testing.T) {
2308 defer func(ov bool) { EnablePrefixMatching = ov }(EnablePrefixMatching)
2309 EnablePrefixMatching = tc.epm
2310
2311 var called *Command
2312 run := func(c *Command, _ []string) { t.Logf("called: %q", c.Name()); called = c }
2313
2314 parent := &Command{Use: "parent", Run: run}
2315 child1 := &Command{Use: "child1", Run: run, Aliases: []string{"this"}}
2316 child2 := &Command{Use: "child2", Run: run, Aliases: []string{"that"}}
2317
2318 parent.AddCommand(child1)
2319 parent.AddCommand(child2)
2320 parent.SetArgs(tc.args)
2321
2322 output := new(bytes.Buffer)
2323 parent.SetOut(output)
2324 parent.SetErr(output)
2325
2326 _ = parent.Execute()
2327
2328 if called == nil {
2329 if tc.call != "" {
2330 t.Errorf("missing expected call to command: %s", tc.call)
2331 }
2332 return
2333 }
2334
2335 if called.Name() != tc.call {
2336 t.Errorf("called command == %q; Wanted %q", called.Name(), tc.call)
2337 } else if got := called.CalledAs(); got != tc.want {
2338 t.Errorf("%s.CalledAs() == %q; Wanted: %q", tc.call, got, tc.want)
2339 }
2340 }
2341
2342 func TestCalledAs(t *testing.T) {
2343 tests := map[string]calledAsTestcase{
2344 "find/no-args": {nil, "parent", "parent", false},
2345 "find/real-name": {[]string{"child1"}, "child1", "child1", false},
2346 "find/full-alias": {[]string{"that"}, "child2", "that", false},
2347 "find/part-no-prefix": {[]string{"thi"}, "", "", false},
2348 "find/part-alias": {[]string{"thi"}, "child1", "this", true},
2349 "find/conflict": {[]string{"th"}, "", "", true},
2350 "traverse/no-args": {nil, "parent", "parent", false},
2351 "traverse/real-name": {[]string{"child1"}, "child1", "child1", false},
2352 "traverse/full-alias": {[]string{"that"}, "child2", "that", false},
2353 "traverse/part-no-prefix": {[]string{"thi"}, "", "", false},
2354 "traverse/part-alias": {[]string{"thi"}, "child1", "this", true},
2355 "traverse/conflict": {[]string{"th"}, "", "", true},
2356 }
2357
2358 for name, tc := range tests {
2359 t.Run(name, tc.test)
2360 }
2361 }
2362
2363 func TestFParseErrWhitelistBackwardCompatibility(t *testing.T) {
2364 c := &Command{Use: "c", Run: emptyRun}
2365 c.Flags().BoolP("boola", "a", false, "a boolean flag")
2366
2367 output, err := executeCommand(c, "c", "-a", "--unknown", "flag")
2368 if err == nil {
2369 t.Error("expected unknown flag error")
2370 }
2371 checkStringContains(t, output, "unknown flag: --unknown")
2372 }
2373
2374 func TestFParseErrWhitelistSameCommand(t *testing.T) {
2375 c := &Command{
2376 Use: "c",
2377 Run: emptyRun,
2378 FParseErrWhitelist: FParseErrWhitelist{
2379 UnknownFlags: true,
2380 },
2381 }
2382 c.Flags().BoolP("boola", "a", false, "a boolean flag")
2383
2384 _, err := executeCommand(c, "c", "-a", "--unknown", "flag")
2385 if err != nil {
2386 t.Error("unexpected error: ", err)
2387 }
2388 }
2389
2390 func TestFParseErrWhitelistParentCommand(t *testing.T) {
2391 root := &Command{
2392 Use: "root",
2393 Run: emptyRun,
2394 FParseErrWhitelist: FParseErrWhitelist{
2395 UnknownFlags: true,
2396 },
2397 }
2398
2399 c := &Command{
2400 Use: "child",
2401 Run: emptyRun,
2402 }
2403 c.Flags().BoolP("boola", "a", false, "a boolean flag")
2404
2405 root.AddCommand(c)
2406
2407 output, err := executeCommand(root, "child", "-a", "--unknown", "flag")
2408 if err == nil {
2409 t.Error("expected unknown flag error")
2410 }
2411 checkStringContains(t, output, "unknown flag: --unknown")
2412 }
2413
2414 func TestFParseErrWhitelistChildCommand(t *testing.T) {
2415 root := &Command{
2416 Use: "root",
2417 Run: emptyRun,
2418 }
2419
2420 c := &Command{
2421 Use: "child",
2422 Run: emptyRun,
2423 FParseErrWhitelist: FParseErrWhitelist{
2424 UnknownFlags: true,
2425 },
2426 }
2427 c.Flags().BoolP("boola", "a", false, "a boolean flag")
2428
2429 root.AddCommand(c)
2430
2431 _, err := executeCommand(root, "child", "-a", "--unknown", "flag")
2432 if err != nil {
2433 t.Error("unexpected error: ", err.Error())
2434 }
2435 }
2436
2437 func TestFParseErrWhitelistSiblingCommand(t *testing.T) {
2438 root := &Command{
2439 Use: "root",
2440 Run: emptyRun,
2441 }
2442
2443 c := &Command{
2444 Use: "child",
2445 Run: emptyRun,
2446 FParseErrWhitelist: FParseErrWhitelist{
2447 UnknownFlags: true,
2448 },
2449 }
2450 c.Flags().BoolP("boola", "a", false, "a boolean flag")
2451
2452 s := &Command{
2453 Use: "sibling",
2454 Run: emptyRun,
2455 }
2456 s.Flags().BoolP("boolb", "b", false, "a boolean flag")
2457
2458 root.AddCommand(c)
2459 root.AddCommand(s)
2460
2461 output, err := executeCommand(root, "sibling", "-b", "--unknown", "flag")
2462 if err == nil {
2463 t.Error("expected unknown flag error")
2464 }
2465 checkStringContains(t, output, "unknown flag: --unknown")
2466 }
2467
2468 func TestSetContext(t *testing.T) {
2469 type key struct{}
2470 val := "foobar"
2471 root := &Command{
2472 Use: "root",
2473 Run: func(cmd *Command, args []string) {
2474 key := cmd.Context().Value(key{})
2475 got, ok := key.(string)
2476 if !ok {
2477 t.Error("key not found in context")
2478 }
2479 if got != val {
2480 t.Errorf("Expected value: \n %v\nGot:\n %v\n", val, got)
2481 }
2482 },
2483 }
2484
2485 ctx := context.WithValue(context.Background(), key{}, val)
2486 root.SetContext(ctx)
2487 err := root.Execute()
2488 if err != nil {
2489 t.Error(err)
2490 }
2491 }
2492
2493 func TestSetContextPreRun(t *testing.T) {
2494 type key struct{}
2495 val := "barr"
2496 root := &Command{
2497 Use: "root",
2498 PreRun: func(cmd *Command, args []string) {
2499 ctx := context.WithValue(cmd.Context(), key{}, val)
2500 cmd.SetContext(ctx)
2501 },
2502 Run: func(cmd *Command, args []string) {
2503 val := cmd.Context().Value(key{})
2504 got, ok := val.(string)
2505 if !ok {
2506 t.Error("key not found in context")
2507 }
2508 if got != val {
2509 t.Errorf("Expected value: \n %v\nGot:\n %v\n", val, got)
2510 }
2511 },
2512 }
2513 err := root.Execute()
2514 if err != nil {
2515 t.Error(err)
2516 }
2517 }
2518
2519 func TestSetContextPreRunOverwrite(t *testing.T) {
2520 type key struct{}
2521 val := "blah"
2522 root := &Command{
2523 Use: "root",
2524 Run: func(cmd *Command, args []string) {
2525 key := cmd.Context().Value(key{})
2526 _, ok := key.(string)
2527 if ok {
2528 t.Error("key found in context when not expected")
2529 }
2530 },
2531 }
2532 ctx := context.WithValue(context.Background(), key{}, val)
2533 root.SetContext(ctx)
2534 err := root.ExecuteContext(context.Background())
2535 if err != nil {
2536 t.Error(err)
2537 }
2538 }
2539
2540 func TestSetContextPersistentPreRun(t *testing.T) {
2541 type key struct{}
2542 val := "barbar"
2543 root := &Command{
2544 Use: "root",
2545 PersistentPreRun: func(cmd *Command, args []string) {
2546 ctx := context.WithValue(cmd.Context(), key{}, val)
2547 cmd.SetContext(ctx)
2548 },
2549 }
2550 child := &Command{
2551 Use: "child",
2552 Run: func(cmd *Command, args []string) {
2553 key := cmd.Context().Value(key{})
2554 got, ok := key.(string)
2555 if !ok {
2556 t.Error("key not found in context")
2557 }
2558 if got != val {
2559 t.Errorf("Expected value: \n %v\nGot:\n %v\n", val, got)
2560 }
2561 },
2562 }
2563 root.AddCommand(child)
2564 root.SetArgs([]string{"child"})
2565 err := root.Execute()
2566 if err != nil {
2567 t.Error(err)
2568 }
2569 }
2570
2571 const VersionFlag = "--version"
2572 const HelpFlag = "--help"
2573
2574 func TestNoRootRunCommandExecutedWithVersionSet(t *testing.T) {
2575 rootCmd := &Command{Use: "root", Version: "1.0.0", Long: "Long description"}
2576 rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
2577
2578 output, err := executeCommand(rootCmd)
2579 if err != nil {
2580 t.Errorf("Unexpected error: %v", err)
2581 }
2582
2583 checkStringContains(t, output, rootCmd.Long)
2584 checkStringContains(t, output, HelpFlag)
2585 checkStringContains(t, output, VersionFlag)
2586 }
2587
2588 func TestNoRootRunCommandExecutedWithoutVersionSet(t *testing.T) {
2589 rootCmd := &Command{Use: "root", Long: "Long description"}
2590 rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
2591
2592 output, err := executeCommand(rootCmd)
2593 if err != nil {
2594 t.Errorf("Unexpected error: %v", err)
2595 }
2596
2597 checkStringContains(t, output, rootCmd.Long)
2598 checkStringContains(t, output, HelpFlag)
2599 checkStringOmits(t, output, VersionFlag)
2600 }
2601
2602 func TestHelpCommandExecutedWithVersionSet(t *testing.T) {
2603 rootCmd := &Command{Use: "root", Version: "1.0.0", Long: "Long description", Run: emptyRun}
2604 rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
2605
2606 output, err := executeCommand(rootCmd, "help")
2607 if err != nil {
2608 t.Errorf("Unexpected error: %v", err)
2609 }
2610
2611 checkStringContains(t, output, rootCmd.Long)
2612 checkStringContains(t, output, HelpFlag)
2613 checkStringContains(t, output, VersionFlag)
2614 }
2615
2616 func TestHelpCommandExecutedWithoutVersionSet(t *testing.T) {
2617 rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
2618 rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
2619
2620 output, err := executeCommand(rootCmd, "help")
2621 if err != nil {
2622 t.Errorf("Unexpected error: %v", err)
2623 }
2624
2625 checkStringContains(t, output, rootCmd.Long)
2626 checkStringContains(t, output, HelpFlag)
2627 checkStringOmits(t, output, VersionFlag)
2628 }
2629
2630 func TestHelpflagCommandExecutedWithVersionSet(t *testing.T) {
2631 rootCmd := &Command{Use: "root", Version: "1.0.0", Long: "Long description", Run: emptyRun}
2632 rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
2633
2634 output, err := executeCommand(rootCmd, HelpFlag)
2635 if err != nil {
2636 t.Errorf("Unexpected error: %v", err)
2637 }
2638
2639 checkStringContains(t, output, rootCmd.Long)
2640 checkStringContains(t, output, HelpFlag)
2641 checkStringContains(t, output, VersionFlag)
2642 }
2643
2644 func TestHelpflagCommandExecutedWithoutVersionSet(t *testing.T) {
2645 rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
2646 rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
2647
2648 output, err := executeCommand(rootCmd, HelpFlag)
2649 if err != nil {
2650 t.Errorf("Unexpected error: %v", err)
2651 }
2652
2653 checkStringContains(t, output, rootCmd.Long)
2654 checkStringContains(t, output, HelpFlag)
2655 checkStringOmits(t, output, VersionFlag)
2656 }
2657
2658 func TestFind(t *testing.T) {
2659 var foo, bar string
2660 root := &Command{
2661 Use: "root",
2662 }
2663 root.PersistentFlags().StringVarP(&foo, "foo", "f", "", "")
2664 root.PersistentFlags().StringVarP(&bar, "bar", "b", "something", "")
2665
2666 child := &Command{
2667 Use: "child",
2668 }
2669 root.AddCommand(child)
2670
2671 testCases := []struct {
2672 args []string
2673 expectedFoundArgs []string
2674 }{
2675 {
2676 []string{"child"},
2677 []string{},
2678 },
2679 {
2680 []string{"child", "child"},
2681 []string{"child"},
2682 },
2683 {
2684 []string{"child", "foo", "child", "bar", "child", "baz", "child"},
2685 []string{"foo", "child", "bar", "child", "baz", "child"},
2686 },
2687 {
2688 []string{"-f", "child", "child"},
2689 []string{"-f", "child"},
2690 },
2691 {
2692 []string{"child", "-f", "child"},
2693 []string{"-f", "child"},
2694 },
2695 {
2696 []string{"-b", "child", "child"},
2697 []string{"-b", "child"},
2698 },
2699 {
2700 []string{"child", "-b", "child"},
2701 []string{"-b", "child"},
2702 },
2703 {
2704 []string{"child", "-b"},
2705 []string{"-b"},
2706 },
2707 {
2708 []string{"-b", "-f", "child", "child"},
2709 []string{"-b", "-f", "child"},
2710 },
2711 {
2712 []string{"-f", "child", "-b", "something", "child"},
2713 []string{"-f", "child", "-b", "something"},
2714 },
2715 {
2716 []string{"-f", "child", "child", "-b"},
2717 []string{"-f", "child", "-b"},
2718 },
2719 {
2720 []string{"-f=child", "-b=something", "child"},
2721 []string{"-f=child", "-b=something"},
2722 },
2723 {
2724 []string{"--foo", "child", "--bar", "something", "child"},
2725 []string{"--foo", "child", "--bar", "something"},
2726 },
2727 }
2728
2729 for _, tc := range testCases {
2730 t.Run(fmt.Sprintf("%v", tc.args), func(t *testing.T) {
2731 cmd, foundArgs, err := root.Find(tc.args)
2732 if err != nil {
2733 t.Fatal(err)
2734 }
2735
2736 if cmd != child {
2737 t.Fatal("Expected cmd to be child, but it was not")
2738 }
2739
2740 if !reflect.DeepEqual(tc.expectedFoundArgs, foundArgs) {
2741 t.Fatalf("Wrong args\nExpected: %v\nGot: %v", tc.expectedFoundArgs, foundArgs)
2742 }
2743 })
2744 }
2745 }
2746
2747 func TestUnknownFlagShouldReturnSameErrorRegardlessOfArgPosition(t *testing.T) {
2748 testCases := [][]string{
2749
2750 {"--namespace", "foo", "--unknown", "child", "--bar"},
2751 {"--namespace", "foo", "child", "--unknown", "--bar"},
2752 {"--namespace", "foo", "child", "--bar", "--unknown"},
2753
2754 {"--unknown", "--namespace=foo", "child", "--bar"},
2755 {"--namespace=foo", "--unknown", "child", "--bar"},
2756 {"--namespace=foo", "child", "--unknown", "--bar"},
2757 {"--namespace=foo", "child", "--bar", "--unknown"},
2758
2759 {"--unknown", "--namespace=foo", "child", "--bar=true"},
2760 {"--namespace=foo", "--unknown", "child", "--bar=true"},
2761 {"--namespace=foo", "child", "--unknown", "--bar=true"},
2762 {"--namespace=foo", "child", "--bar=true", "--unknown"},
2763 }
2764
2765 root := &Command{
2766 Use: "root",
2767 Run: emptyRun,
2768 }
2769 root.PersistentFlags().String("namespace", "", "a string flag")
2770
2771 c := &Command{
2772 Use: "child",
2773 Run: emptyRun,
2774 }
2775 c.Flags().Bool("bar", false, "a boolean flag")
2776
2777 root.AddCommand(c)
2778
2779 for _, tc := range testCases {
2780 t.Run(strings.Join(tc, " "), func(t *testing.T) {
2781 output, err := executeCommand(root, tc...)
2782 if err == nil {
2783 t.Error("expected unknown flag error")
2784 }
2785 checkStringContains(t, output, "unknown flag: --unknown")
2786 })
2787 }
2788 }
2789
View as plain text