mirror of
https://github.com/spf13/cobra
synced 2024-11-25 07:07:15 +00:00
34594c771f
According to golang/go#9504 and https://golang.org/pkg/reflect/#Value, == should not be used on two reflect.Values, but comparing the results of their Interface() method does not work in this case, so let's compare the results of their Pointer() method instead. See https://stackoverflow.com/questions/9643205/how-do-i-compare-two-functions-for-pointer-equality-in-the-latest-go-weekly
1244 lines
32 KiB
Go
1244 lines
32 KiB
Go
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)
|
|
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
|
|
flagInit()
|
|
commandInit()
|
|
return c
|
|
}
|
|
|
|
func initializeWithSameName() *Command {
|
|
tt, tp, te = nil, nil, nil
|
|
rootPersPre, echoPre, echoPersPre, timesPersPre = nil, nil, nil, nil
|
|
var c = cmdRootSameName
|
|
flagInit()
|
|
commandInit()
|
|
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()
|
|
|
|
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.SetGlobalNormalizationFunc(normFunc)
|
|
if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() {
|
|
t.Error("rootCmd seems to have a wrong normalization function")
|
|
}
|
|
|
|
// 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 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()
|
|
}
|
|
}
|