urfave-cli/app_test.go
Jesse Szwedko f585ec7cb8 Remove reordering of flags and arguments
This was introduced by #36, but only worked in the specific case of all
arguments being passed before all flags. If the user mixed them, they
ended up with odd parsing behavior where the arguments were reordered
(causing #103 and #355).

Given the tradeoffs I think we should remove support for flag
reordering.

Fixes #103
Fixes #355
2016-05-07 16:36:54 -07:00

1287 lines
28 KiB
Go

package cli
import (
"bytes"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"strings"
"testing"
)
type opCounts struct {
Total, BashComplete, OnUsageError, Before, CommandNotFound, Action, After, SubCommand int
}
func ExampleApp_Run() {
// set args for examples sake
os.Args = []string{"greet", "--name", "Jeremy"}
app := NewApp()
app.Name = "greet"
app.Flags = []Flag{
StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
}
app.Action = func(c *Context) error {
fmt.Printf("Hello %v\n", c.String("name"))
return nil
}
app.UsageText = "app [first_arg] [second_arg]"
app.Author = "Harrison"
app.Email = "harrison@lolwut.com"
app.Authors = []Author{Author{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
app.Run(os.Args)
// Output:
// Hello Jeremy
}
func ExampleApp_Run_subcommand() {
// set args for examples sake
os.Args = []string{"say", "hi", "english", "--name", "Jeremy"}
app := NewApp()
app.Name = "say"
app.Commands = []Command{
{
Name: "hello",
Aliases: []string{"hi"},
Usage: "use it to see a description",
Description: "This is how we describe hello the function",
Subcommands: []Command{
{
Name: "english",
Aliases: []string{"en"},
Usage: "sends a greeting in english",
Description: "greets someone in english",
Flags: []Flag{
StringFlag{
Name: "name",
Value: "Bob",
Usage: "Name of the person to greet",
},
},
Action: func(c *Context) error {
fmt.Println("Hello,", c.String("name"))
return nil
},
},
},
},
}
app.Run(os.Args)
// Output:
// Hello, Jeremy
}
func ExampleApp_Run_help() {
// set args for examples sake
os.Args = []string{"greet", "h", "describeit"}
app := NewApp()
app.Name = "greet"
app.Flags = []Flag{
StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
}
app.Commands = []Command{
{
Name: "describeit",
Aliases: []string{"d"},
Usage: "use it to see a description",
Description: "This is how we describe describeit the function",
Action: func(c *Context) error {
fmt.Printf("i like to describe things")
return nil
},
},
}
app.Run(os.Args)
// Output:
// NAME:
// greet describeit - use it to see a description
//
// USAGE:
// greet describeit [arguments...]
//
// DESCRIPTION:
// This is how we describe describeit the function
}
func ExampleApp_Run_bashComplete() {
// set args for examples sake
os.Args = []string{"greet", "--generate-bash-completion"}
app := NewApp()
app.Name = "greet"
app.EnableBashCompletion = true
app.Commands = []Command{
{
Name: "describeit",
Aliases: []string{"d"},
Usage: "use it to see a description",
Description: "This is how we describe describeit the function",
Action: func(c *Context) error {
fmt.Printf("i like to describe things")
return nil
},
}, {
Name: "next",
Usage: "next example",
Description: "more stuff to see when generating bash completion",
Action: func(c *Context) error {
fmt.Printf("the next example")
return nil
},
},
}
app.Run(os.Args)
// Output:
// describeit
// d
// next
// help
// h
}
func TestApp_Run(t *testing.T) {
s := ""
app := NewApp()
app.Action = func(c *Context) error {
s = s + c.Args().First()
return nil
}
err := app.Run([]string{"command", "foo"})
expect(t, err, nil)
err = app.Run([]string{"command", "bar"})
expect(t, err, nil)
expect(t, s, "foobar")
}
var commandAppTests = []struct {
name string
expected bool
}{
{"foobar", true},
{"batbaz", true},
{"b", true},
{"f", true},
{"bat", false},
{"nothing", false},
}
func TestApp_Command(t *testing.T) {
app := NewApp()
fooCommand := Command{Name: "foobar", Aliases: []string{"f"}}
batCommand := Command{Name: "batbaz", Aliases: []string{"b"}}
app.Commands = []Command{
fooCommand,
batCommand,
}
for _, test := range commandAppTests {
expect(t, app.Command(test.name) != nil, test.expected)
}
}
func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
var context *Context
a := NewApp()
a.Commands = []Command{
{
Name: "foo",
Action: func(c *Context) error {
context = c
return nil
},
Flags: []Flag{
StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
},
},
Before: func(_ *Context) error { return nil },
},
}
a.Run([]string{"", "foo", "--lang", "spanish", "abcd"})
expect(t, context.Args().Get(0), "abcd")
expect(t, context.String("lang"), "spanish")
}
func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
var parsedOption string
var args []string
app := NewApp()
command := Command{
Name: "cmd",
Flags: []Flag{
StringFlag{Name: "option", Value: "", Usage: "some option"},
},
Action: func(c *Context) error {
parsedOption = c.String("option")
args = c.Args()
return nil
},
}
app.Commands = []Command{command}
app.Run([]string{"", "cmd", "--option", "my-option", "my-arg", "--", "--notARealFlag"})
expect(t, parsedOption, "my-option")
expect(t, args[0], "my-arg")
expect(t, args[1], "--")
expect(t, args[2], "--notARealFlag")
}
func TestApp_CommandWithDash(t *testing.T) {
var args []string
app := NewApp()
command := Command{
Name: "cmd",
Action: func(c *Context) error {
args = c.Args()
return nil
},
}
app.Commands = []Command{command}
app.Run([]string{"", "cmd", "my-arg", "-"})
expect(t, args[0], "my-arg")
expect(t, args[1], "-")
}
func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
var args []string
app := NewApp()
command := Command{
Name: "cmd",
Action: func(c *Context) error {
args = c.Args()
return nil
},
}
app.Commands = []Command{command}
app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"})
expect(t, args[0], "my-arg")
expect(t, args[1], "--")
expect(t, args[2], "notAFlagAtAll")
}
func TestApp_Float64Flag(t *testing.T) {
var meters float64
app := NewApp()
app.Flags = []Flag{
Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"},
}
app.Action = func(c *Context) error {
meters = c.Float64("height")
return nil
}
app.Run([]string{"", "--height", "1.93"})
expect(t, meters, 1.93)
}
func TestApp_ParseSliceFlags(t *testing.T) {
var parsedOption, firstArg string
var parsedIntSlice []int
var parsedStringSlice []string
app := NewApp()
command := Command{
Name: "cmd",
Flags: []Flag{
IntSliceFlag{Name: "p", Value: NewIntSlice(), Usage: "set one or more ip addr"},
StringSliceFlag{Name: "ip", Value: NewStringSlice(), Usage: "set one or more ports to open"},
},
Action: func(c *Context) error {
parsedIntSlice = c.IntSlice("p")
parsedStringSlice = c.StringSlice("ip")
parsedOption = c.String("option")
firstArg = c.Args().First()
return nil
},
}
app.Commands = []Command{command}
app.Run([]string{"", "cmd", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4", "my-arg"})
IntsEquals := func(a, b []int) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
StrsEquals := func(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
var expectedIntSlice = []int{22, 80}
var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"}
if !IntsEquals(parsedIntSlice, expectedIntSlice) {
t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice)
}
if !StrsEquals(parsedStringSlice, expectedStringSlice) {
t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice)
}
}
func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) {
var parsedIntSlice []int
var parsedStringSlice []string
app := NewApp()
command := Command{
Name: "cmd",
Flags: []Flag{
IntSliceFlag{Name: "a", Usage: "set numbers"},
StringSliceFlag{Name: "str", Usage: "set strings"},
},
Action: func(c *Context) error {
parsedIntSlice = c.IntSlice("a")
parsedStringSlice = c.StringSlice("str")
return nil
},
}
app.Commands = []Command{command}
app.Run([]string{"", "cmd", "-a", "2", "-str", "A", "my-arg"})
var expectedIntSlice = []int{2}
var expectedStringSlice = []string{"A"}
if parsedIntSlice[0] != expectedIntSlice[0] {
t.Errorf("%v does not match %v", parsedIntSlice[0], expectedIntSlice[0])
}
if parsedStringSlice[0] != expectedStringSlice[0] {
t.Errorf("%v does not match %v", parsedIntSlice[0], expectedIntSlice[0])
}
}
func TestApp_DefaultStdout(t *testing.T) {
app := NewApp()
if app.Writer != os.Stdout {
t.Error("Default output writer not set.")
}
}
type mockWriter struct {
written []byte
}
func (fw *mockWriter) Write(p []byte) (n int, err error) {
if fw.written == nil {
fw.written = p
} else {
fw.written = append(fw.written, p...)
}
return len(p), nil
}
func (fw *mockWriter) GetWritten() (b []byte) {
return fw.written
}
func TestApp_SetStdout(t *testing.T) {
w := &mockWriter{}
app := NewApp()
app.Name = "test"
app.Writer = w
err := app.Run([]string{"help"})
if err != nil {
t.Fatalf("Run error: %s", err)
}
if len(w.written) == 0 {
t.Error("App did not write output to desired writer.")
}
}
func TestApp_BeforeFunc(t *testing.T) {
counts := &opCounts{}
beforeError := fmt.Errorf("fail")
var err error
app := NewApp()
app.Before = func(c *Context) error {
counts.Total++
counts.Before = counts.Total
s := c.String("opt")
if s == "fail" {
return beforeError
}
return nil
}
app.Commands = []Command{
Command{
Name: "sub",
Action: func(c *Context) error {
counts.Total++
counts.SubCommand = counts.Total
return nil
},
},
}
app.Flags = []Flag{
StringFlag{Name: "opt"},
}
// run with the Before() func succeeding
err = app.Run([]string{"command", "--opt", "succeed", "sub"})
if err != nil {
t.Fatalf("Run error: %s", err)
}
if counts.Before != 1 {
t.Errorf("Before() not executed when expected")
}
if counts.SubCommand != 2 {
t.Errorf("Subcommand not executed when expected")
}
// reset
counts = &opCounts{}
// run with the Before() func failing
err = app.Run([]string{"command", "--opt", "fail", "sub"})
// should be the same error produced by the Before func
if err != beforeError {
t.Errorf("Run error expected, but not received")
}
if counts.Before != 1 {
t.Errorf("Before() not executed when expected")
}
if counts.SubCommand != 0 {
t.Errorf("Subcommand executed when NOT expected")
}
// reset
counts = &opCounts{}
afterError := errors.New("fail again")
app.After = func(_ *Context) error {
return afterError
}
// run with the Before() func failing, wrapped by After()
err = app.Run([]string{"command", "--opt", "fail", "sub"})
// should be the same error produced by the Before func
if _, ok := err.(MultiError); !ok {
t.Errorf("MultiError expected, but not received")
}
if counts.Before != 1 {
t.Errorf("Before() not executed when expected")
}
if counts.SubCommand != 0 {
t.Errorf("Subcommand executed when NOT expected")
}
}
func TestApp_AfterFunc(t *testing.T) {
counts := &opCounts{}
afterError := fmt.Errorf("fail")
var err error
app := NewApp()
app.After = func(c *Context) error {
counts.Total++
counts.After = counts.Total
s := c.String("opt")
if s == "fail" {
return afterError
}
return nil
}
app.Commands = []Command{
Command{
Name: "sub",
Action: func(c *Context) error {
counts.Total++
counts.SubCommand = counts.Total
return nil
},
},
}
app.Flags = []Flag{
StringFlag{Name: "opt"},
}
// run with the After() func succeeding
err = app.Run([]string{"command", "--opt", "succeed", "sub"})
if err != nil {
t.Fatalf("Run error: %s", err)
}
if counts.After != 2 {
t.Errorf("After() not executed when expected")
}
if counts.SubCommand != 1 {
t.Errorf("Subcommand not executed when expected")
}
// reset
counts = &opCounts{}
// run with the Before() func failing
err = app.Run([]string{"command", "--opt", "fail", "sub"})
// should be the same error produced by the Before func
if err != afterError {
t.Errorf("Run error expected, but not received")
}
if counts.After != 2 {
t.Errorf("After() not executed when expected")
}
if counts.SubCommand != 1 {
t.Errorf("Subcommand not executed when expected")
}
}
func TestAppNoHelpFlag(t *testing.T) {
oldFlag := HelpFlag
defer func() {
HelpFlag = oldFlag
}()
HelpFlag = BoolFlag{}
app := NewApp()
app.Writer = ioutil.Discard
err := app.Run([]string{"test", "-h"})
if err != flag.ErrHelp {
t.Errorf("expected error about missing help flag, but got: %s (%T)", err, err)
}
}
func TestAppHelpPrinter(t *testing.T) {
oldPrinter := HelpPrinter
defer func() {
HelpPrinter = oldPrinter
}()
var wasCalled = false
HelpPrinter = func(w io.Writer, template string, data interface{}) {
wasCalled = true
}
app := NewApp()
app.Run([]string{"-h"})
if wasCalled == false {
t.Errorf("Help printer expected to be called, but was not")
}
}
func TestApp_VersionPrinter(t *testing.T) {
oldPrinter := VersionPrinter
defer func() {
VersionPrinter = oldPrinter
}()
var wasCalled = false
VersionPrinter = func(c *Context) {
wasCalled = true
}
app := NewApp()
ctx := NewContext(app, nil, nil)
ShowVersion(ctx)
if wasCalled == false {
t.Errorf("Version printer expected to be called, but was not")
}
}
func TestApp_CommandNotFound(t *testing.T) {
counts := &opCounts{}
app := NewApp()
app.CommandNotFound = func(c *Context, command string) {
counts.Total++
counts.CommandNotFound = counts.Total
}
app.Commands = []Command{
Command{
Name: "bar",
Action: func(c *Context) error {
counts.Total++
counts.SubCommand = counts.Total
return nil
},
},
}
app.Run([]string{"command", "foo"})
expect(t, counts.CommandNotFound, 1)
expect(t, counts.SubCommand, 0)
expect(t, counts.Total, 1)
}
func TestApp_OrderOfOperations(t *testing.T) {
counts := &opCounts{}
resetCounts := func() { counts = &opCounts{} }
app := NewApp()
app.EnableBashCompletion = true
app.BashComplete = func(c *Context) {
counts.Total++
counts.BashComplete = counts.Total
}
app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
counts.Total++
counts.OnUsageError = counts.Total
return errors.New("hay OnUsageError")
}
beforeNoError := func(c *Context) error {
counts.Total++
counts.Before = counts.Total
return nil
}
beforeError := func(c *Context) error {
counts.Total++
counts.Before = counts.Total
return errors.New("hay Before")
}
app.Before = beforeNoError
app.CommandNotFound = func(c *Context, command string) {
counts.Total++
counts.CommandNotFound = counts.Total
}
afterNoError := func(c *Context) error {
counts.Total++
counts.After = counts.Total
return nil
}
afterError := func(c *Context) error {
counts.Total++
counts.After = counts.Total
return errors.New("hay After")
}
app.After = afterNoError
app.Commands = []Command{
Command{
Name: "bar",
Action: func(c *Context) error {
counts.Total++
counts.SubCommand = counts.Total
return nil
},
},
}
app.Action = func(c *Context) error {
counts.Total++
counts.Action = counts.Total
return nil
}
_ = app.Run([]string{"command", "--nope"})
expect(t, counts.OnUsageError, 1)
expect(t, counts.Total, 1)
resetCounts()
_ = app.Run([]string{"command", "--generate-bash-completion"})
expect(t, counts.BashComplete, 1)
expect(t, counts.Total, 1)
resetCounts()
oldOnUsageError := app.OnUsageError
app.OnUsageError = nil
_ = app.Run([]string{"command", "--nope"})
expect(t, counts.Total, 0)
app.OnUsageError = oldOnUsageError
resetCounts()
_ = app.Run([]string{"command", "foo"})
expect(t, counts.OnUsageError, 0)
expect(t, counts.Before, 1)
expect(t, counts.CommandNotFound, 0)
expect(t, counts.Action, 2)
expect(t, counts.After, 3)
expect(t, counts.Total, 3)
resetCounts()
app.Before = beforeError
_ = app.Run([]string{"command", "bar"})
expect(t, counts.OnUsageError, 0)
expect(t, counts.Before, 1)
expect(t, counts.After, 2)
expect(t, counts.Total, 2)
app.Before = beforeNoError
resetCounts()
app.After = nil
_ = app.Run([]string{"command", "bar"})
expect(t, counts.OnUsageError, 0)
expect(t, counts.Before, 1)
expect(t, counts.SubCommand, 2)
expect(t, counts.Total, 2)
app.After = afterNoError
resetCounts()
app.After = afterError
err := app.Run([]string{"command", "bar"})
if err == nil {
t.Fatalf("expected a non-nil error")
}
expect(t, counts.OnUsageError, 0)
expect(t, counts.Before, 1)
expect(t, counts.SubCommand, 2)
expect(t, counts.After, 3)
expect(t, counts.Total, 3)
app.After = afterNoError
resetCounts()
oldCommands := app.Commands
app.Commands = nil
_ = app.Run([]string{"command"})
expect(t, counts.OnUsageError, 0)
expect(t, counts.Before, 1)
expect(t, counts.Action, 2)
expect(t, counts.After, 3)
expect(t, counts.Total, 3)
app.Commands = oldCommands
}
func TestApp_Run_CommandWithSubcommandHasHelpTopic(t *testing.T) {
var subcommandHelpTopics = [][]string{
{"command", "foo", "--help"},
{"command", "foo", "-h"},
{"command", "foo", "help"},
}
for _, flagSet := range subcommandHelpTopics {
t.Logf("==> checking with flags %v", flagSet)
app := NewApp()
buf := new(bytes.Buffer)
app.Writer = buf
subCmdBar := Command{
Name: "bar",
Usage: "does bar things",
}
subCmdBaz := Command{
Name: "baz",
Usage: "does baz things",
}
cmd := Command{
Name: "foo",
Description: "descriptive wall of text about how it does foo things",
Subcommands: []Command{subCmdBar, subCmdBaz},
}
app.Commands = []Command{cmd}
err := app.Run(flagSet)
if err != nil {
t.Error(err)
}
output := buf.String()
t.Logf("output: %q\n", buf.Bytes())
if strings.Contains(output, "No help topic for") {
t.Errorf("expect a help topic, got none: \n%q", output)
}
for _, shouldContain := range []string{
cmd.Name, cmd.Description,
subCmdBar.Name, subCmdBar.Usage,
subCmdBaz.Name, subCmdBaz.Usage,
} {
if !strings.Contains(output, shouldContain) {
t.Errorf("want help to contain %q, did not: \n%q", shouldContain, output)
}
}
}
}
func TestApp_Run_SubcommandFullPath(t *testing.T) {
app := NewApp()
buf := new(bytes.Buffer)
app.Writer = buf
app.Name = "command"
subCmd := Command{
Name: "bar",
Usage: "does bar things",
}
cmd := Command{
Name: "foo",
Description: "foo commands",
Subcommands: []Command{subCmd},
}
app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "bar", "--help"})
if err != nil {
t.Error(err)
}
output := buf.String()
if !strings.Contains(output, "command foo bar - does bar things") {
t.Errorf("expected full path to subcommand: %s", output)
}
if !strings.Contains(output, "command foo bar [arguments...]") {
t.Errorf("expected full path to subcommand: %s", output)
}
}
func TestApp_Run_SubcommandHelpName(t *testing.T) {
app := NewApp()
buf := new(bytes.Buffer)
app.Writer = buf
app.Name = "command"
subCmd := Command{
Name: "bar",
HelpName: "custom",
Usage: "does bar things",
}
cmd := Command{
Name: "foo",
Description: "foo commands",
Subcommands: []Command{subCmd},
}
app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "bar", "--help"})
if err != nil {
t.Error(err)
}
output := buf.String()
if !strings.Contains(output, "custom - does bar things") {
t.Errorf("expected HelpName for subcommand: %s", output)
}
if !strings.Contains(output, "custom [arguments...]") {
t.Errorf("expected HelpName to subcommand: %s", output)
}
}
func TestApp_Run_CommandHelpName(t *testing.T) {
app := NewApp()
buf := new(bytes.Buffer)
app.Writer = buf
app.Name = "command"
subCmd := Command{
Name: "bar",
Usage: "does bar things",
}
cmd := Command{
Name: "foo",
HelpName: "custom",
Description: "foo commands",
Subcommands: []Command{subCmd},
}
app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "bar", "--help"})
if err != nil {
t.Error(err)
}
output := buf.String()
if !strings.Contains(output, "command foo bar - does bar things") {
t.Errorf("expected full path to subcommand: %s", output)
}
if !strings.Contains(output, "command foo bar [arguments...]") {
t.Errorf("expected full path to subcommand: %s", output)
}
}
func TestApp_Run_CommandSubcommandHelpName(t *testing.T) {
app := NewApp()
buf := new(bytes.Buffer)
app.Writer = buf
app.Name = "base"
subCmd := Command{
Name: "bar",
HelpName: "custom",
Usage: "does bar things",
}
cmd := Command{
Name: "foo",
Description: "foo commands",
Subcommands: []Command{subCmd},
}
app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "--help"})
if err != nil {
t.Error(err)
}
output := buf.String()
if !strings.Contains(output, "base foo - foo commands") {
t.Errorf("expected full path to subcommand: %s", output)
}
if !strings.Contains(output, "base foo command [command options] [arguments...]") {
t.Errorf("expected full path to subcommand: %s", output)
}
}
func TestApp_Run_Help(t *testing.T) {
var helpArguments = [][]string{{"boom", "--help"}, {"boom", "-h"}, {"boom", "help"}}
for _, args := range helpArguments {
buf := new(bytes.Buffer)
t.Logf("==> checking with arguments %v", args)
app := NewApp()
app.Name = "boom"
app.Usage = "make an explosive entrance"
app.Writer = buf
app.Action = func(c *Context) error {
buf.WriteString("boom I say!")
return nil
}
err := app.Run(args)
if err != nil {
t.Error(err)
}
output := buf.String()
t.Logf("output: %q\n", buf.Bytes())
if !strings.Contains(output, "boom - make an explosive entrance") {
t.Errorf("want help to contain %q, did not: \n%q", "boom - make an explosive entrance", output)
}
}
}
func TestApp_Run_Version(t *testing.T) {
var versionArguments = [][]string{{"boom", "--version"}, {"boom", "-v"}}
for _, args := range versionArguments {
buf := new(bytes.Buffer)
t.Logf("==> checking with arguments %v", args)
app := NewApp()
app.Name = "boom"
app.Usage = "make an explosive entrance"
app.Version = "0.1.0"
app.Writer = buf
app.Action = func(c *Context) error {
buf.WriteString("boom I say!")
return nil
}
err := app.Run(args)
if err != nil {
t.Error(err)
}
output := buf.String()
t.Logf("output: %q\n", buf.Bytes())
if !strings.Contains(output, "0.1.0") {
t.Errorf("want version to contain %q, did not: \n%q", "0.1.0", output)
}
}
}
func TestApp_Run_Categories(t *testing.T) {
app := NewApp()
app.Name = "categories"
app.Commands = []Command{
Command{
Name: "command1",
Category: "1",
},
Command{
Name: "command2",
Category: "1",
},
Command{
Name: "command3",
Category: "2",
},
}
buf := new(bytes.Buffer)
app.Writer = buf
app.Run([]string{"categories"})
expect := CommandCategories{
&CommandCategory{
Name: "1",
Commands: []Command{
app.Commands[0],
app.Commands[1],
},
},
&CommandCategory{
Name: "2",
Commands: []Command{
app.Commands[2],
},
},
}
if !reflect.DeepEqual(app.Categories(), expect) {
t.Fatalf("expected categories %#v, to equal %#v", app.Categories(), expect)
}
output := buf.String()
t.Logf("output: %q\n", buf.Bytes())
if !strings.Contains(output, "1:\n command1") {
t.Errorf("want buffer to include category %q, did not: \n%q", "1:\n command1", output)
}
}
func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
app := NewApp()
app.Action = func(c *Context) error { return nil }
app.Before = func(c *Context) error { return fmt.Errorf("before error") }
app.After = func(c *Context) error { return fmt.Errorf("after error") }
err := app.Run([]string{"foo"})
if err == nil {
t.Fatalf("expected to receive error from Run, got none")
}
if !strings.Contains(err.Error(), "before error") {
t.Errorf("expected text of error from Before method, but got none in \"%v\"", err)
}
if !strings.Contains(err.Error(), "after error") {
t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
}
}
func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
app := NewApp()
app.Commands = []Command{
Command{
Subcommands: []Command{
Command{
Name: "sub",
},
},
Name: "bar",
Before: func(c *Context) error { return fmt.Errorf("before error") },
After: func(c *Context) error { return fmt.Errorf("after error") },
},
}
err := app.Run([]string{"foo", "bar"})
if err == nil {
t.Fatalf("expected to receive error from Run, got none")
}
if !strings.Contains(err.Error(), "before error") {
t.Errorf("expected text of error from Before method, but got none in \"%v\"", err)
}
if !strings.Contains(err.Error(), "after error") {
t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
}
}
func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) {
app := NewApp()
app.Flags = []Flag{
IntFlag{Name: "flag"},
}
app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
if isSubcommand {
t.Errorf("Expect no subcommand")
}
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
}
return errors.New("intercepted: " + err.Error())
}
app.Commands = []Command{
Command{
Name: "bar",
},
}
err := app.Run([]string{"foo", "--flag=wrong"})
if err == nil {
t.Fatalf("expected to receive error from Run, got none")
}
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
}
}
func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) {
app := NewApp()
app.Flags = []Flag{
IntFlag{Name: "flag"},
}
app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
if isSubcommand {
t.Errorf("Expect subcommand")
}
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
}
return errors.New("intercepted: " + err.Error())
}
app.Commands = []Command{
Command{
Name: "bar",
},
}
err := app.Run([]string{"foo", "--flag=wrong", "bar"})
if err == nil {
t.Fatalf("expected to receive error from Run, got none")
}
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
}
}
func TestHandleAction_WithNonFuncAction(t *testing.T) {
app := NewApp()
app.Action = 42
err := HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
if err == nil {
t.Fatalf("expected to receive error from Run, got none")
}
exitErr, ok := err.(*ExitError)
if !ok {
t.Fatalf("expected to receive a *ExitError")
}
if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type") {
t.Fatalf("expected an unknown Action error, but got: %v", exitErr.Error())
}
if exitErr.ExitCode() != 2 {
t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode())
}
}
func TestHandleAction_WithInvalidFuncSignature(t *testing.T) {
app := NewApp()
app.Action = func() string { return "" }
err := HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
if err == nil {
t.Fatalf("expected to receive error from Run, got none")
}
exitErr, ok := err.(*ExitError)
if !ok {
t.Fatalf("expected to receive a *ExitError")
}
if !strings.HasPrefix(exitErr.Error(), "ERROR unknown Action error") {
t.Fatalf("expected an unknown Action error, but got: %v", exitErr.Error())
}
if exitErr.ExitCode() != 2 {
t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode())
}
}
func TestHandleAction_WithInvalidFuncReturnSignature(t *testing.T) {
app := NewApp()
app.Action = func(_ *Context) (int, error) { return 0, nil }
err := HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
if err == nil {
t.Fatalf("expected to receive error from Run, got none")
}
exitErr, ok := err.(*ExitError)
if !ok {
t.Fatalf("expected to receive a *ExitError")
}
if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action signature") {
t.Fatalf("expected an invalid Action signature error, but got: %v", exitErr.Error())
}
if exitErr.ExitCode() != 2 {
t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode())
}
}