Ensure HandleAction/HandleExitCoder work correctly with *ExitError

Closes #376
This commit is contained in:
Dan Buch 2016-05-02 11:25:37 -04:00
parent a90e2e4ff1
commit 4b4c07bd4f
No known key found for this signature in database
GPG Key ID: FAEF12936DD3E3EC
3 changed files with 75 additions and 13 deletions

10
app.go
View File

@ -168,9 +168,7 @@ func (a *App) Run(arguments []string) (err error) {
if err != nil { if err != nil {
if a.OnUsageError != nil { if a.OnUsageError != nil {
err := a.OnUsageError(context, err, false) err := a.OnUsageError(context, err, false)
if err != nil {
HandleExitCoder(err) HandleExitCoder(err)
}
return err return err
} else { } else {
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
@ -224,9 +222,7 @@ func (a *App) Run(arguments []string) (err error) {
// Run default Action // Run default Action
err = HandleAction(a.Action, context) err = HandleAction(a.Action, context)
if err != nil {
HandleExitCoder(err) HandleExitCoder(err)
}
return err return err
} }
@ -237,7 +233,7 @@ func (a *App) RunAndExitOnError() {
contactSysadmin, runAndExitOnErrorDeprecationURL) contactSysadmin, runAndExitOnErrorDeprecationURL)
if err := a.Run(os.Args); err != nil { if err := a.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) OsExiter(1)
} }
} }
@ -346,9 +342,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
// Run default Action // Run default Action
err = HandleAction(a.Action, context) err = HandleAction(a.Action, context)
if err != nil {
HandleExitCoder(err) HandleExitCoder(err)
}
return err return err
} }
@ -438,7 +432,7 @@ func HandleAction(action interface{}, context *Context) (err error) {
return errInvalidActionSignature return errInvalidActionSignature
} }
if retErr, ok := reflect.ValueOf(vals[0].Interface()).Interface().(error); ok { if retErr, ok := vals[0].Interface().(error); vals[0].IsValid() && ok {
return retErr return retErr
} }

View File

@ -6,6 +6,8 @@ import (
"strings" "strings"
) )
var OsExiter = os.Exit
type MultiError struct { type MultiError struct {
Errors []error Errors []error
} }
@ -26,6 +28,7 @@ func (m MultiError) Error() string {
// ExitCoder is the interface checked by `App` and `Command` for a custom exit // ExitCoder is the interface checked by `App` and `Command` for a custom exit
// code // code
type ExitCoder interface { type ExitCoder interface {
error
ExitCode() int ExitCode() int
} }
@ -56,15 +59,20 @@ func (ee *ExitError) ExitCode() int {
} }
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
// so prints the error to stderr (if it is non-empty) and calls os.Exit with the // so prints the error to stderr (if it is non-empty) and calls OsExiter with the
// given exit code. If the given error is a MultiError, then this func is // given exit code. If the given error is a MultiError, then this func is
// called on all members of the Errors slice. // called on all members of the Errors slice.
func HandleExitCoder(err error) { func HandleExitCoder(err error) {
if err == nil {
return
}
if exitErr, ok := err.(ExitCoder); ok { if exitErr, ok := err.(ExitCoder); ok {
if err.Error() != "" { if err.Error() != "" {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
} }
os.Exit(exitErr.ExitCode()) OsExiter(exitErr.ExitCode())
return
} }
if multiErr, ok := err.(MultiError); ok { if multiErr, ok := err.(MultiError); ok {

60
errors_test.go Normal file
View File

@ -0,0 +1,60 @@
package cli
import (
"errors"
"os"
"testing"
)
func TestHandleExitCoder_nil(t *testing.T) {
exitCode := 0
called := false
OsExiter = func(rc int) {
exitCode = rc
called = true
}
defer func() { OsExiter = os.Exit }()
HandleExitCoder(nil)
expect(t, exitCode, 0)
expect(t, called, false)
}
func TestHandleExitCoder_ExitCoder(t *testing.T) {
exitCode := 0
called := false
OsExiter = func(rc int) {
exitCode = rc
called = true
}
defer func() { OsExiter = os.Exit }()
HandleExitCoder(NewExitError("galactic perimiter breach", 9))
expect(t, exitCode, 9)
expect(t, called, true)
}
func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) {
exitCode := 0
called := false
OsExiter = func(rc int) {
exitCode = rc
called = true
}
defer func() { OsExiter = os.Exit }()
exitErr := NewExitError("galactic perimiter breach", 9)
err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr)
HandleExitCoder(err)
expect(t, exitCode, 9)
expect(t, called, true)
}