package cobra import ( "bytes" "fmt" "os" "reflect" "runtime" "strings" "testing" "text/template" "github.com/spf13/pflag" ) var tp, te, tt, tr []string var rootPersPre, echoPre, echoPersPre, timesPersPre []string var flagb1, flagb2, flagb3, flagbr, flagbp bool var flags1, flags2a, flags2b, flags3, outs string var flagi1, flagi2, flagi3, flagi4, flagir int var rootcalled bool var versionUsed int const strtwoParentHelp = "help message for parent flag strtwo" const strtwoChildHelp = "help message for child flag strtwo" var cmdHidden = &Command{ Use: "hide [secret string to print]", Short: "Print anything to screen (if command is known)", Long: `an absolutely utterly useless command for testing.`, Run: func(cmd *Command, args []string) { outs = "hidden" }, Hidden: true, } var cmdPrint = &Command{ Use: "print [string to print]", Args: MinimumNArgs(1), Short: "Print anything to the screen", Long: `an absolutely utterly useless command for testing.`, Run: func(cmd *Command, args []string) { tp = args }, } var cmdEcho = &Command{ Use: "echo [string to echo]", Aliases: []string{"say"}, Short: "Echo anything to the screen", Long: `an utterly useless command for testing.`, Example: "Just run cobra-test echo", PersistentPreRun: func(cmd *Command, args []string) { echoPersPre = args }, PreRun: func(cmd *Command, args []string) { echoPre = args }, Run: func(cmd *Command, args []string) { te = args }, } var cmdEchoSub = &Command{ Use: "echosub [string to print]", Short: "second sub command for echo", Long: `an absolutely utterly useless command for testing gendocs!.`, Run: func(cmd *Command, args []string) { }, } var cmdDeprecated = &Command{ Use: "deprecated [can't do anything here]", Short: "A command which is deprecated", Long: `an absolutely utterly useless command for testing deprecation!.`, Deprecated: "Please use echo instead", Run: func(cmd *Command, args []string) { }, Args: NoArgs, } var cmdTimes = &Command{ Use: "times [# times] [string to echo]", SuggestFor: []string{"counts"}, Short: "Echo anything to the screen more times", Long: `a slightly useless command for testing.`, PersistentPreRun: func(cmd *Command, args []string) { timesPersPre = args }, Run: func(cmd *Command, args []string) { tt = args }, Args: OnlyValidArgs, ValidArgs: []string{"one", "two", "three", "four"}, } var cmdRootNoRun = &Command{ Use: "cobra-test", Short: "The root can run its own function", Long: "The root description for help", PersistentPreRun: func(cmd *Command, args []string) { rootPersPre = args }, } var cmdRootSameName = &Command{ Use: "print", Short: "Root with the same name as a subcommand", Long: "The root description for help", } var cmdRootTakesArgs = &Command{ Use: "root-with-args [random args]", Short: "The root can run it's own function and takes args!", Long: "The root description for help, and some args", Run: func(cmd *Command, args []string) { tr = args }, Args: ArbitraryArgs, } var cmdRootWithRun = &Command{ Use: "cobra-test", Short: "The root can run its own function", Long: "The root description for help", Run: func(cmd *Command, args []string) { tr = args rootcalled = true }, } var cmdSubNoRun = &Command{ Use: "subnorun", Short: "A subcommand without a Run function", Long: "A long output about a subcommand without a Run function", } var cmdCustomFlags = &Command{ Use: "customflags [flags] -- REMOTE_COMMAND", Short: "A command that expects flags in a custom location", Long: "A long output about a command that expects flags in a custom location", Run: func(cmd *Command, args []string) { }, } var cmdVersion1 = &Command{ Use: "version", Short: "Print the version number", Long: `First version of the version command`, Run: func(cmd *Command, args []string) { versionUsed = 1 }, } var cmdVersion2 = &Command{ Use: "version", Short: "Print the version number", Long: `Second version of the version command`, Run: func(cmd *Command, args []string) { versionUsed = 2 }, } var cmdColon = &Command{ Use: "cmd:colon", Run: func(cmd *Command, args []string) { }, } func flagInit() { cmdEcho.ResetFlags() cmdPrint.ResetFlags() cmdTimes.ResetFlags() cmdRootNoRun.ResetFlags() cmdRootSameName.ResetFlags() cmdRootWithRun.ResetFlags() cmdSubNoRun.ResetFlags() cmdCustomFlags.ResetFlags() cmdVersion1.ResetFlags() cmdVersion2.ResetFlags() cmdRootNoRun.PersistentFlags().StringVarP(&flags2a, "strtwo", "t", "two", strtwoParentHelp) cmdCustomFlags.Flags().IntVar(&flagi4, "intfour", 456, "help message for flag intfour") cmdEcho.Flags().BoolVarP(&flagb1, "boolone", "b", true, "help message for flag boolone") cmdEcho.Flags().IntVarP(&flagi1, "intone", "i", 123, "help message for flag intone") cmdEcho.PersistentFlags().BoolVarP(&flagbp, "persistentbool", "p", false, "help message for flag persistentbool") cmdEcho.PersistentFlags().StringVarP(&flags1, "strone", "s", "one", "help message for flag strone") cmdPrint.Flags().IntVarP(&flagi3, "intthree", "i", 345, "help message for flag intthree") cmdTimes.Flags().BoolVarP(&flagb2, "booltwo", "c", false, "help message for flag booltwo") cmdTimes.Flags().IntVarP(&flagi2, "inttwo", "j", 234, "help message for flag inttwo") cmdTimes.Flags().StringVarP(&flags2b, "strtwo", "t", "2", strtwoChildHelp) cmdTimes.PersistentFlags().StringVarP(&flags2b, "strtwo", "t", "2", strtwoChildHelp) cmdTimes.LocalFlags() // populate lflags before parent is set cmdPrint.Flags().BoolVarP(&flagb3, "boolthree", "b", true, "help message for flag boolthree") cmdPrint.PersistentFlags().StringVarP(&flags3, "strthree", "s", "three", "help message for flag strthree") } func commandInit() { cmdEcho.ResetCommands() cmdPrint.ResetCommands() cmdTimes.ResetCommands() cmdRootNoRun.ResetCommands() cmdRootSameName.ResetCommands() cmdRootWithRun.ResetCommands() cmdSubNoRun.ResetCommands() cmdCustomFlags.ResetCommands() } func initialize() *Command { tt, tp, te = nil, nil, nil rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil var c = cmdRootNoRun commandInit() flagInit() return c } func initializeWithSameName() *Command { tt, tp, te = nil, nil, nil rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil var c = cmdRootSameName commandInit() flagInit() return c } func initializeWithRootCmd() *Command { cmdRootWithRun.ResetCommands() tt, tp, te, tr, rootcalled = nil, nil, nil, nil, false flagInit() cmdRootWithRun.Flags().BoolVarP(&flagbr, "boolroot", "b", false, "help message for flag boolroot") cmdRootWithRun.Flags().IntVarP(&flagir, "introot", "i", 321, "help message for flag introot") commandInit() return cmdRootWithRun } type resulter struct { Error error Output string Command *Command } func fullSetupTest(args ...string) resulter { c := initializeWithRootCmd() return fullTester(c, args...) } func noRRSetupTestSilenced(args ...string) resulter { c := initialize() c.SilenceErrors = true c.SilenceUsage = true return fullTester(c, args...) } func noRRSetupTest(args ...string) resulter { c := initialize() return fullTester(c, args...) } func rootOnlySetupTest(args ...string) resulter { c := initializeWithRootCmd() return simpleTester(c, args...) } func simpleTester(c *Command, args ...string) resulter { buf := new(bytes.Buffer) // Testing flag with invalid input c.SetOutput(buf) c.SetArgs(args) err := c.Execute() output := buf.String() return resulter{err, output, c} } func simpleTesterC(c *Command, args ...string) resulter { buf := new(bytes.Buffer) // Testing flag with invalid input c.SetOutput(buf) c.SetArgs(args) cmd, err := c.ExecuteC() output := buf.String() return resulter{err, output, cmd} } func fullTester(c *Command, args ...string) resulter { buf := new(bytes.Buffer) // Testing flag with invalid input c.SetOutput(buf) cmdEcho.AddCommand(cmdTimes) c.AddCommand(cmdPrint, cmdEcho, cmdSubNoRun, cmdCustomFlags, cmdDeprecated) c.SetArgs(args) err := c.Execute() output := buf.String() return resulter{err, output, c} } func logErr(t *testing.T, found, expected string) { out := new(bytes.Buffer) _, _, line, ok := runtime.Caller(2) if ok { fmt.Fprintf(out, "Line: %d ", line) } fmt.Fprintf(out, "Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found) t.Errorf(out.String()) } func checkStringContains(t *testing.T, found, expected string) { if !strings.Contains(found, expected) { logErr(t, found, expected) } } func checkResultContains(t *testing.T, x resulter, check string) { checkStringContains(t, x.Output, check) } func checkStringOmits(t *testing.T, found, expected string) { if strings.Contains(found, expected) { logErr(t, found, expected) } } func checkResultOmits(t *testing.T, x resulter, check string) { checkStringOmits(t, x.Output, check) } func checkOutputContains(t *testing.T, c *Command, check string) { buf := new(bytes.Buffer) c.SetOutput(buf) c.Execute() if !strings.Contains(buf.String(), check) { logErr(t, buf.String(), check) } } func TestSingleCommand(t *testing.T) { noRRSetupTest("print", "one", "two") if te != nil || tt != nil { t.Error("Wrong command called") } if tp == nil { t.Error("Wrong command called") } if strings.Join(tp, " ") != "one two" { t.Error("Command didn't parse correctly") } } func TestChildCommand(t *testing.T) { noRRSetupTest("echo", "times", "one", "two") if te != nil || tp != nil { t.Error("Wrong command called") } if tt == nil { t.Error("Wrong command called") } if strings.Join(tt, " ") != "one two" { t.Error("Command didn't parse correctly") } } func TestCommandAlias(t *testing.T) { noRRSetupTest("say", "times", "one", "two") if te != nil || tp != nil { t.Error("Wrong command called") } if tt == nil { t.Error("Wrong command called") } if strings.Join(tt, " ") != "one two" { t.Error("Command didn't parse correctly") } } func TestPrefixMatching(t *testing.T) { EnablePrefixMatching = true noRRSetupTest("ech", "times", "one", "two") if te != nil || tp != nil { t.Error("Wrong command called") } if tt == nil { t.Error("Wrong command called") } if strings.Join(tt, " ") != "one two" { t.Error("Command didn't parse correctly") } EnablePrefixMatching = false } func TestNoPrefixMatching(t *testing.T) { EnablePrefixMatching = false noRRSetupTest("ech", "times", "one", "two") if !(tt == nil && te == nil && tp == nil) { t.Error("Wrong command called") } } func TestAliasPrefixMatching(t *testing.T) { EnablePrefixMatching = true noRRSetupTest("sa", "times", "one", "two") if te != nil || tp != nil { t.Error("Wrong command called") } if tt == nil { t.Error("Wrong command called") } if strings.Join(tt, " ") != "one two" { t.Error("Command didn't parse correctly") } EnablePrefixMatching = false } func TestChildSameName(t *testing.T) { c := initializeWithSameName() c.AddCommand(cmdPrint, cmdEcho) c.SetArgs([]string{"print", "one", "two"}) c.Execute() if te != nil || tt != nil { t.Error("Wrong command called") } if tp == nil { t.Error("Wrong command called") } if strings.Join(tp, " ") != "one two" { t.Error("Command didn't parse correctly") } } func TestGrandChildSameName(t *testing.T) { c := initializeWithSameName() cmdTimes.AddCommand(cmdPrint) c.AddCommand(cmdTimes) c.SetArgs([]string{"times", "print", "one", "two"}) c.Execute() if te != nil || tt != nil { t.Error("Wrong command called") } if tp == nil { t.Error("Wrong command called") } if strings.Join(tp, " ") != "one two" { t.Error("Command didn't parse correctly") } } func TestUsage(t *testing.T) { x := fullSetupTest("help") checkResultContains(t, x, cmdRootWithRun.Use+" [flags]") x = fullSetupTest("help", "customflags") checkResultContains(t, x, cmdCustomFlags.Use) checkResultOmits(t, x, cmdCustomFlags.Use+" [flags]") } func TestRootTakesNoArgs(t *testing.T) { c := initializeWithSameName() c.AddCommand(cmdPrint, cmdEcho) result := simpleTester(c, "illegal") if result.Error == nil { t.Fatal("Expected an error") } expectedError := `unknown command "illegal" for "print"` if !strings.Contains(result.Error.Error(), expectedError) { t.Errorf("exptected %v, got %v", expectedError, result.Error.Error()) } } func TestRootTakesArgs(t *testing.T) { c := cmdRootTakesArgs result := simpleTester(c, "legal") if result.Error != nil { t.Errorf("expected no error, but got %v", result.Error) } } func TestSubCmdTakesNoArgs(t *testing.T) { result := fullSetupTest("deprecated", "illegal") if result.Error == nil { t.Fatal("Expected an error") } expectedError := `unknown command "illegal" for "cobra-test deprecated"` if !strings.Contains(result.Error.Error(), expectedError) { t.Errorf("expected %v, got %v", expectedError, result.Error.Error()) } } func TestSubCmdTakesArgs(t *testing.T) { noRRSetupTest("echo", "times", "one", "two") if strings.Join(tt, " ") != "one two" { t.Error("Command didn't parse correctly") } } func TestCmdOnlyValidArgs(t *testing.T) { result := noRRSetupTest("echo", "times", "one", "two", "five") if result.Error == nil { t.Fatal("Expected an error") } expectedError := `invalid argument "five"` if !strings.Contains(result.Error.Error(), expectedError) { t.Errorf("expected %v, got %v", expectedError, result.Error.Error()) } } func TestFlagLong(t *testing.T) { noRRSetupTest("echo", "--intone=13", "something", "--", "here") if cmdEcho.ArgsLenAtDash() != 1 { t.Errorf("expected argsLenAtDash: %d but got %d", 1, cmdRootNoRun.ArgsLenAtDash()) } if strings.Join(te, " ") != "something here" { t.Errorf("flags didn't leave proper args remaining..%s given", te) } if flagi1 != 13 { t.Errorf("int flag didn't get correct value, had %d", flagi1) } if flagi2 != 234 { t.Errorf("default flag value changed, 234 expected, %d given", flagi2) } } func TestFlagShort(t *testing.T) { noRRSetupTest("echo", "-i13", "--", "something", "here") if cmdEcho.ArgsLenAtDash() != 0 { t.Errorf("expected argsLenAtDash: %d but got %d", 0, cmdRootNoRun.ArgsLenAtDash()) } if strings.Join(te, " ") != "something here" { t.Errorf("flags didn't leave proper args remaining..%s given", te) } if flagi1 != 13 { t.Errorf("int flag didn't get correct value, had %d", flagi1) } if flagi2 != 234 { t.Errorf("default flag value changed, 234 expected, %d given", flagi2) } noRRSetupTest("echo", "-i", "13", "something", "here") if strings.Join(te, " ") != "something here" { t.Errorf("flags didn't leave proper args remaining..%s given", te) } if flagi1 != 13 { t.Errorf("int flag didn't get correct value, had %d", flagi1) } if flagi2 != 234 { t.Errorf("default flag value changed, 234 expected, %d given", flagi2) } noRRSetupTest("print", "-i99", "one", "two") if strings.Join(tp, " ") != "one two" { t.Errorf("flags didn't leave proper args remaining..%s given", tp) } if flagi3 != 99 { t.Errorf("int flag didn't get correct value, had %d", flagi3) } if flagi1 != 123 { t.Errorf("default flag value changed on different command with same shortname, 234 expected, %d given", flagi2) } } func TestChildCommandFlags(t *testing.T) { noRRSetupTest("echo", "times", "-j", "99", "one", "two") if strings.Join(tt, " ") != "one two" { t.Errorf("flags didn't leave proper args remaining..%s given", tt) } // Testing with flag that shouldn't be persistent r := noRRSetupTest("echo", "times", "-j", "99", "-i77", "one", "two") if r.Error == nil { t.Errorf("invalid flag should generate error") } if !strings.Contains(r.Error.Error(), "unknown shorthand") { t.Errorf("Wrong error message displayed, \n %s", r.Error) } if flagi2 != 99 { t.Errorf("flag value should be 99, %d given", flagi2) } if flagi1 != 123 { t.Errorf("unset flag should have default value, expecting 123, given %d", flagi1) } // Testing with flag only existing on child r = noRRSetupTest("echo", "-j", "99", "-i77", "one", "two") if r.Error == nil { t.Errorf("invalid flag should generate error") } if !strings.Contains(r.Error.Error(), "unknown shorthand flag") { t.Errorf("Wrong error message displayed, \n %s", r.Error) } // Testing with persistent flag overwritten by child noRRSetupTest("echo", "times", "--strtwo=child", "one", "two") if flags2b != "child" { t.Errorf("flag value should be child, %s given", flags2b) } if flags2a != "two" { t.Errorf("unset flag should have default value, expecting two, given %s", flags2a) } // Testing flag with invalid input r = noRRSetupTest("echo", "-i10E") if r.Error == nil { t.Errorf("invalid input should generate error") } if !strings.Contains(r.Error.Error(), "invalid syntax") { t.Errorf("Wrong error message displayed, \n %s", r.Error) } } func TestTrailingCommandFlags(t *testing.T) { x := fullSetupTest("echo", "two", "-x") if x.Error == nil { t.Errorf("invalid flag should generate error") } } func TestInvalidSubcommandFlags(t *testing.T) { cmd := initializeWithRootCmd() cmd.AddCommand(cmdTimes) result := simpleTester(cmd, "times", "--inttwo=2", "--badflag=bar") // given that we are not checking here result.Error we check for // stock usage message checkResultContains(t, result, "cobra-test times [# times]") if strings.Contains(result.Error.Error(), "unknown flag: --inttwo") { t.Errorf("invalid --badflag flag shouldn't fail on 'unknown' --inttwo flag") } } func TestSubcommandExecuteC(t *testing.T) { cmd := initializeWithRootCmd() double := &Command{ Use: "double message", Run: func(c *Command, args []string) { msg := strings.Join(args, " ") c.Println(msg, msg) }, } echo := &Command{ Use: "echo message", Run: func(c *Command, args []string) { msg := strings.Join(args, " ") c.Println(msg) }, } cmd.AddCommand(double, echo) result := simpleTesterC(cmd, "double", "hello", "world") checkResultContains(t, result, "hello world hello world") if result.Command.Name() != "double" { t.Errorf("invalid cmd returned from ExecuteC: should be 'double' but got %s", result.Command.Name()) } result = simpleTesterC(cmd, "echo", "msg", "to", "be", "echoed") checkResultContains(t, result, "msg to be echoed") if result.Command.Name() != "echo" { t.Errorf("invalid cmd returned from ExecuteC: should be 'echo' but got %s", result.Command.Name()) } } func TestSubcommandArgEvaluation(t *testing.T) { cmd := initializeWithRootCmd() first := &Command{ Use: "first", Run: func(cmd *Command, args []string) { }, } cmd.AddCommand(first) second := &Command{ Use: "second", Run: func(cmd *Command, args []string) { fmt.Fprintf(cmd.OutOrStdout(), "%v", args) }, } first.AddCommand(second) result := simpleTester(cmd, "first", "second", "first", "third") expectedOutput := fmt.Sprint([]string{"first third"}) if result.Output != expectedOutput { t.Errorf("exptected %v, got %v", expectedOutput, result.Output) } } func TestPersistentFlags(t *testing.T) { fullSetupTest("echo", "-s", "something", "-p", "more", "here") // persistentFlag should act like normal flag on its own command if strings.Join(te, " ") != "more here" { t.Errorf("flags didn't leave proper args remaining..%s given", te) } if flags1 != "something" { t.Errorf("string flag didn't get correct value, had %v", flags1) } if !flagbp { t.Errorf("persistent bool flag not parsed correctly. Expected true, had %v", flagbp) } // persistentFlag should act like normal flag on its own command fullSetupTest("echo", "times", "-s", "again", "-c", "-p", "one", "two") if strings.Join(tt, " ") != "one two" { t.Errorf("flags didn't leave proper args remaining. %s given", tt) } if flags1 != "again" { t.Errorf("string flag didn't get correct value, had %v", flags1) } if !flagb2 { t.Errorf("local flag not parsed correctly. Expected true, had %v", flagb2) } if !flagbp { t.Errorf("persistent bool flag not parsed correctly. Expected true, had %v", flagbp) } } func TestHelpCommand(t *testing.T) { x := fullSetupTest("help") checkResultContains(t, x, cmdRootWithRun.Long) x = fullSetupTest("help", "echo") checkResultContains(t, x, cmdEcho.Long) x = fullSetupTest("help", "echo", "times") checkResultContains(t, x, cmdTimes.Long) } func TestChildCommandHelp(t *testing.T) { c := noRRSetupTest("print", "--help") checkResultContains(t, c, strtwoParentHelp) r := noRRSetupTest("echo", "times", "--help") checkResultContains(t, r, strtwoChildHelp) } func TestNonRunChildHelp(t *testing.T) { x := noRRSetupTest("subnorun") checkResultContains(t, x, cmdSubNoRun.Long) } func TestRunnableRootCommand(t *testing.T) { x := fullSetupTest("") if !rootcalled { t.Errorf("Root Function was not called\n out:%v", x.Error) } } func TestVisitParents(t *testing.T) { c := &Command{Use: "app"} sub := &Command{Use: "sub"} dsub := &Command{Use: "dsub"} sub.AddCommand(dsub) c.AddCommand(sub) total := 0 add := func(x *Command) { total++ } sub.VisitParents(add) if total != 1 { t.Errorf("Should have visited 1 parent but visited %d", total) } total = 0 dsub.VisitParents(add) if total != 2 { t.Errorf("Should have visited 2 parent but visited %d", total) } total = 0 c.VisitParents(add) if total != 0 { t.Errorf("Should have not visited any parent but visited %d", total) } } func TestRunnableRootCommandNilInput(t *testing.T) { c := initializeWithRootCmd() buf := new(bytes.Buffer) // Testing flag with invalid input c.SetOutput(buf) cmdEcho.AddCommand(cmdTimes) c.AddCommand(cmdPrint, cmdEcho) c.SetArgs([]string{}) err := c.Execute() if err != nil { t.Errorf("Execute() failed with %v", err) } if !rootcalled { t.Errorf("Root Function was not called") } } func TestRunnableRootCommandEmptyInput(t *testing.T) { args := []string{"", "--introot=12", ""} c := initializeWithRootCmd() buf := new(bytes.Buffer) // Testing flag with invalid input c.SetOutput(buf) cmdEcho.AddCommand(cmdTimes) c.AddCommand(cmdPrint, cmdEcho) c.SetArgs(args) c.Execute() if !rootcalled { t.Errorf("Root Function was not called.\nOutput was:\n%s\n", buf) } } func TestInvalidSubcommandWhenArgsAllowed(t *testing.T) { fullSetupTest("echo", "invalid-sub") if te[0] != "invalid-sub" { t.Errorf("Subcommand didn't work...") } } func TestRootFlags(t *testing.T) { fullSetupTest("-i", "17", "-b") if !flagbr { t.Errorf("flag value should be true, %v given", flagbr) } if flagir != 17 { t.Errorf("flag value should be 17, %d given", flagir) } } func TestRootHelp(t *testing.T) { x := fullSetupTest("--help") checkResultContains(t, x, "Available Commands:") checkResultContains(t, x, "for more information about a command") if strings.Contains(x.Output, "unknown flag: --help") { t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) } if strings.Contains(x.Output, cmdEcho.Use) { t.Errorf("--help shouldn't display subcommand's usage, Got: \n %s", x.Output) } x = fullSetupTest("echo", "--help") if strings.Contains(x.Output, cmdTimes.Use) { t.Errorf("--help shouldn't display subsubcommand's usage, Got: \n %s", x.Output) } checkResultContains(t, x, "Available Commands:") checkResultContains(t, x, "for more information about a command") if strings.Contains(x.Output, "unknown flag: --help") { t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) } } func TestFlagAccess(t *testing.T) { initialize() cmdEcho.AddCommand(cmdTimes) local := cmdTimes.LocalFlags() inherited := cmdTimes.InheritedFlags() for _, f := range []string{"inttwo", "strtwo", "booltwo"} { if local.Lookup(f) == nil { t.Errorf("LocalFlags expected to contain %s, Got: nil", f) } } if inherited.Lookup("strone") == nil { t.Errorf("InheritedFlags expected to contain strone, Got: nil") } if inherited.Lookup("strtwo") != nil { t.Errorf("InheritedFlags shouldn not contain overwritten flag strtwo") } } func TestNoNRunnableRootCommandNilInput(t *testing.T) { c := initialize() buf := new(bytes.Buffer) // Testing flag with invalid input c.SetOutput(buf) cmdEcho.AddCommand(cmdTimes) c.AddCommand(cmdPrint, cmdEcho) c.SetArgs([]string{}) c.Execute() if !strings.Contains(buf.String(), cmdRootNoRun.Long) { t.Errorf("Expected to get help output, Got: \n %s", buf) } } func TestRootNoCommandHelp(t *testing.T) { x := rootOnlySetupTest("--help") checkResultOmits(t, x, "Available Commands:") checkResultOmits(t, x, "for more information about a command") if strings.Contains(x.Output, "unknown flag: --help") { t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) } x = rootOnlySetupTest("echo", "--help") checkResultOmits(t, x, "Available Commands:") checkResultOmits(t, x, "for more information about a command") if strings.Contains(x.Output, "unknown flag: --help") { t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) } } func TestRootUnknownCommand(t *testing.T) { r := noRRSetupTest("bogus") s := "Error: unknown command \"bogus\" for \"cobra-test\"\nRun 'cobra-test --help' for usage.\n" if r.Output != s { t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", s, r.Output) } r = noRRSetupTest("--strtwo=a", "bogus") if r.Output != s { t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", s, r.Output) } } func TestRootUnknownCommandSilenced(t *testing.T) { r := noRRSetupTestSilenced("bogus") if r.Output != "" { t.Errorf("Unexpected response.\nExpecting to be: \n\"\"\n Got:\n %q\n", r.Output) } r = noRRSetupTestSilenced("--strtwo=a", "bogus") if r.Output != "" { t.Errorf("Unexpected response.\nExpecting to be:\n\"\"\nGot:\n %q\n", r.Output) } } func TestRootSuggestions(t *testing.T) { outputWithSuggestions := "Error: unknown command \"%s\" for \"cobra-test\"\n\nDid you mean this?\n\t%s\n\nRun 'cobra-test --help' for usage.\n" outputWithoutSuggestions := "Error: unknown command \"%s\" for \"cobra-test\"\nRun 'cobra-test --help' for usage.\n" cmd := initializeWithRootCmd() cmd.AddCommand(cmdTimes) tests := map[string]string{ "time": "times", "tiems": "times", "tims": "times", "timeS": "times", "rimes": "times", "ti": "times", "t": "times", "timely": "times", "ri": "", "timezone": "", "foo": "", "counts": "times", } for typo, suggestion := range tests { for _, suggestionsDisabled := range []bool{false, true} { cmd.DisableSuggestions = suggestionsDisabled result := simpleTester(cmd, typo) expected := "" if len(suggestion) == 0 || suggestionsDisabled { expected = fmt.Sprintf(outputWithoutSuggestions, typo) } else { expected = fmt.Sprintf(outputWithSuggestions, typo, suggestion) } if result.Output != expected { t.Errorf("Unexpected response.\nExpecting to be:\n %q\nGot:\n %q\n", expected, result.Output) } } } } func TestFlagsBeforeCommand(t *testing.T) { // short without space x := fullSetupTest("-i10", "echo") if x.Error != nil { t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error) } x = noRRSetupTest("echo", "-i=10") if x.Error != nil { t.Errorf("Valid Input shouldn't have errors, got:\n %s", x.Error) } // long with equals x = noRRSetupTest("--intone=123", "echo", "one", "two") if x.Error != nil { t.Errorf("Valid Input shouldn't have errors, got:\n %s", x.Error) } // With parsing error properly reported x = fullSetupTest("-i10E", "echo") if !strings.Contains(x.Error.Error(), "invalid syntax") { t.Errorf("Wrong error message displayed, \n %s", x.Error) } } func TestRemoveCommand(t *testing.T) { versionUsed = 0 c := initializeWithRootCmd() c.AddCommand(cmdVersion1) c.RemoveCommand(cmdVersion1) x := fullTester(c, "version") if x.Error == nil { t.Errorf("Removed command should not have been called\n") return } } func TestCommandWithoutSubcommands(t *testing.T) { c := initializeWithRootCmd() x := simpleTester(c, "") if x.Error != nil { t.Errorf("Calling command without subcommands should not have error: %v", x.Error) return } } func TestCommandWithoutSubcommandsWithArg(t *testing.T) { c := initializeWithRootCmd() expectedArgs := []string{"arg"} x := simpleTester(c, "arg") if x.Error != nil { t.Errorf("Calling command without subcommands but with arg should not have error: %v", x.Error) return } if !reflect.DeepEqual(expectedArgs, tr) { t.Errorf("Calling command without subcommands but with arg has wrong args: expected: %v, actual: %v", expectedArgs, tr) return } } func TestReplaceCommandWithRemove(t *testing.T) { versionUsed = 0 c := initializeWithRootCmd() c.AddCommand(cmdVersion1) c.RemoveCommand(cmdVersion1) c.AddCommand(cmdVersion2) x := fullTester(c, "version") if x.Error != nil { t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error) return } if versionUsed == 1 { t.Errorf("Removed command shouldn't be called\n") } if versionUsed != 2 { t.Errorf("Replacing command should have been called but didn't\n") } } func TestDeprecatedSub(t *testing.T) { c := fullSetupTest("deprecated") checkResultContains(t, c, cmdDeprecated.Deprecated) } func TestPreRun(t *testing.T) { noRRSetupTest("echo", "one", "two") if echoPre == nil || echoPersPre == nil { t.Error("PreRun or PersistentPreRun not called") } if rootPersPre != nil || timesPersPre != nil { t.Error("Wrong *Pre functions called!") } noRRSetupTest("echo", "times", "one", "two") if timesPersPre == nil { t.Error("PreRun or PersistentPreRun not called") } if echoPre != nil || echoPersPre != nil || rootPersPre != nil { t.Error("Wrong *Pre functions called!") } noRRSetupTest("print", "one", "two") if rootPersPre == nil { t.Error("Parent PersistentPreRun not called but should not have been") } if echoPre != nil || echoPersPre != nil || timesPersPre != nil { t.Error("Wrong *Pre functions called!") } } // Check if cmdEchoSub gets PersistentPreRun from rootCmd even if is added last func TestPeristentPreRunPropagation(t *testing.T) { rootCmd := initialize() // First add the cmdEchoSub to cmdPrint cmdPrint.AddCommand(cmdEchoSub) // Now add cmdPrint to rootCmd rootCmd.AddCommand(cmdPrint) rootCmd.SetArgs([]string{"print", "echosub", "lala"}) rootCmd.Execute() if len(rootPersPre) == 0 || rootPersPre[0] != "lala" { t.Error("RootCmd PersistentPreRun not called but should have been") } } func TestGlobalNormFuncPropagation(t *testing.T) { normFunc := func(f *pflag.FlagSet, name string) pflag.NormalizedName { return pflag.NormalizedName(name) } rootCmd := initialize() rootCmd.AddCommand(cmdEcho) rootCmd.SetGlobalNormalizationFunc(normFunc) if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() { t.Error("rootCmd seems to have a wrong normalization function") } // Also check it propagates retroactively if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(cmdEcho.GlobalNormalizationFunc()).Pointer() { t.Error("cmdEcho should have had the normalization function of rootCmd") } // First add the cmdEchoSub to cmdPrint cmdPrint.AddCommand(cmdEchoSub) if cmdPrint.GlobalNormalizationFunc() != nil && cmdEchoSub.GlobalNormalizationFunc() != nil { t.Error("cmdPrint and cmdEchoSub should had no normalization functions") } // Now add cmdPrint to rootCmd rootCmd.AddCommand(cmdPrint) if reflect.ValueOf(cmdPrint.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() || reflect.ValueOf(cmdEchoSub.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() { t.Error("cmdPrint and cmdEchoSub should had the normalization function of rootCmd") } } func TestNormPassedOnLocal(t *testing.T) { n := func(f *pflag.FlagSet, name string) pflag.NormalizedName { return pflag.NormalizedName(strings.ToUpper(name)) } cmd := &Command{} flagVal := false cmd.Flags().BoolVar(&flagVal, "flagname", true, "this is a dummy flag") cmd.SetGlobalNormalizationFunc(n) if cmd.LocalFlags().Lookup("flagname") != cmd.LocalFlags().Lookup("FLAGNAME") { t.Error("Normalization function should be passed on to Local flag set") } } func TestNormPassedOnInherited(t *testing.T) { n := func(f *pflag.FlagSet, name string) pflag.NormalizedName { return pflag.NormalizedName(strings.ToUpper(name)) } cmd, childBefore, childAfter := &Command{}, &Command{}, &Command{} flagVal := false cmd.AddCommand(childBefore) cmd.PersistentFlags().BoolVar(&flagVal, "flagname", true, "this is a dummy flag") cmd.SetGlobalNormalizationFunc(n) cmd.AddCommand(childAfter) if f := childBefore.InheritedFlags(); f.Lookup("flagname") == nil || f.Lookup("flagname") != f.Lookup("FLAGNAME") { t.Error("Normalization function should be passed on to inherited flag set in command added before flag") } if f := childAfter.InheritedFlags(); f.Lookup("flagname") == nil || f.Lookup("flagname") != f.Lookup("FLAGNAME") { t.Error("Normalization function should be passed on to inherited flag set in command added after flag") } } // Related to https://github.com/spf13/cobra/issues/521. func TestNormConsistent(t *testing.T) { n := func(f *pflag.FlagSet, name string) pflag.NormalizedName { return pflag.NormalizedName(strings.ToUpper(name)) } id := func(f *pflag.FlagSet, name string) pflag.NormalizedName { return pflag.NormalizedName(name) } cmd := &Command{} flagVal := false cmd.Flags().BoolVar(&flagVal, "flagname", true, "this is a dummy flag") // Build local flag set cmd.LocalFlags() cmd.SetGlobalNormalizationFunc(n) cmd.SetGlobalNormalizationFunc(id) if cmd.LocalFlags().Lookup("flagname") == cmd.LocalFlags().Lookup("FLAGNAME") { t.Error("Normalizing flag names should not result in duplicate flags") } } func TestFlagOnPflagCommandLine(t *testing.T) { flagName := "flagOnCommandLine" pflag.String(flagName, "", "about my flag") r := fullSetupTest("--help") checkResultContains(t, r, flagName) // Reset pflag.CommandLine flagset. pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError) } func TestAddTemplateFunctions(t *testing.T) { AddTemplateFunc("t", func() bool { return true }) AddTemplateFuncs(template.FuncMap{ "f": func() bool { return false }, "h": func() string { return "Hello," }, "w": func() string { return "world." }}) const usage = "Hello, world." c := &Command{} c.SetUsageTemplate(`{{if t}}{{h}}{{end}}{{if f}}{{h}}{{end}} {{w}}`) if us := c.UsageString(); us != usage { t.Errorf("c.UsageString() != \"%s\", is \"%s\"", usage, us) } } func TestUsageIsNotPrintedTwice(t *testing.T) { var cmd = &Command{Use: "root"} var sub = &Command{Use: "sub"} cmd.AddCommand(sub) r := simpleTester(cmd, "") if strings.Count(r.Output, "Usage:") != 1 { t.Error("Usage output is not printed exactly once") } } func BenchmarkInheritedFlags(b *testing.B) { initialize() cmdEcho.AddCommand(cmdTimes) b.ResetTimer() for i := 0; i < b.N; i++ { cmdTimes.InheritedFlags() } } func BenchmarkLocalFlags(b *testing.B) { initialize() cmdEcho.AddCommand(cmdTimes) b.ResetTimer() for i := 0; i < b.N; i++ { cmdTimes.LocalFlags() } }