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