Merge pull request #84 from eparis/subcommand-invalid-flag

Stop special casing runnable root commands
This commit is contained in:
Eric Paris 2015-04-06 14:57:12 -05:00
commit 29e27b1649
2 changed files with 89 additions and 51 deletions

View file

@ -3,11 +3,13 @@ package cobra
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"os"
"strings" "strings"
"testing" "testing"
) )
var _ = fmt.Println var _ = fmt.Println
var _ = os.Stderr
var tp, te, tt, t1 []string var tp, te, tt, t1 []string
var flagb1, flagb2, flagb3, flagbr, flagbp bool var flagb1, flagb2, flagb3, flagbr, flagbp bool
@ -22,7 +24,7 @@ const strtwoChildHelp = "help message for child flag strtwo"
var cmdPrint = &Command{ var cmdPrint = &Command{
Use: "print [string to print]", Use: "print [string to print]",
Short: "Print anything to the screen", Short: "Print anything to the screen",
Long: `an utterly useless command for testing.`, Long: `an absolutely utterly useless command for testing.`,
Run: func(cmd *Command, args []string) { Run: func(cmd *Command, args []string) {
tp = args tp = args
}, },
@ -41,7 +43,7 @@ var cmdEcho = &Command{
var cmdTimes = &Command{ var cmdTimes = &Command{
Use: "times [# times] [string to echo]", Use: "times [# times] [string to echo]",
Short: "Echo anything to the screen more times", Short: "Echo anything to the screen more times",
Long: `an slightly useless command for testing.`, Long: `a slightly useless command for testing.`,
Run: func(cmd *Command, args []string) { Run: func(cmd *Command, args []string) {
tt = args tt = args
}, },
@ -417,6 +419,20 @@ func TestTrailingCommandFlags(t *testing.T) {
} }
} }
func TestInvalidSubCommandFlags(t *testing.T) {
cmd := initializeWithRootCmd()
cmd.AddCommand(cmdTimes)
result := simpleTester(cmd, "times --inttwo=2 --badflag=bar")
checkResultContains(t, result, "unknown flag: --badflag")
if strings.Contains(result.Output, "unknown flag: --inttwo") {
t.Errorf("invalid --badflag flag shouldn't fail on 'unknown' --inttwo flag")
}
}
func TestPersistentFlags(t *testing.T) { func TestPersistentFlags(t *testing.T) {
fullSetupTest("echo -s something -p more here") fullSetupTest("echo -s something -p more here")
@ -473,6 +489,53 @@ func TestRunnableRootCommand(t *testing.T) {
} }
} }
func TestRunnableRootCommandNilInput(t *testing.T) {
empty_arg := make([]string, 0)
c := initializeWithRootCmd()
buf := new(bytes.Buffer)
// Testing flag with invalid input
c.SetOutput(buf)
cmdEcho.AddCommand(cmdTimes)
c.AddCommand(cmdPrint, cmdEcho)
c.SetArgs(empty_arg)
c.Execute()
if rootcalled != true {
t.Errorf("Root Function was not called")
}
}
func TestRunnableRootCommandEmptyInput(t *testing.T) {
args := make([]string, 3)
args[0] = ""
args[1] = "--introot=12"
args[2] = ""
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 != true {
t.Errorf("Root Function was not called.\n\nOutput was:\n\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) { func TestRootFlags(t *testing.T) {
fullSetupTest("-i 17 -b") fullSetupTest("-i 17 -b")
@ -534,6 +597,24 @@ func TestFlagAccess(t *testing.T) {
} }
} }
func TestNoNRunnableRootCommandNilInput(t *testing.T) {
args := make([]string, 0)
c := initialize()
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 !strings.Contains(buf.String(), cmdRootNoRun.Long) {
t.Errorf("Expected to get help output, Got: \n %s", buf)
}
}
func TestRootNoCommandHelp(t *testing.T) { func TestRootNoCommandHelp(t *testing.T) {
x := rootOnlySetupTest("--help") x := rootOnlySetupTest("--help")

View file

@ -305,6 +305,8 @@ func stripFlags(args []string, c *Command) []string {
inFlag = true inFlag = true
case inFlag: case inFlag:
inFlag = false inFlag = false
case y == "":
// strip empty commands, as the go tests expect this to be ok....
case !strings.HasPrefix(y, "-"): case !strings.HasPrefix(y, "-"):
commands = append(commands, y) commands = append(commands, y)
inFlag = false inFlag = false
@ -375,9 +377,8 @@ func (c *Command) Find(arrs []string) (*Command, []string, error) {
commandFound, a := innerfind(c, arrs) commandFound, a := innerfind(c, arrs)
// if commander returned and the first argument (if it exists) doesn't // If we matched on the root, but we asked for a subcommand, return an error
// match the command name, return nil & error if commandFound.Name() == c.Name() && len(stripFlags(arrs, c)) > 0 && commandFound.Name() != arrs[0] {
if commandFound.Name() == c.Name() && len(arrs[0]) > 0 && commandFound.Name() != arrs[0] {
return nil, a, fmt.Errorf("unknown command %q", a[0]) return nil, a, fmt.Errorf("unknown command %q", a[0])
} }
@ -398,16 +399,6 @@ func (c *Command) Root() *Command {
return findRoot(c) return findRoot(c)
} }
// execute the command determined by args and the command tree
func (c *Command) findAndExecute(args []string) (err error) {
cmd, a, e := c.Find(args)
if e != nil {
return e
}
return cmd.execute(a)
}
func (c *Command) execute(a []string) (err error) { func (c *Command) execute(a []string) (err error) {
if c == nil { if c == nil {
return fmt.Errorf("Called Execute() on a nil Command") return fmt.Errorf("Called Execute() on a nil Command")
@ -494,45 +485,11 @@ func (c *Command) Execute() (err error) {
c.Help() c.Help()
} }
} else { } else {
err = c.findAndExecute(args) cmd, flags, e := c.Find(args)
}
// Now handle the case where the root is runnable and only flags are provided
if err != nil && c.Runnable() {
// This is pretty much a custom version of the *Command.execute method
// with a few differences because it's the final command (no fall back)
e := c.ParseFlags(args)
if e != nil { if e != nil {
// Flags parsing had an error.
// If an error happens here, we have to report it to the user
c.Println(e.Error())
// If an error happens search also for subcommand info about that
if c.cmdErrorBuf != nil && c.cmdErrorBuf.Len() > 0 {
c.Println(c.cmdErrorBuf.String())
} else {
c.Usage()
}
err = e err = e
return
} else { } else {
// If help is called, regardless of other flags, we print that err = cmd.execute(flags)
if c.helpFlagVal {
c.Help()
return nil
}
argWoFlags := c.Flags().Args()
if len(argWoFlags) > 0 {
// If there are arguments (not flags) one of the earlier
// cases should have caught it.. It means invalid usage
// print the usage
c.Usage()
} else {
// Only flags left... Call root.Run
c.preRun()
c.Run(c, argWoFlags)
err = nil
}
} }
} }