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