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