mirror of
https://github.com/spf13/cobra
synced 2024-11-24 14:47:12 +00:00
2933 lines
77 KiB
Go
2933 lines
77 KiB
Go
// Copyright 2013-2023 The Cobra Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package cobra
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
func emptyRun(*Command, []string) {}
|
|
|
|
func executeCommand(root *Command, args ...string) (output string, err error) {
|
|
_, output, err = executeCommandC(root, args...)
|
|
return output, err
|
|
}
|
|
|
|
func executeCommandWithContext(ctx context.Context, root *Command, args ...string) (output string, err error) {
|
|
buf := new(bytes.Buffer)
|
|
root.SetOut(buf)
|
|
root.SetErr(buf)
|
|
root.SetArgs(args)
|
|
|
|
err = root.ExecuteContext(ctx)
|
|
|
|
return buf.String(), err
|
|
}
|
|
|
|
func executeCommandC(root *Command, args ...string) (c *Command, output string, err error) {
|
|
buf := new(bytes.Buffer)
|
|
root.SetOut(buf)
|
|
root.SetErr(buf)
|
|
root.SetArgs(args)
|
|
|
|
c, err = root.ExecuteC()
|
|
|
|
return c, buf.String(), err
|
|
}
|
|
|
|
func executeCommandWithContextC(ctx context.Context, root *Command, args ...string) (c *Command, output string, err error) {
|
|
buf := new(bytes.Buffer)
|
|
root.SetOut(buf)
|
|
root.SetErr(buf)
|
|
root.SetArgs(args)
|
|
|
|
c, err = root.ExecuteContextC(ctx)
|
|
|
|
return c, buf.String(), err
|
|
}
|
|
|
|
func resetCommandLineFlagSet() {
|
|
pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
|
|
}
|
|
|
|
func checkStringContains(t *testing.T, got, expected string) {
|
|
if !strings.Contains(got, expected) {
|
|
t.Errorf("Expected to contain: \n %v\nGot:\n %v\n", expected, got)
|
|
}
|
|
}
|
|
|
|
func checkStringOmits(t *testing.T, got, expected string) {
|
|
if strings.Contains(got, expected) {
|
|
t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got)
|
|
}
|
|
}
|
|
|
|
const onetwo = "one two"
|
|
|
|
func TestSingleCommand(t *testing.T) {
|
|
var rootCmdArgs []string
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Args: ExactArgs(2),
|
|
Run: func(_ *Command, args []string) { rootCmdArgs = args },
|
|
}
|
|
aCmd := &Command{Use: "a", Args: NoArgs, Run: emptyRun}
|
|
bCmd := &Command{Use: "b", Args: NoArgs, Run: emptyRun}
|
|
rootCmd.AddCommand(aCmd, bCmd)
|
|
|
|
output, err := executeCommand(rootCmd, "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
got := strings.Join(rootCmdArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
|
|
}
|
|
}
|
|
|
|
func TestChildCommand(t *testing.T) {
|
|
var child1CmdArgs []string
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
child1Cmd := &Command{
|
|
Use: "child1",
|
|
Args: ExactArgs(2),
|
|
Run: func(_ *Command, args []string) { child1CmdArgs = args },
|
|
}
|
|
child2Cmd := &Command{Use: "child2", Args: NoArgs, Run: emptyRun}
|
|
rootCmd.AddCommand(child1Cmd, child2Cmd)
|
|
|
|
output, err := executeCommand(rootCmd, "child1", "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
got := strings.Join(child1CmdArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("child1CmdArgs expected: %q, got: %q", onetwo, got)
|
|
}
|
|
}
|
|
|
|
func TestCallCommandWithoutSubcommands(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
_, err := executeCommand(rootCmd)
|
|
if err != nil {
|
|
t.Errorf("Calling command without subcommands should not have error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRootExecuteUnknownCommand(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
|
|
|
|
output, _ := executeCommand(rootCmd, "unknown")
|
|
|
|
expected := "Error: unknown command \"unknown\" for \"root\"\nRun 'root --help' for usage.\n"
|
|
|
|
if output != expected {
|
|
t.Errorf("Expected:\n %q\nGot:\n %q\n", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestSubcommandExecuteC(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
c, output, err := executeCommandC(rootCmd, "child")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
if c.Name() != "child" {
|
|
t.Errorf(`invalid command returned from ExecuteC: expected "child"', got: %q`, c.Name())
|
|
}
|
|
}
|
|
|
|
func TestExecuteContext(t *testing.T) {
|
|
ctx := context.TODO()
|
|
|
|
ctxRun := func(cmd *Command, args []string) {
|
|
if cmd.Context() != ctx {
|
|
t.Errorf("Command %q must have context when called with ExecuteContext", cmd.Use)
|
|
}
|
|
}
|
|
|
|
rootCmd := &Command{Use: "root", Run: ctxRun, PreRun: ctxRun}
|
|
childCmd := &Command{Use: "child", Run: ctxRun, PreRun: ctxRun}
|
|
granchildCmd := &Command{Use: "grandchild", Run: ctxRun, PreRun: ctxRun}
|
|
|
|
childCmd.AddCommand(granchildCmd)
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
if _, err := executeCommandWithContext(ctx, rootCmd, ""); err != nil {
|
|
t.Errorf("Root command must not fail: %+v", err)
|
|
}
|
|
|
|
if _, err := executeCommandWithContext(ctx, rootCmd, "child"); err != nil {
|
|
t.Errorf("Subcommand must not fail: %+v", err)
|
|
}
|
|
|
|
if _, err := executeCommandWithContext(ctx, rootCmd, "child", "grandchild"); err != nil {
|
|
t.Errorf("Command child must not fail: %+v", err)
|
|
}
|
|
}
|
|
|
|
func TestExecuteContextC(t *testing.T) {
|
|
ctx := context.TODO()
|
|
|
|
ctxRun := func(cmd *Command, args []string) {
|
|
if cmd.Context() != ctx {
|
|
t.Errorf("Command %q must have context when called with ExecuteContext", cmd.Use)
|
|
}
|
|
}
|
|
|
|
rootCmd := &Command{Use: "root", Run: ctxRun, PreRun: ctxRun}
|
|
childCmd := &Command{Use: "child", Run: ctxRun, PreRun: ctxRun}
|
|
granchildCmd := &Command{Use: "grandchild", Run: ctxRun, PreRun: ctxRun}
|
|
|
|
childCmd.AddCommand(granchildCmd)
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
if _, _, err := executeCommandWithContextC(ctx, rootCmd, ""); err != nil {
|
|
t.Errorf("Root command must not fail: %+v", err)
|
|
}
|
|
|
|
if _, _, err := executeCommandWithContextC(ctx, rootCmd, "child"); err != nil {
|
|
t.Errorf("Subcommand must not fail: %+v", err)
|
|
}
|
|
|
|
if _, _, err := executeCommandWithContextC(ctx, rootCmd, "child", "grandchild"); err != nil {
|
|
t.Errorf("Command child must not fail: %+v", err)
|
|
}
|
|
}
|
|
|
|
func TestExecute_NoContext(t *testing.T) {
|
|
run := func(cmd *Command, args []string) {
|
|
if cmd.Context() != context.Background() {
|
|
t.Errorf("Command %s must have background context", cmd.Use)
|
|
}
|
|
}
|
|
|
|
rootCmd := &Command{Use: "root", Run: run, PreRun: run}
|
|
childCmd := &Command{Use: "child", Run: run, PreRun: run}
|
|
granchildCmd := &Command{Use: "grandchild", Run: run, PreRun: run}
|
|
|
|
childCmd.AddCommand(granchildCmd)
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
if _, err := executeCommand(rootCmd, ""); err != nil {
|
|
t.Errorf("Root command must not fail: %+v", err)
|
|
}
|
|
|
|
if _, err := executeCommand(rootCmd, "child"); err != nil {
|
|
t.Errorf("Subcommand must not fail: %+v", err)
|
|
}
|
|
|
|
if _, err := executeCommand(rootCmd, "child", "grandchild"); err != nil {
|
|
t.Errorf("Command child must not fail: %+v", err)
|
|
}
|
|
}
|
|
|
|
func TestRootUnknownCommandSilenced(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
rootCmd.SilenceErrors = true
|
|
rootCmd.SilenceUsage = true
|
|
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
|
|
|
|
output, _ := executeCommand(rootCmd, "unknown")
|
|
if output != "" {
|
|
t.Errorf("Expected blank output, because of silenced usage.\nGot:\n %q\n", output)
|
|
}
|
|
}
|
|
|
|
func TestCommandAlias(t *testing.T) {
|
|
var timesCmdArgs []string
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
echoCmd := &Command{
|
|
Use: "echo",
|
|
Aliases: []string{"say", "tell"},
|
|
Args: NoArgs,
|
|
Run: emptyRun,
|
|
}
|
|
timesCmd := &Command{
|
|
Use: "times",
|
|
Args: ExactArgs(2),
|
|
Run: func(_ *Command, args []string) { timesCmdArgs = args },
|
|
}
|
|
echoCmd.AddCommand(timesCmd)
|
|
rootCmd.AddCommand(echoCmd)
|
|
|
|
output, err := executeCommand(rootCmd, "tell", "times", "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
got := strings.Join(timesCmdArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
|
|
}
|
|
}
|
|
|
|
func TestEnablePrefixMatching(t *testing.T) {
|
|
EnablePrefixMatching = true
|
|
|
|
var aCmdArgs []string
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
aCmd := &Command{
|
|
Use: "aCmd",
|
|
Args: ExactArgs(2),
|
|
Run: func(_ *Command, args []string) { aCmdArgs = args },
|
|
}
|
|
bCmd := &Command{Use: "bCmd", Args: NoArgs, Run: emptyRun}
|
|
rootCmd.AddCommand(aCmd, bCmd)
|
|
|
|
output, err := executeCommand(rootCmd, "a", "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
got := strings.Join(aCmdArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("aCmdArgs expected: %q, got: %q", onetwo, got)
|
|
}
|
|
|
|
EnablePrefixMatching = defaultPrefixMatching
|
|
}
|
|
|
|
func TestAliasPrefixMatching(t *testing.T) {
|
|
EnablePrefixMatching = true
|
|
|
|
var timesCmdArgs []string
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
echoCmd := &Command{
|
|
Use: "echo",
|
|
Aliases: []string{"say", "tell"},
|
|
Args: NoArgs,
|
|
Run: emptyRun,
|
|
}
|
|
timesCmd := &Command{
|
|
Use: "times",
|
|
Args: ExactArgs(2),
|
|
Run: func(_ *Command, args []string) { timesCmdArgs = args },
|
|
}
|
|
echoCmd.AddCommand(timesCmd)
|
|
rootCmd.AddCommand(echoCmd)
|
|
|
|
output, err := executeCommand(rootCmd, "sa", "times", "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
got := strings.Join(timesCmdArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
|
|
}
|
|
|
|
EnablePrefixMatching = defaultPrefixMatching
|
|
}
|
|
|
|
// TestPlugin checks usage as plugin for another command such as kubectl. The
|
|
// executable is `kubectl-plugin`, but we run it as `kubectl plugin`. The help
|
|
// text should reflect the way we run the command.
|
|
func TestPlugin(t *testing.T) {
|
|
cmd := &Command{
|
|
Use: "kubectl-plugin",
|
|
Args: NoArgs,
|
|
Annotations: map[string]string{
|
|
CommandDisplayNameAnnotation: "kubectl plugin",
|
|
},
|
|
Run: emptyRun,
|
|
}
|
|
|
|
cmdHelp, err := executeCommand(cmd, "-h")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, cmdHelp, "kubectl plugin [flags]")
|
|
checkStringContains(t, cmdHelp, "help for kubectl plugin")
|
|
}
|
|
|
|
// TestPlugin checks usage as plugin with sub commands.
|
|
func TestPluginWithSubCommands(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "kubectl-plugin",
|
|
Args: NoArgs,
|
|
Annotations: map[string]string{
|
|
CommandDisplayNameAnnotation: "kubectl plugin",
|
|
},
|
|
}
|
|
|
|
subCmd := &Command{Use: "sub [flags]", Args: NoArgs, Run: emptyRun}
|
|
rootCmd.AddCommand(subCmd)
|
|
|
|
rootHelp, err := executeCommand(rootCmd, "-h")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, rootHelp, "kubectl plugin [command]")
|
|
checkStringContains(t, rootHelp, "help for kubectl plugin")
|
|
checkStringContains(t, rootHelp, "kubectl plugin [command] --help")
|
|
|
|
childHelp, err := executeCommand(rootCmd, "sub", "-h")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, childHelp, "kubectl plugin sub [flags]")
|
|
checkStringContains(t, childHelp, "help for sub")
|
|
|
|
helpHelp, err := executeCommand(rootCmd, "help", "-h")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, helpHelp, "kubectl plugin help [path to command]")
|
|
checkStringContains(t, helpHelp, "kubectl plugin help [command]")
|
|
}
|
|
|
|
// TestChildSameName checks the correct behaviour of cobra in cases,
|
|
// when an application with name "foo" and with subcommand "foo"
|
|
// is executed with args "foo foo".
|
|
func TestChildSameName(t *testing.T) {
|
|
var fooCmdArgs []string
|
|
rootCmd := &Command{Use: "foo", Args: NoArgs, Run: emptyRun}
|
|
fooCmd := &Command{
|
|
Use: "foo",
|
|
Args: ExactArgs(2),
|
|
Run: func(_ *Command, args []string) { fooCmdArgs = args },
|
|
}
|
|
barCmd := &Command{Use: "bar", Args: NoArgs, Run: emptyRun}
|
|
rootCmd.AddCommand(fooCmd, barCmd)
|
|
|
|
output, err := executeCommand(rootCmd, "foo", "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
got := strings.Join(fooCmdArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
|
|
}
|
|
}
|
|
|
|
// TestGrandChildSameName checks the correct behaviour of cobra in cases,
|
|
// when user has a root command and a grand child
|
|
// with the same name.
|
|
func TestGrandChildSameName(t *testing.T) {
|
|
var fooCmdArgs []string
|
|
rootCmd := &Command{Use: "foo", Args: NoArgs, Run: emptyRun}
|
|
barCmd := &Command{Use: "bar", Args: NoArgs, Run: emptyRun}
|
|
fooCmd := &Command{
|
|
Use: "foo",
|
|
Args: ExactArgs(2),
|
|
Run: func(_ *Command, args []string) { fooCmdArgs = args },
|
|
}
|
|
barCmd.AddCommand(fooCmd)
|
|
rootCmd.AddCommand(barCmd)
|
|
|
|
output, err := executeCommand(rootCmd, "bar", "foo", "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
got := strings.Join(fooCmdArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
|
|
}
|
|
}
|
|
|
|
func TestFlagLong(t *testing.T) {
|
|
var cArgs []string
|
|
c := &Command{
|
|
Use: "c",
|
|
Args: ArbitraryArgs,
|
|
Run: func(_ *Command, args []string) { cArgs = args },
|
|
}
|
|
|
|
var intFlagValue int
|
|
var stringFlagValue string
|
|
c.Flags().IntVar(&intFlagValue, "intf", -1, "")
|
|
c.Flags().StringVar(&stringFlagValue, "sf", "", "")
|
|
|
|
output, err := executeCommand(c, "--intf=7", "--sf=abc", "one", "--", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
if c.ArgsLenAtDash() != 1 {
|
|
t.Errorf("Expected ArgsLenAtDash: %v but got %v", 1, c.ArgsLenAtDash())
|
|
}
|
|
if intFlagValue != 7 {
|
|
t.Errorf("Expected intFlagValue: %v, got %v", 7, intFlagValue)
|
|
}
|
|
if stringFlagValue != "abc" {
|
|
t.Errorf("Expected stringFlagValue: %q, got %q", "abc", stringFlagValue)
|
|
}
|
|
|
|
got := strings.Join(cArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
|
|
}
|
|
}
|
|
|
|
func TestFlagShort(t *testing.T) {
|
|
var cArgs []string
|
|
c := &Command{
|
|
Use: "c",
|
|
Args: ArbitraryArgs,
|
|
Run: func(_ *Command, args []string) { cArgs = args },
|
|
}
|
|
|
|
var intFlagValue int
|
|
var stringFlagValue string
|
|
c.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
|
|
c.Flags().StringVarP(&stringFlagValue, "sf", "s", "", "")
|
|
|
|
output, err := executeCommand(c, "-i", "7", "-sabc", "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
if intFlagValue != 7 {
|
|
t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
|
|
}
|
|
if stringFlagValue != "abc" {
|
|
t.Errorf("Expected stringFlagValue: %q, got %q", "abc", stringFlagValue)
|
|
}
|
|
|
|
got := strings.Join(cArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
|
|
}
|
|
}
|
|
|
|
func TestChildFlag(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
var intFlagValue int
|
|
childCmd.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
|
|
|
|
output, err := executeCommand(rootCmd, "child", "-i7")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
if intFlagValue != 7 {
|
|
t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
|
|
}
|
|
}
|
|
|
|
func TestChildFlagWithParentLocalFlag(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
var intFlagValue int
|
|
rootCmd.Flags().StringP("sf", "s", "", "")
|
|
childCmd.Flags().IntVarP(&intFlagValue, "intf", "i", -1, "")
|
|
|
|
_, err := executeCommand(rootCmd, "child", "-i7", "-sabc")
|
|
if err == nil {
|
|
t.Errorf("Invalid flag should generate error")
|
|
}
|
|
|
|
checkStringContains(t, err.Error(), "unknown shorthand")
|
|
|
|
if intFlagValue != 7 {
|
|
t.Errorf("Expected flag value: %v, got %v", 7, intFlagValue)
|
|
}
|
|
}
|
|
|
|
func TestFlagInvalidInput(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
rootCmd.Flags().IntP("intf", "i", -1, "")
|
|
|
|
_, err := executeCommand(rootCmd, "-iabc")
|
|
if err == nil {
|
|
t.Errorf("Invalid flag value should generate error")
|
|
}
|
|
|
|
checkStringContains(t, err.Error(), "invalid syntax")
|
|
}
|
|
|
|
func TestFlagBeforeCommand(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
var flagValue int
|
|
childCmd.Flags().IntVarP(&flagValue, "intf", "i", -1, "")
|
|
|
|
// With short flag.
|
|
_, err := executeCommand(rootCmd, "-i7", "child")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if flagValue != 7 {
|
|
t.Errorf("Expected flag value: %v, got %v", 7, flagValue)
|
|
}
|
|
|
|
// With long flag.
|
|
_, err = executeCommand(rootCmd, "--intf=8", "child")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if flagValue != 8 {
|
|
t.Errorf("Expected flag value: %v, got %v", 9, flagValue)
|
|
}
|
|
}
|
|
|
|
func TestStripFlags(t *testing.T) {
|
|
tests := []struct {
|
|
input []string
|
|
output []string
|
|
}{
|
|
{
|
|
[]string{"foo", "bar"},
|
|
[]string{"foo", "bar"},
|
|
},
|
|
{
|
|
[]string{"foo", "--str", "-s"},
|
|
[]string{"foo"},
|
|
},
|
|
{
|
|
[]string{"-s", "foo", "--str", "bar"},
|
|
[]string{},
|
|
},
|
|
{
|
|
[]string{"-i10", "echo"},
|
|
[]string{"echo"},
|
|
},
|
|
{
|
|
[]string{"-i=10", "echo"},
|
|
[]string{"echo"},
|
|
},
|
|
{
|
|
[]string{"--int=100", "echo"},
|
|
[]string{"echo"},
|
|
},
|
|
{
|
|
[]string{"-ib", "echo", "-sfoo", "baz"},
|
|
[]string{"echo", "baz"},
|
|
},
|
|
{
|
|
[]string{"-i=baz", "bar", "-i", "foo", "blah"},
|
|
[]string{"bar", "blah"},
|
|
},
|
|
{
|
|
[]string{"--int=baz", "-sbar", "-i", "foo", "blah"},
|
|
[]string{"blah"},
|
|
},
|
|
{
|
|
[]string{"--bool", "bar", "-i", "foo", "blah"},
|
|
[]string{"bar", "blah"},
|
|
},
|
|
{
|
|
[]string{"-b", "bar", "-i", "foo", "blah"},
|
|
[]string{"bar", "blah"},
|
|
},
|
|
{
|
|
[]string{"--persist", "bar"},
|
|
[]string{"bar"},
|
|
},
|
|
{
|
|
[]string{"-p", "bar"},
|
|
[]string{"bar"},
|
|
},
|
|
{
|
|
[]string{"-s", "value", "bar"},
|
|
[]string{"bar"},
|
|
},
|
|
{
|
|
[]string{"-s=value", "bar"},
|
|
[]string{"bar"},
|
|
},
|
|
{
|
|
[]string{"-svalue", "bar"},
|
|
[]string{"bar"},
|
|
},
|
|
{
|
|
[]string{"-ps", "value", "bar"},
|
|
[]string{"bar"},
|
|
},
|
|
{
|
|
[]string{"-ps=value", "bar"},
|
|
[]string{"bar"},
|
|
},
|
|
{
|
|
[]string{"-psvalue", "bar"},
|
|
[]string{"bar"},
|
|
},
|
|
}
|
|
|
|
c := &Command{Use: "c", Run: emptyRun}
|
|
c.PersistentFlags().BoolP("persist", "p", false, "")
|
|
c.Flags().IntP("int", "i", -1, "")
|
|
c.Flags().StringP("str", "s", "", "")
|
|
c.Flags().BoolP("bool", "b", false, "")
|
|
|
|
for i, test := range tests {
|
|
got, _ := stripFlags(test.input, c)
|
|
if !reflect.DeepEqual(test.output, got) {
|
|
t.Errorf("(%v) Expected: %v, got: %v", i, test.output, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDisableFlagParsing(t *testing.T) {
|
|
var cArgs []string
|
|
c := &Command{
|
|
Use: "c",
|
|
DisableFlagParsing: true,
|
|
Run: func(_ *Command, args []string) {
|
|
cArgs = args
|
|
},
|
|
}
|
|
|
|
args := []string{"cmd", "-v", "-race", "-file", "foo.go"}
|
|
output, err := executeCommand(c, args...)
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(args, cArgs) {
|
|
t.Errorf("Expected: %v, got: %v", args, cArgs)
|
|
}
|
|
}
|
|
|
|
func TestPersistentFlagsOnSameCommand(t *testing.T) {
|
|
var rootCmdArgs []string
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Args: ArbitraryArgs,
|
|
Run: func(_ *Command, args []string) { rootCmdArgs = args },
|
|
}
|
|
|
|
var flagValue int
|
|
rootCmd.PersistentFlags().IntVarP(&flagValue, "intf", "i", -1, "")
|
|
|
|
output, err := executeCommand(rootCmd, "-i7", "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
got := strings.Join(rootCmdArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("rootCmdArgs expected: %q, got %q", onetwo, got)
|
|
}
|
|
if flagValue != 7 {
|
|
t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
|
|
}
|
|
}
|
|
|
|
// TestEmptyInputs checks,
|
|
// if flags correctly parsed with blank strings in args.
|
|
func TestEmptyInputs(t *testing.T) {
|
|
c := &Command{Use: "c", Run: emptyRun}
|
|
|
|
var flagValue int
|
|
c.Flags().IntVarP(&flagValue, "intf", "i", -1, "")
|
|
|
|
output, err := executeCommand(c, "", "-i7", "")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
if flagValue != 7 {
|
|
t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
|
|
}
|
|
}
|
|
|
|
func TestChildFlagShadowsParentPersistentFlag(t *testing.T) {
|
|
parent := &Command{Use: "parent", Run: emptyRun}
|
|
child := &Command{Use: "child", Run: emptyRun}
|
|
|
|
parent.PersistentFlags().Bool("boolf", false, "")
|
|
parent.PersistentFlags().Int("intf", -1, "")
|
|
child.Flags().String("strf", "", "")
|
|
child.Flags().Int("intf", -1, "")
|
|
|
|
parent.AddCommand(child)
|
|
|
|
childInherited := child.InheritedFlags()
|
|
childLocal := child.LocalFlags()
|
|
|
|
if childLocal.Lookup("strf") == nil {
|
|
t.Error(`LocalFlags expected to contain "strf", got "nil"`)
|
|
}
|
|
if childInherited.Lookup("boolf") == nil {
|
|
t.Error(`InheritedFlags expected to contain "boolf", got "nil"`)
|
|
}
|
|
|
|
if childInherited.Lookup("intf") != nil {
|
|
t.Errorf(`InheritedFlags should not contain shadowed flag "intf"`)
|
|
}
|
|
if childLocal.Lookup("intf") == nil {
|
|
t.Error(`LocalFlags expected to contain "intf", got "nil"`)
|
|
}
|
|
}
|
|
|
|
func TestPersistentFlagsOnChild(t *testing.T) {
|
|
var childCmdArgs []string
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
Args: ArbitraryArgs,
|
|
Run: func(_ *Command, args []string) { childCmdArgs = args },
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
var parentFlagValue int
|
|
var childFlagValue int
|
|
rootCmd.PersistentFlags().IntVarP(&parentFlagValue, "parentf", "p", -1, "")
|
|
childCmd.Flags().IntVarP(&childFlagValue, "childf", "c", -1, "")
|
|
|
|
output, err := executeCommand(rootCmd, "child", "-c7", "-p8", "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
got := strings.Join(childCmdArgs, " ")
|
|
if got != onetwo {
|
|
t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
|
|
}
|
|
if parentFlagValue != 8 {
|
|
t.Errorf("parentFlagValue expected: %v, got %v", 8, parentFlagValue)
|
|
}
|
|
if childFlagValue != 7 {
|
|
t.Errorf("childFlagValue expected: %v, got %v", 7, childFlagValue)
|
|
}
|
|
}
|
|
|
|
func TestRequiredFlags(t *testing.T) {
|
|
c := &Command{Use: "c", Run: emptyRun}
|
|
c.Flags().String("foo1", "", "")
|
|
assertNoErr(t, c.MarkFlagRequired("foo1"))
|
|
c.Flags().String("foo2", "", "")
|
|
assertNoErr(t, c.MarkFlagRequired("foo2"))
|
|
c.Flags().String("bar", "", "")
|
|
|
|
expected := fmt.Sprintf("required flag(s) %q, %q not set", "foo1", "foo2")
|
|
|
|
_, err := executeCommand(c)
|
|
got := err.Error()
|
|
|
|
if got != expected {
|
|
t.Errorf("Expected error: %q, got: %q", expected, got)
|
|
}
|
|
}
|
|
|
|
func TestPersistentRequiredFlags(t *testing.T) {
|
|
parent := &Command{Use: "parent", Run: emptyRun}
|
|
parent.PersistentFlags().String("foo1", "", "")
|
|
assertNoErr(t, parent.MarkPersistentFlagRequired("foo1"))
|
|
parent.PersistentFlags().String("foo2", "", "")
|
|
assertNoErr(t, parent.MarkPersistentFlagRequired("foo2"))
|
|
parent.Flags().String("foo3", "", "")
|
|
|
|
child := &Command{Use: "child", Run: emptyRun}
|
|
child.Flags().String("bar1", "", "")
|
|
assertNoErr(t, child.MarkFlagRequired("bar1"))
|
|
child.Flags().String("bar2", "", "")
|
|
assertNoErr(t, child.MarkFlagRequired("bar2"))
|
|
child.Flags().String("bar3", "", "")
|
|
|
|
parent.AddCommand(child)
|
|
|
|
expected := fmt.Sprintf("required flag(s) %q, %q, %q, %q not set", "bar1", "bar2", "foo1", "foo2")
|
|
|
|
_, err := executeCommand(parent, "child")
|
|
if err.Error() != expected {
|
|
t.Errorf("Expected %q, got %q", expected, err.Error())
|
|
}
|
|
}
|
|
|
|
func TestPersistentRequiredFlagsWithDisableFlagParsing(t *testing.T) {
|
|
// Make sure a required persistent flag does not break
|
|
// commands that disable flag parsing
|
|
|
|
parent := &Command{Use: "parent", Run: emptyRun}
|
|
parent.PersistentFlags().Bool("foo", false, "")
|
|
flag := parent.PersistentFlags().Lookup("foo")
|
|
assertNoErr(t, parent.MarkPersistentFlagRequired("foo"))
|
|
|
|
child := &Command{Use: "child", Run: emptyRun}
|
|
child.DisableFlagParsing = true
|
|
|
|
parent.AddCommand(child)
|
|
|
|
if _, err := executeCommand(parent, "--foo", "child"); err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// Reset the flag or else it will remember the state from the previous command
|
|
flag.Changed = false
|
|
if _, err := executeCommand(parent, "child", "--foo"); err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// Reset the flag or else it will remember the state from the previous command
|
|
flag.Changed = false
|
|
if _, err := executeCommand(parent, "child"); err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestInitHelpFlagMergesFlags(t *testing.T) {
|
|
usage := "custom flag"
|
|
rootCmd := &Command{Use: "root"}
|
|
rootCmd.PersistentFlags().Bool("help", false, "custom flag")
|
|
childCmd := &Command{Use: "child"}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
childCmd.InitDefaultHelpFlag()
|
|
got := childCmd.Flags().Lookup("help").Usage
|
|
if got != usage {
|
|
t.Errorf("Expected the help flag from the root command with usage: %v\nGot the default with usage: %v", usage, got)
|
|
}
|
|
}
|
|
|
|
func TestHelpCommandExecuted(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
|
|
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, "help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, rootCmd.Long)
|
|
}
|
|
|
|
func TestHelpCommandExecutedOnChild(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
output, err := executeCommand(rootCmd, "help", "child")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, childCmd.Long)
|
|
}
|
|
|
|
func TestHelpCommandExecutedOnChildWithFlagThatShadowsParentFlag(t *testing.T) {
|
|
parent := &Command{Use: "parent", Run: emptyRun}
|
|
child := &Command{Use: "child", Run: emptyRun}
|
|
parent.AddCommand(child)
|
|
|
|
parent.PersistentFlags().Bool("foo", false, "parent foo usage")
|
|
parent.PersistentFlags().Bool("bar", false, "parent bar usage")
|
|
child.Flags().Bool("foo", false, "child foo usage") // This shadows parent's foo flag
|
|
child.Flags().Bool("baz", false, "child baz usage")
|
|
|
|
got, err := executeCommand(parent, "help", "child")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := `Usage:
|
|
parent child [flags]
|
|
|
|
Flags:
|
|
--baz child baz usage
|
|
--foo child foo usage
|
|
-h, --help help for child
|
|
|
|
Global Flags:
|
|
--bar parent bar usage
|
|
`
|
|
|
|
if got != expected {
|
|
t.Errorf("Help text mismatch.\nExpected:\n%s\n\nGot:\n%s\n", expected, got)
|
|
}
|
|
}
|
|
|
|
func TestSetHelpCommand(t *testing.T) {
|
|
c := &Command{Use: "c", Run: emptyRun}
|
|
c.AddCommand(&Command{Use: "empty", Run: emptyRun})
|
|
|
|
expected := "WORKS"
|
|
c.SetHelpCommand(&Command{
|
|
Use: "help [command]",
|
|
Short: "Help about any command",
|
|
Long: `Help provides help for any command in the application.
|
|
Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
|
Run: func(c *Command, _ []string) { c.Print(expected) },
|
|
})
|
|
|
|
got, err := executeCommand(c, "help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
if got != expected {
|
|
t.Errorf("Expected to contain %q, got %q", expected, got)
|
|
}
|
|
}
|
|
|
|
func TestHelpFlagExecuted(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
|
|
|
|
output, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, rootCmd.Long)
|
|
}
|
|
|
|
func TestHelpFlagExecutedOnChild(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Long: "Long description", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
output, err := executeCommand(rootCmd, "child", "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, childCmd.Long)
|
|
}
|
|
|
|
// TestHelpFlagInHelp checks,
|
|
// if '--help' flag is shown in help for child (executing `parent help child`),
|
|
// that has no other flags.
|
|
// Related to https://github.com/spf13/cobra/issues/302.
|
|
func TestHelpFlagInHelp(t *testing.T) {
|
|
parentCmd := &Command{Use: "parent", Run: func(*Command, []string) {}}
|
|
|
|
childCmd := &Command{Use: "child", Run: func(*Command, []string) {}}
|
|
parentCmd.AddCommand(childCmd)
|
|
|
|
output, err := executeCommand(parentCmd, "help", "child")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "[flags]")
|
|
}
|
|
|
|
func TestFlagsInUsage(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: func(*Command, []string) {}}
|
|
output, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "[flags]")
|
|
}
|
|
|
|
func TestHelpExecutedOnNonRunnableChild(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Long: "Long description"}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
output, err := executeCommand(rootCmd, "child")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, childCmd.Long)
|
|
}
|
|
|
|
func TestVersionFlagExecuted(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
|
|
|
|
output, err := executeCommand(rootCmd, "--version", "arg1")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "root version 1.0.0")
|
|
}
|
|
|
|
func TestVersionFlagExecutedWithNoName(t *testing.T) {
|
|
rootCmd := &Command{Version: "1.0.0", Run: emptyRun}
|
|
|
|
output, err := executeCommand(rootCmd, "--version", "arg1")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "version 1.0.0")
|
|
}
|
|
|
|
func TestShortAndLongVersionFlagInHelp(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
|
|
|
|
output, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "-v, --version")
|
|
}
|
|
|
|
func TestLongVersionFlagOnlyInHelpWhenShortPredefined(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
|
|
rootCmd.Flags().StringP("foo", "v", "", "not a version flag")
|
|
|
|
output, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringOmits(t, output, "-v, --version")
|
|
checkStringContains(t, output, "--version")
|
|
}
|
|
|
|
func TestShorthandVersionFlagExecuted(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
|
|
|
|
output, err := executeCommand(rootCmd, "-v", "arg1")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "root version 1.0.0")
|
|
}
|
|
|
|
func TestVersionTemplate(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
|
|
rootCmd.SetVersionTemplate(`customized version: {{.Version}}`)
|
|
|
|
output, err := executeCommand(rootCmd, "--version", "arg1")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "customized version: 1.0.0")
|
|
}
|
|
|
|
func TestShorthandVersionTemplate(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
|
|
rootCmd.SetVersionTemplate(`customized version: {{.Version}}`)
|
|
|
|
output, err := executeCommand(rootCmd, "-v", "arg1")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "customized version: 1.0.0")
|
|
}
|
|
|
|
func TestRootErrPrefixExecutedOnSubcommand(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
rootCmd.SetErrPrefix("root error prefix:")
|
|
rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, "sub", "--unknown-flag")
|
|
if err == nil {
|
|
t.Errorf("Expected error")
|
|
}
|
|
|
|
checkStringContains(t, output, "root error prefix: unknown flag: --unknown-flag")
|
|
}
|
|
|
|
func TestRootAndSubErrPrefix(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
subCmd := &Command{Use: "sub", Run: emptyRun}
|
|
rootCmd.AddCommand(subCmd)
|
|
rootCmd.SetErrPrefix("root error prefix:")
|
|
subCmd.SetErrPrefix("sub error prefix:")
|
|
|
|
if output, err := executeCommand(rootCmd, "--unknown-root-flag"); err == nil {
|
|
t.Errorf("Expected error")
|
|
} else {
|
|
checkStringContains(t, output, "root error prefix: unknown flag: --unknown-root-flag")
|
|
}
|
|
|
|
if output, err := executeCommand(rootCmd, "sub", "--unknown-sub-flag"); err == nil {
|
|
t.Errorf("Expected error")
|
|
} else {
|
|
checkStringContains(t, output, "sub error prefix: unknown flag: --unknown-sub-flag")
|
|
}
|
|
}
|
|
|
|
func TestVersionFlagExecutedOnSubcommand(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0"}
|
|
rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, "--version", "sub")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "root version 1.0.0")
|
|
}
|
|
|
|
func TestShorthandVersionFlagExecutedOnSubcommand(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0"}
|
|
rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, "-v", "sub")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "root version 1.0.0")
|
|
}
|
|
|
|
func TestVersionFlagOnlyAddedToRoot(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
|
|
rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
|
|
|
|
_, err := executeCommand(rootCmd, "sub", "--version")
|
|
if err == nil {
|
|
t.Errorf("Expected error")
|
|
}
|
|
|
|
checkStringContains(t, err.Error(), "unknown flag: --version")
|
|
}
|
|
|
|
func TestShortVersionFlagOnlyAddedToRoot(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}
|
|
rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun})
|
|
|
|
_, err := executeCommand(rootCmd, "sub", "-v")
|
|
if err == nil {
|
|
t.Errorf("Expected error")
|
|
}
|
|
|
|
checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
|
|
}
|
|
|
|
func TestVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
|
|
_, err := executeCommand(rootCmd, "--version")
|
|
if err == nil {
|
|
t.Errorf("Expected error")
|
|
}
|
|
checkStringContains(t, err.Error(), "unknown flag: --version")
|
|
}
|
|
|
|
func TestShorthandVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
|
|
_, err := executeCommand(rootCmd, "-v")
|
|
if err == nil {
|
|
t.Errorf("Expected error")
|
|
}
|
|
checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
|
|
}
|
|
|
|
func TestShorthandVersionFlagOnlyAddedIfShorthandNotDefined(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun, Version: "1.2.3"}
|
|
rootCmd.Flags().StringP("notversion", "v", "", "not a version flag")
|
|
|
|
_, err := executeCommand(rootCmd, "-v")
|
|
if err == nil {
|
|
t.Errorf("Expected error")
|
|
}
|
|
check(t, rootCmd.Flags().ShorthandLookup("v").Name, "notversion")
|
|
checkStringContains(t, err.Error(), "flag needs an argument: 'v' in -v")
|
|
}
|
|
|
|
func TestShorthandVersionFlagOnlyAddedIfVersionNotDefined(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun, Version: "1.2.3"}
|
|
rootCmd.Flags().Bool("version", false, "a different kind of version flag")
|
|
|
|
_, err := executeCommand(rootCmd, "-v")
|
|
if err == nil {
|
|
t.Errorf("Expected error")
|
|
}
|
|
checkStringContains(t, err.Error(), "unknown shorthand flag: 'v' in -v")
|
|
}
|
|
|
|
func TestUsageIsNotPrintedTwice(t *testing.T) {
|
|
var cmd = &Command{Use: "root"}
|
|
var sub = &Command{Use: "sub"}
|
|
cmd.AddCommand(sub)
|
|
|
|
output, _ := executeCommand(cmd, "")
|
|
if strings.Count(output, "Usage:") != 1 {
|
|
t.Error("Usage output is not printed exactly once")
|
|
}
|
|
}
|
|
|
|
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 parents but visited %d", total)
|
|
}
|
|
|
|
total = 0
|
|
c.VisitParents(add)
|
|
if total != 0 {
|
|
t.Errorf("Should have visited no parents but visited %d", total)
|
|
}
|
|
}
|
|
|
|
func TestSuggestions(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
timesCmd := &Command{
|
|
Use: "times",
|
|
SuggestFor: []string{"counts"},
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(timesCmd)
|
|
|
|
templateWithSuggestions := "Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n"
|
|
templateWithoutSuggestions := "Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n"
|
|
|
|
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{true, false} {
|
|
rootCmd.DisableSuggestions = suggestionsDisabled
|
|
|
|
var expected string
|
|
output, _ := executeCommand(rootCmd, typo)
|
|
|
|
if suggestion == "" || suggestionsDisabled {
|
|
expected = fmt.Sprintf(templateWithoutSuggestions, typo)
|
|
} else {
|
|
expected = fmt.Sprintf(templateWithSuggestions, typo, suggestion)
|
|
}
|
|
|
|
if output != expected {
|
|
t.Errorf("Unexpected response.\nExpected:\n %q\nGot:\n %q\n", expected, output)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCaseInsensitive(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Run: emptyRun, Aliases: []string{"alternative"}}
|
|
granchildCmd := &Command{Use: "GRANDCHILD", Run: emptyRun, Aliases: []string{"ALIAS"}}
|
|
|
|
childCmd.AddCommand(granchildCmd)
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
tests := []struct {
|
|
args []string
|
|
failWithoutEnabling bool
|
|
}{
|
|
{
|
|
args: []string{"child"},
|
|
failWithoutEnabling: false,
|
|
},
|
|
{
|
|
args: []string{"CHILD"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
{
|
|
args: []string{"chILD"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
{
|
|
args: []string{"CHIld"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
{
|
|
args: []string{"alternative"},
|
|
failWithoutEnabling: false,
|
|
},
|
|
{
|
|
args: []string{"ALTERNATIVE"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
{
|
|
args: []string{"ALTernatIVE"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
{
|
|
args: []string{"alternatiVE"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
{
|
|
args: []string{"child", "GRANDCHILD"},
|
|
failWithoutEnabling: false,
|
|
},
|
|
{
|
|
args: []string{"child", "grandchild"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
{
|
|
args: []string{"CHIld", "GRANdchild"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
{
|
|
args: []string{"alternative", "ALIAS"},
|
|
failWithoutEnabling: false,
|
|
},
|
|
{
|
|
args: []string{"alternative", "alias"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
{
|
|
args: []string{"CHILD", "alias"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
{
|
|
args: []string{"CHIld", "aliAS"},
|
|
failWithoutEnabling: true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
for _, enableCaseInsensitivity := range []bool{true, false} {
|
|
EnableCaseInsensitive = enableCaseInsensitivity
|
|
|
|
output, err := executeCommand(rootCmd, test.args...)
|
|
expectedFailure := test.failWithoutEnabling && !enableCaseInsensitivity
|
|
|
|
if !expectedFailure && output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if !expectedFailure && err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
EnableCaseInsensitive = defaultCaseInsensitive
|
|
}
|
|
|
|
// This test make sure we keep backwards-compatibility with respect
|
|
// to command names case sensitivity behavior.
|
|
func TestCaseSensitivityBackwardCompatibility(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Run: emptyRun}
|
|
|
|
rootCmd.AddCommand(childCmd)
|
|
_, err := executeCommand(rootCmd, strings.ToUpper(childCmd.Use))
|
|
if err == nil {
|
|
t.Error("Expected error on calling a command in upper case while command names are case sensitive. Got nil.")
|
|
}
|
|
|
|
}
|
|
|
|
func TestRemoveCommand(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
rootCmd.RemoveCommand(childCmd)
|
|
|
|
_, err := executeCommand(rootCmd, "child")
|
|
if err == nil {
|
|
t.Error("Expected error on calling removed command. Got nil.")
|
|
}
|
|
}
|
|
|
|
func TestReplaceCommandWithRemove(t *testing.T) {
|
|
childUsed := 0
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
child1Cmd := &Command{
|
|
Use: "child",
|
|
Run: func(*Command, []string) { childUsed = 1 },
|
|
}
|
|
child2Cmd := &Command{
|
|
Use: "child",
|
|
Run: func(*Command, []string) { childUsed = 2 },
|
|
}
|
|
rootCmd.AddCommand(child1Cmd)
|
|
rootCmd.RemoveCommand(child1Cmd)
|
|
rootCmd.AddCommand(child2Cmd)
|
|
|
|
output, err := executeCommand(rootCmd, "child")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
if childUsed == 1 {
|
|
t.Error("Removed command shouldn't be called")
|
|
}
|
|
if childUsed != 2 {
|
|
t.Error("Replacing command should have been called but didn't")
|
|
}
|
|
}
|
|
|
|
func TestDeprecatedCommand(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
deprecatedCmd := &Command{
|
|
Use: "deprecated",
|
|
Deprecated: "This command is deprecated",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(deprecatedCmd)
|
|
|
|
output, err := executeCommand(rootCmd, "deprecated")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, deprecatedCmd.Deprecated)
|
|
}
|
|
|
|
func TestHooks(t *testing.T) {
|
|
var (
|
|
persPreArgs string
|
|
preArgs string
|
|
runArgs string
|
|
postArgs string
|
|
persPostArgs string
|
|
)
|
|
|
|
c := &Command{
|
|
Use: "c",
|
|
PersistentPreRun: func(_ *Command, args []string) {
|
|
persPreArgs = strings.Join(args, " ")
|
|
},
|
|
PreRun: func(_ *Command, args []string) {
|
|
preArgs = strings.Join(args, " ")
|
|
},
|
|
Run: func(_ *Command, args []string) {
|
|
runArgs = strings.Join(args, " ")
|
|
},
|
|
PostRun: func(_ *Command, args []string) {
|
|
postArgs = strings.Join(args, " ")
|
|
},
|
|
PersistentPostRun: func(_ *Command, args []string) {
|
|
persPostArgs = strings.Join(args, " ")
|
|
},
|
|
}
|
|
|
|
output, err := executeCommand(c, "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
for _, v := range []struct {
|
|
name string
|
|
got string
|
|
}{
|
|
{"persPreArgs", persPreArgs},
|
|
{"preArgs", preArgs},
|
|
{"runArgs", runArgs},
|
|
{"postArgs", postArgs},
|
|
{"persPostArgs", persPostArgs},
|
|
} {
|
|
if v.got != onetwo {
|
|
t.Errorf("Expected %s %q, got %q", v.name, onetwo, v.got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPersistentHooks(t *testing.T) {
|
|
EnableTraverseRunHooks = true
|
|
testPersistentHooks(t, []string{
|
|
"parent PersistentPreRun",
|
|
"child PersistentPreRun",
|
|
"child PreRun",
|
|
"child Run",
|
|
"child PostRun",
|
|
"child PersistentPostRun",
|
|
"parent PersistentPostRun",
|
|
})
|
|
|
|
EnableTraverseRunHooks = false
|
|
testPersistentHooks(t, []string{
|
|
"child PersistentPreRun",
|
|
"child PreRun",
|
|
"child Run",
|
|
"child PostRun",
|
|
"child PersistentPostRun",
|
|
})
|
|
}
|
|
|
|
func testPersistentHooks(t *testing.T, expectedHookRunOrder []string) {
|
|
var hookRunOrder []string
|
|
|
|
validateHook := func(args []string, hookName string) {
|
|
hookRunOrder = append(hookRunOrder, hookName)
|
|
got := strings.Join(args, " ")
|
|
if onetwo != got {
|
|
t.Errorf("Expected %s %q, got %q", hookName, onetwo, got)
|
|
}
|
|
}
|
|
|
|
parentCmd := &Command{
|
|
Use: "parent",
|
|
PersistentPreRun: func(_ *Command, args []string) {
|
|
validateHook(args, "parent PersistentPreRun")
|
|
},
|
|
PreRun: func(_ *Command, args []string) {
|
|
validateHook(args, "parent PreRun")
|
|
},
|
|
Run: func(_ *Command, args []string) {
|
|
validateHook(args, "parent Run")
|
|
},
|
|
PostRun: func(_ *Command, args []string) {
|
|
validateHook(args, "parent PostRun")
|
|
},
|
|
PersistentPostRun: func(_ *Command, args []string) {
|
|
validateHook(args, "parent PersistentPostRun")
|
|
},
|
|
}
|
|
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
PersistentPreRun: func(_ *Command, args []string) {
|
|
validateHook(args, "child PersistentPreRun")
|
|
},
|
|
PreRun: func(_ *Command, args []string) {
|
|
validateHook(args, "child PreRun")
|
|
},
|
|
Run: func(_ *Command, args []string) {
|
|
validateHook(args, "child Run")
|
|
},
|
|
PostRun: func(_ *Command, args []string) {
|
|
validateHook(args, "child PostRun")
|
|
},
|
|
PersistentPostRun: func(_ *Command, args []string) {
|
|
validateHook(args, "child PersistentPostRun")
|
|
},
|
|
}
|
|
parentCmd.AddCommand(childCmd)
|
|
|
|
output, err := executeCommand(parentCmd, "child", "one", "two")
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
for idx, exp := range expectedHookRunOrder {
|
|
if len(hookRunOrder) > idx {
|
|
if act := hookRunOrder[idx]; act != exp {
|
|
t.Errorf("Expected %q at %d, got %q", exp, idx, act)
|
|
}
|
|
} else {
|
|
t.Errorf("Expected %q at %d, got nothing", exp, idx)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Related to https://github.com/spf13/cobra/issues/521.
|
|
func TestGlobalNormFuncPropagation(t *testing.T) {
|
|
normFunc := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
|
return pflag.NormalizedName(name)
|
|
}
|
|
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{Use: "child", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.SetGlobalNormalizationFunc(normFunc)
|
|
if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() {
|
|
t.Error("rootCmd seems to have a wrong normalization function")
|
|
}
|
|
|
|
if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(childCmd.GlobalNormalizationFunc()).Pointer() {
|
|
t.Error("childCmd should have had the normalization function of rootCmd")
|
|
}
|
|
}
|
|
|
|
// Related to https://github.com/spf13/cobra/issues/521.
|
|
func TestNormPassedOnLocal(t *testing.T) {
|
|
toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
|
return pflag.NormalizedName(strings.ToUpper(name))
|
|
}
|
|
|
|
c := &Command{}
|
|
c.Flags().Bool("flagname", true, "this is a dummy flag")
|
|
c.SetGlobalNormalizationFunc(toUpper)
|
|
if c.LocalFlags().Lookup("flagname") != c.LocalFlags().Lookup("FLAGNAME") {
|
|
t.Error("Normalization function should be passed on to Local flag set")
|
|
}
|
|
}
|
|
|
|
// Related to https://github.com/spf13/cobra/issues/521.
|
|
func TestNormPassedOnInherited(t *testing.T) {
|
|
toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
|
return pflag.NormalizedName(strings.ToUpper(name))
|
|
}
|
|
|
|
c := &Command{}
|
|
c.SetGlobalNormalizationFunc(toUpper)
|
|
|
|
child1 := &Command{}
|
|
c.AddCommand(child1)
|
|
|
|
c.PersistentFlags().Bool("flagname", true, "")
|
|
|
|
child2 := &Command{}
|
|
c.AddCommand(child2)
|
|
|
|
inherited := child1.InheritedFlags()
|
|
if inherited.Lookup("flagname") == nil || inherited.Lookup("flagname") != inherited.Lookup("FLAGNAME") {
|
|
t.Error("Normalization function should be passed on to inherited flag set in command added before flag")
|
|
}
|
|
|
|
inherited = child2.InheritedFlags()
|
|
if inherited.Lookup("flagname") == nil || inherited.Lookup("flagname") != inherited.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 TestConsistentNormalizedName(t *testing.T) {
|
|
toUpper := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
|
return pflag.NormalizedName(strings.ToUpper(name))
|
|
}
|
|
n := func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
|
return pflag.NormalizedName(name)
|
|
}
|
|
|
|
c := &Command{}
|
|
c.Flags().Bool("flagname", true, "")
|
|
c.SetGlobalNormalizationFunc(toUpper)
|
|
c.SetGlobalNormalizationFunc(n)
|
|
|
|
if c.LocalFlags().Lookup("flagname") == c.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")
|
|
|
|
c := &Command{Use: "c", Run: emptyRun}
|
|
c.AddCommand(&Command{Use: "child", Run: emptyRun})
|
|
|
|
output, _ := executeCommand(c, "--help")
|
|
checkStringContains(t, output, flagName)
|
|
|
|
resetCommandLineFlagSet()
|
|
}
|
|
|
|
// TestHiddenCommandExecutes checks,
|
|
// if hidden commands run as intended.
|
|
func TestHiddenCommandExecutes(t *testing.T) {
|
|
executed := false
|
|
c := &Command{
|
|
Use: "c",
|
|
Hidden: true,
|
|
Run: func(*Command, []string) { executed = true },
|
|
}
|
|
|
|
output, err := executeCommand(c)
|
|
if output != "" {
|
|
t.Errorf("Unexpected output: %v", output)
|
|
}
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
if !executed {
|
|
t.Error("Hidden command should have been executed")
|
|
}
|
|
}
|
|
|
|
// test to ensure hidden commands do not show up in usage/help text
|
|
func TestHiddenCommandIsHidden(t *testing.T) {
|
|
c := &Command{Use: "c", Hidden: true, Run: emptyRun}
|
|
if c.IsAvailableCommand() {
|
|
t.Errorf("Hidden command should be unavailable")
|
|
}
|
|
}
|
|
|
|
func TestCommandsAreSorted(t *testing.T) {
|
|
EnableCommandSorting = true
|
|
|
|
originalNames := []string{"middle", "zlast", "afirst"}
|
|
expectedNames := []string{"afirst", "middle", "zlast"}
|
|
|
|
var rootCmd = &Command{Use: "root"}
|
|
|
|
for _, name := range originalNames {
|
|
rootCmd.AddCommand(&Command{Use: name})
|
|
}
|
|
|
|
for i, c := range rootCmd.Commands() {
|
|
got := c.Name()
|
|
if expectedNames[i] != got {
|
|
t.Errorf("Expected: %s, got: %s", expectedNames[i], got)
|
|
}
|
|
}
|
|
|
|
EnableCommandSorting = defaultCommandSorting
|
|
}
|
|
|
|
func TestEnableCommandSortingIsDisabled(t *testing.T) {
|
|
EnableCommandSorting = false
|
|
|
|
originalNames := []string{"middle", "zlast", "afirst"}
|
|
|
|
var rootCmd = &Command{Use: "root"}
|
|
|
|
for _, name := range originalNames {
|
|
rootCmd.AddCommand(&Command{Use: name})
|
|
}
|
|
|
|
for i, c := range rootCmd.Commands() {
|
|
got := c.Name()
|
|
if originalNames[i] != got {
|
|
t.Errorf("expected: %s, got: %s", originalNames[i], got)
|
|
}
|
|
}
|
|
|
|
EnableCommandSorting = defaultCommandSorting
|
|
}
|
|
|
|
func TestUsageWithGroup(t *testing.T) {
|
|
var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
|
|
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
|
|
|
rootCmd.AddGroup(&Group{ID: "group1", Title: "group1"})
|
|
rootCmd.AddGroup(&Group{ID: "group2", Title: "group2"})
|
|
|
|
rootCmd.AddCommand(&Command{Use: "cmd1", GroupID: "group1", Run: emptyRun})
|
|
rootCmd.AddCommand(&Command{Use: "cmd2", GroupID: "group2", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// help should be ungrouped here
|
|
checkStringContains(t, output, "\nAdditional Commands:\n help")
|
|
checkStringContains(t, output, "\ngroup1\n cmd1")
|
|
checkStringContains(t, output, "\ngroup2\n cmd2")
|
|
}
|
|
|
|
func TestUsageHelpGroup(t *testing.T) {
|
|
var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
|
|
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
|
|
|
rootCmd.AddGroup(&Group{ID: "group", Title: "group"})
|
|
rootCmd.AddCommand(&Command{Use: "xxx", GroupID: "group", Run: emptyRun})
|
|
rootCmd.SetHelpCommandGroupID("group")
|
|
|
|
output, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// now help should be grouped under "group"
|
|
checkStringOmits(t, output, "\nAdditional Commands:\n help")
|
|
checkStringContains(t, output, "\ngroup\n help")
|
|
}
|
|
|
|
func TestUsageCompletionGroup(t *testing.T) {
|
|
var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
|
|
|
|
rootCmd.AddGroup(&Group{ID: "group", Title: "group"})
|
|
rootCmd.AddGroup(&Group{ID: "help", Title: "help"})
|
|
|
|
rootCmd.AddCommand(&Command{Use: "xxx", GroupID: "group", Run: emptyRun})
|
|
rootCmd.SetHelpCommandGroupID("help")
|
|
rootCmd.SetCompletionCommandGroupID("group")
|
|
|
|
output, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// now completion should be grouped under "group"
|
|
checkStringOmits(t, output, "\nAdditional Commands:\n completion")
|
|
checkStringContains(t, output, "\ngroup\n completion")
|
|
}
|
|
|
|
func TestUngroupedCommand(t *testing.T) {
|
|
var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
|
|
|
|
rootCmd.AddGroup(&Group{ID: "group", Title: "group"})
|
|
rootCmd.AddGroup(&Group{ID: "help", Title: "help"})
|
|
|
|
rootCmd.AddCommand(&Command{Use: "xxx", GroupID: "group", Run: emptyRun})
|
|
rootCmd.SetHelpCommandGroupID("help")
|
|
rootCmd.SetCompletionCommandGroupID("group")
|
|
|
|
// Add a command without a group
|
|
rootCmd.AddCommand(&Command{Use: "yyy", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// The yyy command should be in the additional command "group"
|
|
checkStringContains(t, output, "\nAdditional Commands:\n yyy")
|
|
}
|
|
|
|
func TestAddGroup(t *testing.T) {
|
|
var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
|
|
|
|
rootCmd.AddGroup(&Group{ID: "group", Title: "Test group"})
|
|
rootCmd.AddCommand(&Command{Use: "cmd", GroupID: "group", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, "\nTest group\n cmd")
|
|
}
|
|
|
|
func TestWrongGroupFirstLevel(t *testing.T) {
|
|
var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
|
|
|
|
rootCmd.AddGroup(&Group{ID: "group", Title: "Test group"})
|
|
// Use the wrong group ID
|
|
rootCmd.AddCommand(&Command{Use: "cmd", GroupID: "wrong", Run: emptyRun})
|
|
|
|
defer func() {
|
|
if recover() == nil {
|
|
t.Errorf("The code should have panicked due to a missing group")
|
|
}
|
|
}()
|
|
_, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWrongGroupNestedLevel(t *testing.T) {
|
|
var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
|
|
var childCmd = &Command{Use: "child", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
childCmd.AddGroup(&Group{ID: "group", Title: "Test group"})
|
|
// Use the wrong group ID
|
|
childCmd.AddCommand(&Command{Use: "cmd", GroupID: "wrong", Run: emptyRun})
|
|
|
|
defer func() {
|
|
if recover() == nil {
|
|
t.Errorf("The code should have panicked due to a missing group")
|
|
}
|
|
}()
|
|
_, err := executeCommand(rootCmd, "child", "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWrongGroupForHelp(t *testing.T) {
|
|
var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
|
|
var childCmd = &Command{Use: "child", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.AddGroup(&Group{ID: "group", Title: "Test group"})
|
|
// Use the wrong group ID
|
|
rootCmd.SetHelpCommandGroupID("wrong")
|
|
|
|
defer func() {
|
|
if recover() == nil {
|
|
t.Errorf("The code should have panicked due to a missing group")
|
|
}
|
|
}()
|
|
_, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWrongGroupForCompletion(t *testing.T) {
|
|
var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun}
|
|
var childCmd = &Command{Use: "child", Run: emptyRun}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.AddGroup(&Group{ID: "group", Title: "Test group"})
|
|
// Use the wrong group ID
|
|
rootCmd.SetCompletionCommandGroupID("wrong")
|
|
|
|
defer func() {
|
|
if recover() == nil {
|
|
t.Errorf("The code should have panicked due to a missing group")
|
|
}
|
|
}()
|
|
_, err := executeCommand(rootCmd, "--help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSetOutput(t *testing.T) {
|
|
c := &Command{}
|
|
c.SetOutput(nil)
|
|
if out := c.OutOrStdout(); out != os.Stdout {
|
|
t.Errorf("Expected setting output to nil to revert back to stdout")
|
|
}
|
|
}
|
|
|
|
func TestSetOut(t *testing.T) {
|
|
c := &Command{}
|
|
c.SetOut(nil)
|
|
if out := c.OutOrStdout(); out != os.Stdout {
|
|
t.Errorf("Expected setting output to nil to revert back to stdout")
|
|
}
|
|
}
|
|
|
|
func TestSetErr(t *testing.T) {
|
|
c := &Command{}
|
|
c.SetErr(nil)
|
|
if out := c.ErrOrStderr(); out != os.Stderr {
|
|
t.Errorf("Expected setting error to nil to revert back to stderr")
|
|
}
|
|
}
|
|
|
|
func TestSetIn(t *testing.T) {
|
|
c := &Command{}
|
|
c.SetIn(nil)
|
|
if out := c.InOrStdin(); out != os.Stdin {
|
|
t.Errorf("Expected setting input to nil to revert back to stdin")
|
|
}
|
|
}
|
|
|
|
func TestUsageStringRedirected(t *testing.T) {
|
|
c := &Command{}
|
|
|
|
c.usageFunc = func(cmd *Command) error {
|
|
cmd.Print("[stdout1]")
|
|
cmd.PrintErr("[stderr2]")
|
|
cmd.Print("[stdout3]")
|
|
return nil
|
|
}
|
|
|
|
expected := "[stdout1][stderr2][stdout3]"
|
|
if got := c.UsageString(); got != expected {
|
|
t.Errorf("Expected usage string to consider both stdout and stderr")
|
|
}
|
|
}
|
|
|
|
func TestCommandPrintRedirection(t *testing.T) {
|
|
errBuff, outBuff := bytes.NewBuffer(nil), bytes.NewBuffer(nil)
|
|
root := &Command{
|
|
Run: func(cmd *Command, args []string) {
|
|
|
|
cmd.PrintErr("PrintErr")
|
|
cmd.PrintErrln("PrintErr", "line")
|
|
cmd.PrintErrf("PrintEr%s", "r")
|
|
|
|
cmd.Print("Print")
|
|
cmd.Println("Print", "line")
|
|
cmd.Printf("Prin%s", "t")
|
|
},
|
|
}
|
|
|
|
root.SetErr(errBuff)
|
|
root.SetOut(outBuff)
|
|
|
|
if err := root.Execute(); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
gotErrBytes, err := io.ReadAll(errBuff)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
gotOutBytes, err := io.ReadAll(outBuff)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if wantErr := []byte("PrintErrPrintErr line\nPrintErr"); !bytes.Equal(gotErrBytes, wantErr) {
|
|
t.Errorf("got: '%s' want: '%s'", gotErrBytes, wantErr)
|
|
}
|
|
|
|
if wantOut := []byte("PrintPrint line\nPrint"); !bytes.Equal(gotOutBytes, wantOut) {
|
|
t.Errorf("got: '%s' want: '%s'", gotOutBytes, wantOut)
|
|
}
|
|
}
|
|
|
|
func TestFlagErrorFunc(t *testing.T) {
|
|
c := &Command{Use: "c", Run: emptyRun}
|
|
|
|
expectedFmt := "This is expected: %v"
|
|
c.SetFlagErrorFunc(func(_ *Command, err error) error {
|
|
return fmt.Errorf(expectedFmt, err)
|
|
})
|
|
|
|
_, err := executeCommand(c, "--unknown-flag")
|
|
|
|
got := err.Error()
|
|
expected := fmt.Sprintf(expectedFmt, "unknown flag: --unknown-flag")
|
|
if got != expected {
|
|
t.Errorf("Expected %v, got %v", expected, got)
|
|
}
|
|
}
|
|
|
|
func TestFlagErrorFuncHelp(t *testing.T) {
|
|
c := &Command{Use: "c", Run: emptyRun}
|
|
c.PersistentFlags().Bool("help", false, "help for c")
|
|
c.SetFlagErrorFunc(func(_ *Command, err error) error {
|
|
return fmt.Errorf("wrap error: %w", err)
|
|
})
|
|
|
|
out, err := executeCommand(c, "--help")
|
|
if err != nil {
|
|
t.Errorf("--help should not fail: %v", err)
|
|
}
|
|
|
|
expected := `Usage:
|
|
c [flags]
|
|
|
|
Flags:
|
|
--help help for c
|
|
`
|
|
if out != expected {
|
|
t.Errorf("Expected: %v, got: %v", expected, out)
|
|
}
|
|
|
|
out, err = executeCommand(c, "-h")
|
|
if err != nil {
|
|
t.Errorf("-h should not fail: %v", err)
|
|
}
|
|
|
|
if out != expected {
|
|
t.Errorf("Expected: %v, got: %v", expected, out)
|
|
}
|
|
}
|
|
|
|
// TestSortedFlags checks,
|
|
// if cmd.LocalFlags() is unsorted when cmd.Flags().SortFlags set to false.
|
|
// Related to https://github.com/spf13/cobra/issues/404.
|
|
func TestSortedFlags(t *testing.T) {
|
|
c := &Command{}
|
|
c.Flags().SortFlags = false
|
|
names := []string{"C", "B", "A", "D"}
|
|
for _, name := range names {
|
|
c.Flags().Bool(name, false, "")
|
|
}
|
|
|
|
i := 0
|
|
c.LocalFlags().VisitAll(func(f *pflag.Flag) {
|
|
if i == len(names) {
|
|
return
|
|
}
|
|
if stringInSlice(f.Name, names) {
|
|
if names[i] != f.Name {
|
|
t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name)
|
|
}
|
|
i++
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestMergeCommandLineToFlags checks,
|
|
// if pflag.CommandLine is correctly merged to c.Flags() after first call
|
|
// of c.mergePersistentFlags.
|
|
// Related to https://github.com/spf13/cobra/issues/443.
|
|
func TestMergeCommandLineToFlags(t *testing.T) {
|
|
pflag.Bool("boolflag", false, "")
|
|
c := &Command{Use: "c", Run: emptyRun}
|
|
c.mergePersistentFlags()
|
|
if c.Flags().Lookup("boolflag") == nil {
|
|
t.Fatal("Expecting to have flag from CommandLine in c.Flags()")
|
|
}
|
|
|
|
resetCommandLineFlagSet()
|
|
}
|
|
|
|
// TestUseDeprecatedFlags checks,
|
|
// if cobra.Execute() prints a message, if a deprecated flag is used.
|
|
// Related to https://github.com/spf13/cobra/issues/463.
|
|
func TestUseDeprecatedFlags(t *testing.T) {
|
|
c := &Command{Use: "c", Run: emptyRun}
|
|
c.Flags().BoolP("deprecated", "d", false, "deprecated flag")
|
|
assertNoErr(t, c.Flags().MarkDeprecated("deprecated", "This flag is deprecated"))
|
|
|
|
output, err := executeCommand(c, "c", "-d")
|
|
if err != nil {
|
|
t.Error("Unexpected error:", err)
|
|
}
|
|
checkStringContains(t, output, "This flag is deprecated")
|
|
}
|
|
|
|
func TestTraverseWithParentFlags(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", TraverseChildren: true}
|
|
rootCmd.Flags().String("str", "", "")
|
|
rootCmd.Flags().BoolP("bool", "b", false, "")
|
|
|
|
childCmd := &Command{Use: "child"}
|
|
childCmd.Flags().Int("int", -1, "")
|
|
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
c, args, err := rootCmd.Traverse([]string{"-b", "--str", "ok", "child", "--int"})
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if len(args) != 1 || args[0] != "--int" {
|
|
t.Errorf("Wrong args: %v", args)
|
|
}
|
|
if c.Name() != childCmd.Name() {
|
|
t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name())
|
|
}
|
|
}
|
|
|
|
func TestTraverseWithShorthandCombinationInParentFlags(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", TraverseChildren: true}
|
|
stringVal := rootCmd.Flags().StringP("str", "s", "", "")
|
|
boolVal := rootCmd.Flags().BoolP("bool", "b", false, "")
|
|
|
|
childCmd := &Command{Use: "child"}
|
|
childCmd.Flags().Int("int", -1, "")
|
|
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
c, args, err := rootCmd.Traverse([]string{"-bs", "ok", "child", "--int"})
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if len(args) != 1 || args[0] != "--int" {
|
|
t.Errorf("Wrong args: %v", args)
|
|
}
|
|
if c.Name() != childCmd.Name() {
|
|
t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name())
|
|
}
|
|
if *stringVal != "ok" {
|
|
t.Errorf("Expected -s to be set to: %s, got: %s", "ok", *stringVal)
|
|
}
|
|
if !*boolVal {
|
|
t.Errorf("Expected -b to be set")
|
|
}
|
|
}
|
|
|
|
func TestTraverseWithArgumentIdenticalToCommandName(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", TraverseChildren: true}
|
|
stringVal := rootCmd.Flags().StringP("str", "s", "", "")
|
|
boolVal := rootCmd.Flags().BoolP("bool", "b", false, "")
|
|
|
|
childCmd := &Command{Use: "child"}
|
|
childCmd.Flags().Int("int", -1, "")
|
|
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
c, args, err := rootCmd.Traverse([]string{"-bs", "child", "child", "--int"})
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if len(args) != 1 || args[0] != "--int" {
|
|
t.Errorf("Wrong args: %v", args)
|
|
}
|
|
if c.Name() != childCmd.Name() {
|
|
t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name())
|
|
}
|
|
if *stringVal != "child" {
|
|
t.Errorf("Expected -s to be set to: %s, got: %s", "child", *stringVal)
|
|
}
|
|
if !*boolVal {
|
|
t.Errorf("Expected -b to be set")
|
|
}
|
|
}
|
|
|
|
func TestTraverseNoParentFlags(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", TraverseChildren: true}
|
|
rootCmd.Flags().String("foo", "", "foo things")
|
|
|
|
childCmd := &Command{Use: "child"}
|
|
childCmd.Flags().String("str", "", "")
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
c, args, err := rootCmd.Traverse([]string{"child"})
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if len(args) != 0 {
|
|
t.Errorf("Wrong args %v", args)
|
|
}
|
|
if c.Name() != childCmd.Name() {
|
|
t.Errorf("Expected command: %q, got: %q", childCmd.Name(), c.Name())
|
|
}
|
|
}
|
|
|
|
func TestTraverseWithBadParentFlags(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", TraverseChildren: true}
|
|
|
|
childCmd := &Command{Use: "child"}
|
|
childCmd.Flags().String("str", "", "")
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
expected := "unknown flag: --str"
|
|
|
|
c, _, err := rootCmd.Traverse([]string{"--str", "ok", "child"})
|
|
if err == nil || !strings.Contains(err.Error(), expected) {
|
|
t.Errorf("Expected error, %q, got %q", expected, err)
|
|
}
|
|
if c != nil {
|
|
t.Errorf("Expected nil command")
|
|
}
|
|
}
|
|
|
|
func TestTraverseWithBadChildFlag(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", TraverseChildren: true}
|
|
rootCmd.Flags().String("str", "", "")
|
|
|
|
childCmd := &Command{Use: "child"}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
// Expect no error because the last commands args shouldn't be parsed in
|
|
// Traverse.
|
|
c, args, err := rootCmd.Traverse([]string{"child", "--str"})
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if len(args) != 1 || args[0] != "--str" {
|
|
t.Errorf("Wrong args: %v", args)
|
|
}
|
|
if c.Name() != childCmd.Name() {
|
|
t.Errorf("Expected command %q, got: %q", childCmd.Name(), c.Name())
|
|
}
|
|
}
|
|
|
|
func TestTraverseWithTwoSubcommands(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", TraverseChildren: true}
|
|
|
|
subCmd := &Command{Use: "sub", TraverseChildren: true}
|
|
rootCmd.AddCommand(subCmd)
|
|
|
|
subsubCmd := &Command{
|
|
Use: "subsub",
|
|
}
|
|
subCmd.AddCommand(subsubCmd)
|
|
|
|
c, _, err := rootCmd.Traverse([]string{"sub", "subsub"})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
if c.Name() != subsubCmd.Name() {
|
|
t.Fatalf("Expected command: %q, got %q", subsubCmd.Name(), c.Name())
|
|
}
|
|
}
|
|
|
|
// TestUpdateName checks if c.Name() updates on changed c.Use.
|
|
// Related to https://github.com/spf13/cobra/pull/422#discussion_r143918343.
|
|
func TestUpdateName(t *testing.T) {
|
|
c := &Command{Use: "name xyz"}
|
|
originalName := c.Name()
|
|
|
|
c.Use = "changedName abc"
|
|
if originalName == c.Name() || c.Name() != "changedName" {
|
|
t.Error("c.Name() should be updated on changed c.Use")
|
|
}
|
|
}
|
|
|
|
type calledAsTestcase struct {
|
|
args []string
|
|
call string
|
|
want string
|
|
epm bool
|
|
}
|
|
|
|
func (tc *calledAsTestcase) test(t *testing.T) {
|
|
defer func(ov bool) { EnablePrefixMatching = ov }(EnablePrefixMatching)
|
|
EnablePrefixMatching = tc.epm
|
|
|
|
var called *Command
|
|
run := func(c *Command, _ []string) { t.Logf("called: %q", c.Name()); called = c }
|
|
|
|
parent := &Command{Use: "parent", Run: run}
|
|
child1 := &Command{Use: "child1", Run: run, Aliases: []string{"this"}}
|
|
child2 := &Command{Use: "child2", Run: run, Aliases: []string{"that"}}
|
|
|
|
parent.AddCommand(child1)
|
|
parent.AddCommand(child2)
|
|
parent.SetArgs(tc.args)
|
|
|
|
output := new(bytes.Buffer)
|
|
parent.SetOut(output)
|
|
parent.SetErr(output)
|
|
|
|
_ = parent.Execute()
|
|
|
|
if called == nil {
|
|
if tc.call != "" {
|
|
t.Errorf("missing expected call to command: %s", tc.call)
|
|
}
|
|
return
|
|
}
|
|
|
|
if called.Name() != tc.call {
|
|
t.Errorf("called command == %q; Wanted %q", called.Name(), tc.call)
|
|
} else if got := called.CalledAs(); got != tc.want {
|
|
t.Errorf("%s.CalledAs() == %q; Wanted: %q", tc.call, got, tc.want)
|
|
}
|
|
}
|
|
|
|
func TestCalledAs(t *testing.T) {
|
|
tests := map[string]calledAsTestcase{
|
|
"find/no-args": {nil, "parent", "parent", false},
|
|
"find/real-name": {[]string{"child1"}, "child1", "child1", false},
|
|
"find/full-alias": {[]string{"that"}, "child2", "that", false},
|
|
"find/part-no-prefix": {[]string{"thi"}, "", "", false},
|
|
"find/part-alias": {[]string{"thi"}, "child1", "this", true},
|
|
"find/conflict": {[]string{"th"}, "", "", true},
|
|
"traverse/no-args": {nil, "parent", "parent", false},
|
|
"traverse/real-name": {[]string{"child1"}, "child1", "child1", false},
|
|
"traverse/full-alias": {[]string{"that"}, "child2", "that", false},
|
|
"traverse/part-no-prefix": {[]string{"thi"}, "", "", false},
|
|
"traverse/part-alias": {[]string{"thi"}, "child1", "this", true},
|
|
"traverse/conflict": {[]string{"th"}, "", "", true},
|
|
}
|
|
|
|
for name, tc := range tests {
|
|
t.Run(name, tc.test)
|
|
}
|
|
}
|
|
|
|
func TestFParseErrWhitelistBackwardCompatibility(t *testing.T) {
|
|
c := &Command{Use: "c", Run: emptyRun}
|
|
c.Flags().BoolP("boola", "a", false, "a boolean flag")
|
|
|
|
output, err := executeCommand(c, "c", "-a", "--unknown", "flag")
|
|
if err == nil {
|
|
t.Error("expected unknown flag error")
|
|
}
|
|
checkStringContains(t, output, "unknown flag: --unknown")
|
|
}
|
|
|
|
func TestFParseErrWhitelistSameCommand(t *testing.T) {
|
|
c := &Command{
|
|
Use: "c",
|
|
Run: emptyRun,
|
|
FParseErrWhitelist: FParseErrWhitelist{
|
|
UnknownFlags: true,
|
|
},
|
|
}
|
|
c.Flags().BoolP("boola", "a", false, "a boolean flag")
|
|
|
|
_, err := executeCommand(c, "c", "-a", "--unknown", "flag")
|
|
if err != nil {
|
|
t.Error("unexpected error: ", err)
|
|
}
|
|
}
|
|
|
|
func TestFParseErrWhitelistParentCommand(t *testing.T) {
|
|
root := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
FParseErrWhitelist: FParseErrWhitelist{
|
|
UnknownFlags: true,
|
|
},
|
|
}
|
|
|
|
c := &Command{
|
|
Use: "child",
|
|
Run: emptyRun,
|
|
}
|
|
c.Flags().BoolP("boola", "a", false, "a boolean flag")
|
|
|
|
root.AddCommand(c)
|
|
|
|
output, err := executeCommand(root, "child", "-a", "--unknown", "flag")
|
|
if err == nil {
|
|
t.Error("expected unknown flag error")
|
|
}
|
|
checkStringContains(t, output, "unknown flag: --unknown")
|
|
}
|
|
|
|
func TestFParseErrWhitelistChildCommand(t *testing.T) {
|
|
root := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
|
|
c := &Command{
|
|
Use: "child",
|
|
Run: emptyRun,
|
|
FParseErrWhitelist: FParseErrWhitelist{
|
|
UnknownFlags: true,
|
|
},
|
|
}
|
|
c.Flags().BoolP("boola", "a", false, "a boolean flag")
|
|
|
|
root.AddCommand(c)
|
|
|
|
_, err := executeCommand(root, "child", "-a", "--unknown", "flag")
|
|
if err != nil {
|
|
t.Error("unexpected error: ", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestFParseErrWhitelistSiblingCommand(t *testing.T) {
|
|
root := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
|
|
c := &Command{
|
|
Use: "child",
|
|
Run: emptyRun,
|
|
FParseErrWhitelist: FParseErrWhitelist{
|
|
UnknownFlags: true,
|
|
},
|
|
}
|
|
c.Flags().BoolP("boola", "a", false, "a boolean flag")
|
|
|
|
s := &Command{
|
|
Use: "sibling",
|
|
Run: emptyRun,
|
|
}
|
|
s.Flags().BoolP("boolb", "b", false, "a boolean flag")
|
|
|
|
root.AddCommand(c)
|
|
root.AddCommand(s)
|
|
|
|
output, err := executeCommand(root, "sibling", "-b", "--unknown", "flag")
|
|
if err == nil {
|
|
t.Error("expected unknown flag error")
|
|
}
|
|
checkStringContains(t, output, "unknown flag: --unknown")
|
|
}
|
|
|
|
func TestSetContext(t *testing.T) {
|
|
type key struct{}
|
|
val := "foobar"
|
|
root := &Command{
|
|
Use: "root",
|
|
Run: func(cmd *Command, args []string) {
|
|
key := cmd.Context().Value(key{})
|
|
got, ok := key.(string)
|
|
if !ok {
|
|
t.Error("key not found in context")
|
|
}
|
|
if got != val {
|
|
t.Errorf("Expected value: \n %v\nGot:\n %v\n", val, got)
|
|
}
|
|
},
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), key{}, val)
|
|
root.SetContext(ctx)
|
|
err := root.Execute()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestSetContextPreRun(t *testing.T) {
|
|
type key struct{}
|
|
val := "barr"
|
|
root := &Command{
|
|
Use: "root",
|
|
PreRun: func(cmd *Command, args []string) {
|
|
ctx := context.WithValue(cmd.Context(), key{}, val)
|
|
cmd.SetContext(ctx)
|
|
},
|
|
Run: func(cmd *Command, args []string) {
|
|
val := cmd.Context().Value(key{})
|
|
got, ok := val.(string)
|
|
if !ok {
|
|
t.Error("key not found in context")
|
|
}
|
|
if got != val {
|
|
t.Errorf("Expected value: \n %v\nGot:\n %v\n", val, got)
|
|
}
|
|
},
|
|
}
|
|
err := root.Execute()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestSetContextPreRunOverwrite(t *testing.T) {
|
|
type key struct{}
|
|
val := "blah"
|
|
root := &Command{
|
|
Use: "root",
|
|
Run: func(cmd *Command, args []string) {
|
|
key := cmd.Context().Value(key{})
|
|
_, ok := key.(string)
|
|
if ok {
|
|
t.Error("key found in context when not expected")
|
|
}
|
|
},
|
|
}
|
|
ctx := context.WithValue(context.Background(), key{}, val)
|
|
root.SetContext(ctx)
|
|
err := root.ExecuteContext(context.Background())
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestSetContextPersistentPreRun(t *testing.T) {
|
|
type key struct{}
|
|
val := "barbar"
|
|
root := &Command{
|
|
Use: "root",
|
|
PersistentPreRun: func(cmd *Command, args []string) {
|
|
ctx := context.WithValue(cmd.Context(), key{}, val)
|
|
cmd.SetContext(ctx)
|
|
},
|
|
}
|
|
child := &Command{
|
|
Use: "child",
|
|
Run: func(cmd *Command, args []string) {
|
|
key := cmd.Context().Value(key{})
|
|
got, ok := key.(string)
|
|
if !ok {
|
|
t.Error("key not found in context")
|
|
}
|
|
if got != val {
|
|
t.Errorf("Expected value: \n %v\nGot:\n %v\n", val, got)
|
|
}
|
|
},
|
|
}
|
|
root.AddCommand(child)
|
|
root.SetArgs([]string{"child"})
|
|
err := root.Execute()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
const VersionFlag = "--version"
|
|
const HelpFlag = "--help"
|
|
|
|
func TestNoRootRunCommandExecutedWithVersionSet(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Long: "Long description"}
|
|
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, rootCmd.Long)
|
|
checkStringContains(t, output, HelpFlag)
|
|
checkStringContains(t, output, VersionFlag)
|
|
}
|
|
|
|
func TestNoRootRunCommandExecutedWithoutVersionSet(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Long: "Long description"}
|
|
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, rootCmd.Long)
|
|
checkStringContains(t, output, HelpFlag)
|
|
checkStringOmits(t, output, VersionFlag)
|
|
}
|
|
|
|
func TestHelpCommandExecutedWithVersionSet(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Long: "Long description", Run: emptyRun}
|
|
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, "help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, rootCmd.Long)
|
|
checkStringContains(t, output, HelpFlag)
|
|
checkStringContains(t, output, VersionFlag)
|
|
}
|
|
|
|
func TestHelpCommandExecutedWithoutVersionSet(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
|
|
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, "help")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, rootCmd.Long)
|
|
checkStringContains(t, output, HelpFlag)
|
|
checkStringOmits(t, output, VersionFlag)
|
|
}
|
|
|
|
func TestHelpflagCommandExecutedWithVersionSet(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Version: "1.0.0", Long: "Long description", Run: emptyRun}
|
|
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, HelpFlag)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, rootCmd.Long)
|
|
checkStringContains(t, output, HelpFlag)
|
|
checkStringContains(t, output, VersionFlag)
|
|
}
|
|
|
|
func TestHelpflagCommandExecutedWithoutVersionSet(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Long: "Long description", Run: emptyRun}
|
|
rootCmd.AddCommand(&Command{Use: "child", Run: emptyRun})
|
|
|
|
output, err := executeCommand(rootCmd, HelpFlag)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
checkStringContains(t, output, rootCmd.Long)
|
|
checkStringContains(t, output, HelpFlag)
|
|
checkStringOmits(t, output, VersionFlag)
|
|
}
|
|
|
|
func TestFind(t *testing.T) {
|
|
var foo, bar string
|
|
var persist bool
|
|
root := &Command{
|
|
Use: "root",
|
|
}
|
|
root.PersistentFlags().StringVarP(&foo, "foo", "f", "", "")
|
|
root.PersistentFlags().StringVarP(&bar, "bar", "b", "something", "")
|
|
root.PersistentFlags().BoolVarP(&persist, "persist", "p", false, "")
|
|
|
|
child := &Command{
|
|
Use: "child",
|
|
}
|
|
root.AddCommand(child)
|
|
|
|
testCases := []struct {
|
|
args []string
|
|
expectedFoundArgs []string
|
|
}{
|
|
{
|
|
[]string{"child"},
|
|
[]string{},
|
|
},
|
|
{
|
|
[]string{"child", "child"},
|
|
[]string{"child"},
|
|
},
|
|
{
|
|
[]string{"child", "foo", "child", "bar", "child", "baz", "child"},
|
|
[]string{"foo", "child", "bar", "child", "baz", "child"},
|
|
},
|
|
{
|
|
[]string{"-f", "child", "child"},
|
|
[]string{"-f", "child"},
|
|
},
|
|
{
|
|
[]string{"child", "-f", "child"},
|
|
[]string{"-f", "child"},
|
|
},
|
|
{
|
|
[]string{"-b", "child", "child"},
|
|
[]string{"-b", "child"},
|
|
},
|
|
{
|
|
[]string{"child", "-b", "child"},
|
|
[]string{"-b", "child"},
|
|
},
|
|
{
|
|
[]string{"child", "-b"},
|
|
[]string{"-b"},
|
|
},
|
|
{
|
|
[]string{"-b", "-f", "child", "child"},
|
|
[]string{"-b", "-f", "child"},
|
|
},
|
|
{
|
|
[]string{"-f", "child", "-b", "something", "child"},
|
|
[]string{"-f", "child", "-b", "something"},
|
|
},
|
|
{
|
|
[]string{"-f", "child", "child", "-b"},
|
|
[]string{"-f", "child", "-b"},
|
|
},
|
|
{
|
|
[]string{"-f=child", "-b=something", "child"},
|
|
[]string{"-f=child", "-b=something"},
|
|
},
|
|
{
|
|
[]string{"--foo", "child", "--bar", "something", "child"},
|
|
[]string{"--foo", "child", "--bar", "something"},
|
|
},
|
|
{
|
|
[]string{"-f", "value", "child"},
|
|
[]string{"-f", "value"},
|
|
},
|
|
{
|
|
[]string{"-f=value", "child"},
|
|
[]string{"-f=value"},
|
|
},
|
|
{
|
|
[]string{"-fvalue", "child"},
|
|
[]string{"-fvalue"},
|
|
},
|
|
{
|
|
[]string{"-pf", "value", "child"},
|
|
[]string{"-pf", "value"},
|
|
},
|
|
{
|
|
[]string{"-pf=value", "child"},
|
|
[]string{"-pf=value"},
|
|
},
|
|
{
|
|
[]string{"-pfvalue", "child"},
|
|
[]string{"-pfvalue"},
|
|
},
|
|
{
|
|
[]string{"-pf", "child", "child"},
|
|
[]string{"-pf", "child"},
|
|
},
|
|
{
|
|
[]string{"-pf", "child", "-pb", "something", "child"},
|
|
[]string{"-pf", "child", "-pb", "something"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(fmt.Sprintf("%v", tc.args), func(t *testing.T) {
|
|
cmd, foundArgs, err := root.Find(tc.args)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if cmd != child {
|
|
t.Fatal("Expected cmd to be child, but it was not")
|
|
}
|
|
|
|
if !reflect.DeepEqual(tc.expectedFoundArgs, foundArgs) {
|
|
t.Fatalf("Wrong args\nExpected: %v\nGot: %v", tc.expectedFoundArgs, foundArgs)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUnknownFlagShouldReturnSameErrorRegardlessOfArgPosition(t *testing.T) {
|
|
testCases := [][]string{
|
|
// {"--unknown", "--namespace", "foo", "child", "--bar"}, // FIXME: This test case fails, returning the error `unknown command "foo" for "root"` instead of the expected error `unknown flag: --unknown`
|
|
{"--namespace", "foo", "--unknown", "child", "--bar"},
|
|
{"--namespace", "foo", "child", "--unknown", "--bar"},
|
|
{"--namespace", "foo", "child", "--bar", "--unknown"},
|
|
|
|
{"--unknown", "--namespace=foo", "child", "--bar"},
|
|
{"--namespace=foo", "--unknown", "child", "--bar"},
|
|
{"--namespace=foo", "child", "--unknown", "--bar"},
|
|
{"--namespace=foo", "child", "--bar", "--unknown"},
|
|
|
|
{"--unknown", "--namespace=foo", "child", "--bar=true"},
|
|
{"--namespace=foo", "--unknown", "child", "--bar=true"},
|
|
{"--namespace=foo", "child", "--unknown", "--bar=true"},
|
|
{"--namespace=foo", "child", "--bar=true", "--unknown"},
|
|
}
|
|
|
|
root := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
root.PersistentFlags().String("namespace", "", "a string flag")
|
|
|
|
c := &Command{
|
|
Use: "child",
|
|
Run: emptyRun,
|
|
}
|
|
c.Flags().Bool("bar", false, "a boolean flag")
|
|
|
|
root.AddCommand(c)
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(strings.Join(tc, " "), func(t *testing.T) {
|
|
output, err := executeCommand(root, tc...)
|
|
if err == nil {
|
|
t.Error("expected unknown flag error")
|
|
}
|
|
checkStringContains(t, output, "unknown flag: --unknown")
|
|
})
|
|
}
|
|
}
|