mirror of
https://github.com/spf13/cobra
synced 2024-11-16 18:57:08 +00:00
5c9146990b
The current (desired) behavior when a Command specifies a flag that has the same name as a persistent/inherited flag, is that the local definition takes precedence. This change updates the various Flag subset functions to respect that behavior: * LocalFlags: now returns only the set of flags and persistent flags attached to the Command itself. * InheritedFlags: now returns only the set of persistent flags inherited from the Command's parent(s), excluding any that are overwritten by a local flag. * NonInheritedFlags: changed to an alias of LocalFlags. * AllPersistentFlags: removed as not very useful; it returned the set of all persistent flags attached to the Command and its parent(s). Default UsageTemplate updated to use LocalFlags and InheritedFlags
603 lines
15 KiB
Go
603 lines
15 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, flags2a, flags2b, flags3 string
|
|
var flagi1, flagi2, flagi3, flagir int
|
|
var globalFlag1 bool
|
|
var flagEcho, rootcalled bool
|
|
|
|
const strtwoParentHelp = "help message for parent flag strtwo"
|
|
const strtwoChildHelp = "help message for child flag strtwo"
|
|
|
|
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()
|
|
cmdRootNoRun.PersistentFlags().StringVarP(&flags2a, "strtwo", "t", "two", strtwoParentHelp)
|
|
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(&flags2b, "strtwo", "t", "2", strtwoChildHelp)
|
|
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 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.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 TestChildCommandHelp(t *testing.T) {
|
|
c := noRRSetupTest("print --help")
|
|
checkResultContains(t, c, strtwoParentHelp)
|
|
r := noRRSetupTest("echo times --help")
|
|
checkResultContains(t, r, strtwoChildHelp)
|
|
}
|
|
|
|
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 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()
|
|
|
|
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 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 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)
|
|
}
|
|
|
|
}
|