mirror of
https://github.com/spf13/cobra
synced 2024-11-05 05:17:12 +00:00
9b6c92647a
For a single root command with a Run method, the help output still contains 'help [command]' as a subcommand (because Help is always added). Since the only subcommand would be 'help', the help is better off omitted. This change allows a command to be used both as a subcommand or a root command without having to define a custom help that elides the help command when no subcommands are added. Instead, the default help command is only added when subcommands are present.
553 lines
14 KiB
Go
553 lines
14 KiB
Go
package cobra
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
var _ = fmt.Println
|
|
|
|
var tp, te, tt, t1 []string
|
|
var flagb1, flagb2, flagb3, flagbr bool
|
|
var flags1, flags2, flags3 string
|
|
var flagi1, flagi2, flagi3, flagir int
|
|
var globalFlag1 bool
|
|
var flagEcho, rootcalled bool
|
|
|
|
var cmdPrint = &Command{
|
|
Use: "print [string to print]",
|
|
Short: "Print anything to the screen",
|
|
Long: `an 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.`,
|
|
Run: func(cmd *Command, args []string) {
|
|
te = args
|
|
},
|
|
}
|
|
|
|
var cmdTimes = &Command{
|
|
Use: "times [# times] [string to echo]",
|
|
Short: "Echo anything to the screen more times",
|
|
Long: `an slightly useless command for testing.`,
|
|
Run: func(cmd *Command, args []string) {
|
|
tt = args
|
|
},
|
|
}
|
|
|
|
var cmdRootNoRun = &Command{
|
|
Use: "cobra-test",
|
|
Short: "The root can run it's own function",
|
|
Long: "The root description for help",
|
|
}
|
|
|
|
var cmdRootSameName = &Command{
|
|
Use: "print",
|
|
Short: "Root with the same name as a subcommand",
|
|
Long: "The root description for help",
|
|
}
|
|
|
|
var cmdRootWithRun = &Command{
|
|
Use: "cobra-test",
|
|
Short: "The root can run it's own function",
|
|
Long: "The root description for help",
|
|
Run: func(cmd *Command, args []string) {
|
|
rootcalled = true
|
|
},
|
|
}
|
|
|
|
func flagInit() {
|
|
cmdEcho.ResetFlags()
|
|
cmdPrint.ResetFlags()
|
|
cmdTimes.ResetFlags()
|
|
cmdRootNoRun.ResetFlags()
|
|
cmdRootSameName.ResetFlags()
|
|
cmdRootWithRun.ResetFlags()
|
|
cmdEcho.Flags().IntVarP(&flagi1, "intone", "i", 123, "help message for flag intone")
|
|
cmdTimes.Flags().IntVarP(&flagi2, "inttwo", "j", 234, "help message for flag inttwo")
|
|
cmdPrint.Flags().IntVarP(&flagi3, "intthree", "i", 345, "help message for flag intthree")
|
|
cmdEcho.PersistentFlags().StringVarP(&flags1, "strone", "s", "one", "help message for flag strone")
|
|
cmdTimes.PersistentFlags().StringVarP(&flags2, "strtwo", "t", "two", "help message for flag strtwo")
|
|
cmdPrint.PersistentFlags().StringVarP(&flags3, "strthree", "s", "three", "help message for flag strthree")
|
|
cmdEcho.Flags().BoolVarP(&flagb1, "boolone", "b", true, "help message for flag boolone")
|
|
cmdTimes.Flags().BoolVarP(&flagb2, "booltwo", "c", false, "help message for flag booltwo")
|
|
cmdPrint.Flags().BoolVarP(&flagb3, "boolthree", "b", true, "help message for flag boolthree")
|
|
}
|
|
|
|
func commandInit() {
|
|
cmdEcho.ResetCommands()
|
|
cmdPrint.ResetCommands()
|
|
cmdTimes.ResetCommands()
|
|
cmdRootNoRun.ResetCommands()
|
|
cmdRootSameName.ResetCommands()
|
|
cmdRootWithRun.ResetCommands()
|
|
}
|
|
|
|
func initialize() *Command {
|
|
tt, tp, te = nil, nil, nil
|
|
var c = cmdRootNoRun
|
|
flagInit()
|
|
commandInit()
|
|
return c
|
|
}
|
|
|
|
func initializeWithSameName() *Command {
|
|
tt, tp, te = nil, nil, nil
|
|
var c = cmdRootSameName
|
|
flagInit()
|
|
commandInit()
|
|
return c
|
|
}
|
|
|
|
func initializeWithRootCmd() *Command {
|
|
cmdRootWithRun.ResetCommands()
|
|
tt, tp, te, rootcalled = 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(input string) resulter {
|
|
c := initializeWithRootCmd()
|
|
|
|
return fullTester(c, input)
|
|
}
|
|
|
|
func noRRSetupTest(input string) resulter {
|
|
c := initialize()
|
|
|
|
return fullTester(c, input)
|
|
}
|
|
|
|
func rootOnlySetupTest(input string) resulter {
|
|
c := initializeWithRootCmd()
|
|
|
|
return simpleTester(c, input)
|
|
}
|
|
|
|
func simpleTester(c *Command, input string) resulter {
|
|
buf := new(bytes.Buffer)
|
|
// Testing flag with invalid input
|
|
c.SetOutput(buf)
|
|
c.SetArgs(strings.Split(input, " "))
|
|
|
|
err := c.Execute()
|
|
output := buf.String()
|
|
|
|
return resulter{err, output, c}
|
|
}
|
|
|
|
func fullTester(c *Command, input string) resulter {
|
|
buf := new(bytes.Buffer)
|
|
// Testing flag with invalid input
|
|
c.SetOutput(buf)
|
|
cmdEcho.AddCommand(cmdTimes)
|
|
c.AddCommand(cmdPrint, cmdEcho)
|
|
c.SetArgs(strings.Split(input, " "))
|
|
|
|
err := c.Execute()
|
|
output := buf.String()
|
|
|
|
return resulter{err, output, c}
|
|
}
|
|
|
|
func checkResultContains(t *testing.T, x resulter, check string) {
|
|
if !strings.Contains(x.Output, check) {
|
|
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", check, x.Output)
|
|
}
|
|
}
|
|
|
|
func checkResultOmits(t *testing.T, x resulter, check string) {
|
|
if strings.Contains(x.Output, check) {
|
|
t.Errorf("Unexpected response.\nExpecting to omit: \n %q\nGot:\n %q\n", check, x.Output)
|
|
}
|
|
}
|
|
|
|
func checkOutputContains(t *testing.T, c *Command, check string) {
|
|
buf := new(bytes.Buffer)
|
|
c.SetOutput(buf)
|
|
c.Execute()
|
|
|
|
if !strings.Contains(buf.String(), check) {
|
|
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", check, buf.String())
|
|
}
|
|
}
|
|
|
|
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(strings.Split("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 TestFlagLong(t *testing.T) {
|
|
noRRSetupTest("echo --intone=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)
|
|
}
|
|
}
|
|
|
|
func TestFlagShort(t *testing.T) {
|
|
noRRSetupTest("echo -i13 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("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.Output, "unknown shorthand") {
|
|
t.Errorf("Wrong error message displayed, \n %s", r.Output)
|
|
}
|
|
|
|
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.Output, "intone=123") {
|
|
t.Errorf("Wrong error message displayed, \n %s", r.Output)
|
|
}
|
|
|
|
// Testing flag with invalid input
|
|
r = noRRSetupTest("echo -i10E")
|
|
|
|
if r.Error == nil {
|
|
t.Errorf("invalid input should generate error")
|
|
}
|
|
|
|
if !strings.Contains(r.Output, "invalid argument \"10E\" for -i10E") {
|
|
t.Errorf("Wrong error message displayed, \n %s", r.Output)
|
|
}
|
|
}
|
|
|
|
func TestTrailingCommandFlags(t *testing.T) {
|
|
x := fullSetupTest("echo two -x")
|
|
|
|
if x.Error == nil {
|
|
t.Errorf("invalid flag should generate error")
|
|
}
|
|
}
|
|
|
|
func TestPersistentFlags(t *testing.T) {
|
|
fullSetupTest("echo -s something more here")
|
|
|
|
// persistentFlag should act like normal flag on it's own command
|
|
if strings.Join(te, " ") != "more here" {
|
|
t.Errorf("flags didn't leave proper args remaining..%s given", te)
|
|
}
|
|
|
|
// persistentFlag should act like normal flag on it's own command
|
|
if flags1 != "something" {
|
|
t.Errorf("string flag didn't get correct value, had %v", flags1)
|
|
}
|
|
|
|
fullSetupTest("echo times -s again -c test here")
|
|
|
|
if strings.Join(tt, " ") != "test here" {
|
|
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 != true {
|
|
t.Errorf("local flag not parsed correctly. Expected false, had %v", flagb2)
|
|
}
|
|
}
|
|
|
|
func TestHelpCommand(t *testing.T) {
|
|
c := fullSetupTest("help echo")
|
|
checkResultContains(t, c, cmdEcho.Long)
|
|
|
|
r := fullSetupTest("help echo times")
|
|
checkResultContains(t, r, cmdTimes.Long)
|
|
}
|
|
|
|
func TestRunnableRootCommand(t *testing.T) {
|
|
fullSetupTest("")
|
|
|
|
if rootcalled != true {
|
|
t.Errorf("Root Function was not called")
|
|
}
|
|
}
|
|
|
|
func TestRootFlags(t *testing.T) {
|
|
fullSetupTest("-i 17 -b")
|
|
|
|
if flagbr != true {
|
|
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 that command")
|
|
|
|
if strings.Contains(x.Output, "unknown flag: --help") {
|
|
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output)
|
|
}
|
|
|
|
x = fullSetupTest("echo --help")
|
|
|
|
checkResultContains(t, x, "Available Commands:")
|
|
checkResultContains(t, x, "for more information about that command")
|
|
|
|
if strings.Contains(x.Output, "unknown flag: --help") {
|
|
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output)
|
|
}
|
|
|
|
}
|
|
|
|
func TestRootNoCommandHelp(t *testing.T) {
|
|
x := rootOnlySetupTest("--help")
|
|
|
|
checkResultOmits(t, x, "Available Commands:")
|
|
checkResultOmits(t, x, "for more information about that 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 that command")
|
|
|
|
if strings.Contains(x.Output, "unknown flag: --help") {
|
|
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.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)
|
|
}
|
|
|
|
// short (int) with equals
|
|
// It appears that pflags doesn't support this...
|
|
// Commenting out until support can be added
|
|
|
|
//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.Output, "invalid argument \"10E\" for -i10E") {
|
|
t.Errorf("Wrong error message displayed, \n %s", x.Output)
|
|
}
|
|
|
|
//With quotes
|
|
x = fullSetupTest("-s=\"walking\" echo")
|
|
if x.Error != nil {
|
|
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
|
|
}
|
|
|
|
//With quotes and space
|
|
x = fullSetupTest("-s=\"walking fast\" echo")
|
|
if x.Error != nil {
|
|
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
|
|
}
|
|
|
|
//With inner quote
|
|
x = fullSetupTest("-s=\"walking \\\"Inner Quote\\\" fast\" echo")
|
|
if x.Error != nil {
|
|
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
|
|
}
|
|
|
|
//With quotes and space
|
|
x = fullSetupTest("-s=\"walking \\\"Inner Quote\\\" fast\" echo")
|
|
if x.Error != nil {
|
|
t.Errorf("Valid Input shouldn't have errors, got:\n %q", x.Error)
|
|
}
|
|
|
|
}
|