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