test coverage 100% !!
This commit is contained in:
parent
6a01a15f75
commit
fe4a138ac8
|
@ -95,6 +95,19 @@ func Example_mappings() {
|
||||||
// output: map[john:123 mary:456]
|
// output: map[john:123 mary:456]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This example demonstrates arguments with keys and values separated by commas
|
||||||
|
func Example_mappingsWithCommas() {
|
||||||
|
// The args you would pass in on the command line
|
||||||
|
os.Args = split("./example --userids john=123 mary=456")
|
||||||
|
|
||||||
|
var args struct {
|
||||||
|
UserIDs map[string]int
|
||||||
|
}
|
||||||
|
MustParse(&args)
|
||||||
|
fmt.Println(args.UserIDs)
|
||||||
|
// output: map[john:123 mary:456]
|
||||||
|
}
|
||||||
|
|
||||||
// This eample demonstrates multiple value arguments that can be mixed with
|
// This eample demonstrates multiple value arguments that can be mixed with
|
||||||
// other arguments.
|
// other arguments.
|
||||||
func Example_multipleMixed() {
|
func Example_multipleMixed() {
|
||||||
|
@ -130,6 +143,7 @@ func Example_helpText() {
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
// This is only necessary when running inside golang's runnable example harness
|
||||||
osExit = func(int) {}
|
osExit = func(int) {}
|
||||||
|
stdout = os.Stdout
|
||||||
|
|
||||||
MustParse(&args)
|
MustParse(&args)
|
||||||
|
|
||||||
|
@ -162,6 +176,7 @@ func Example_helpPlaceholder() {
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
// This is only necessary when running inside golang's runnable example harness
|
||||||
osExit = func(int) {}
|
osExit = func(int) {}
|
||||||
|
stdout = os.Stdout
|
||||||
|
|
||||||
MustParse(&args)
|
MustParse(&args)
|
||||||
|
|
||||||
|
@ -202,6 +217,7 @@ func Example_helpTextWithSubcommand() {
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
// This is only necessary when running inside golang's runnable example harness
|
||||||
osExit = func(int) {}
|
osExit = func(int) {}
|
||||||
|
stdout = os.Stdout
|
||||||
|
|
||||||
MustParse(&args)
|
MustParse(&args)
|
||||||
|
|
||||||
|
@ -239,6 +255,7 @@ func Example_helpTextForSubcommand() {
|
||||||
|
|
||||||
// This is only necessary when running inside golang's runnable example harness
|
// This is only necessary when running inside golang's runnable example harness
|
||||||
osExit = func(int) {}
|
osExit = func(int) {}
|
||||||
|
stdout = os.Stdout
|
||||||
|
|
||||||
MustParse(&args)
|
MustParse(&args)
|
||||||
|
|
||||||
|
|
31
parse.go
31
parse.go
|
@ -13,9 +13,6 @@ import (
|
||||||
scalar "github.com/alexflint/go-scalar"
|
scalar "github.com/alexflint/go-scalar"
|
||||||
)
|
)
|
||||||
|
|
||||||
// to enable monkey-patching during tests
|
|
||||||
var osExit = os.Exit
|
|
||||||
|
|
||||||
// path represents a sequence of steps to find the output location for an
|
// path represents a sequence of steps to find the output location for an
|
||||||
// argument or subcommand in the final destination struct
|
// argument or subcommand in the final destination struct
|
||||||
type path struct {
|
type path struct {
|
||||||
|
@ -80,7 +77,7 @@ var ErrVersion = errors.New("version requested by user")
|
||||||
func MustParse(dest ...interface{}) *Parser {
|
func MustParse(dest ...interface{}) *Parser {
|
||||||
p, err := NewParser(Config{}, dest...)
|
p, err := NewParser(Config{}, dest...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Fprintln(stdout, err)
|
||||||
osExit(-1)
|
osExit(-1)
|
||||||
return nil // just in case osExit was monkey-patched
|
return nil // just in case osExit was monkey-patched
|
||||||
}
|
}
|
||||||
|
@ -88,10 +85,10 @@ func MustParse(dest ...interface{}) *Parser {
|
||||||
err = p.Parse(flags())
|
err = p.Parse(flags())
|
||||||
switch {
|
switch {
|
||||||
case err == ErrHelp:
|
case err == ErrHelp:
|
||||||
p.writeHelpForCommand(os.Stdout, p.lastCmd)
|
p.writeHelpForCommand(stdout, p.lastCmd)
|
||||||
osExit(0)
|
osExit(0)
|
||||||
case err == ErrVersion:
|
case err == ErrVersion:
|
||||||
fmt.Println(p.version)
|
fmt.Fprintln(stdout, p.version)
|
||||||
osExit(0)
|
osExit(0)
|
||||||
case err != nil:
|
case err != nil:
|
||||||
p.failWithCommand(err.Error(), p.lastCmd)
|
p.failWithCommand(err.Error(), p.lastCmd)
|
||||||
|
@ -688,15 +685,7 @@ func (p *Parser) val(dest path) reflect.Value {
|
||||||
v = v.Elem()
|
v = v.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
next := v.FieldByIndex(field.Index)
|
v = v.FieldByIndex(field.Index)
|
||||||
if !next.IsValid() {
|
|
||||||
// it is appropriate to panic here because this can only happen due to
|
|
||||||
// an internal bug in this library (since we construct the path ourselves
|
|
||||||
// by reflecting on the same struct)
|
|
||||||
panic(fmt.Errorf("error resolving path %v: %v has no field named %v",
|
|
||||||
dest.fields, v.Type(), field))
|
|
||||||
}
|
|
||||||
v = next
|
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
@ -723,15 +712,3 @@ func findSubcommand(cmds []*command, name string) *command {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isZero returns true if v contains the zero value for its type
|
|
||||||
func isZero(v reflect.Value) bool {
|
|
||||||
t := v.Type()
|
|
||||||
if t.Kind() == reflect.Slice || t.Kind() == reflect.Map {
|
|
||||||
return v.IsNil()
|
|
||||||
}
|
|
||||||
if !t.Comparable() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return v.Interface() == reflect.Zero(t).Interface()
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package arg
|
package arg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"net"
|
"net"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"os"
|
"os"
|
||||||
|
@ -461,7 +462,7 @@ func TestMissingValueAtEnd(t *testing.T) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMissingValueInMIddle(t *testing.T) {
|
func TestMissingValueInMiddle(t *testing.T) {
|
||||||
var args struct {
|
var args struct {
|
||||||
Foo string
|
Foo string
|
||||||
Bar string
|
Bar string
|
||||||
|
@ -546,6 +547,14 @@ func TestNoMoreOptions(t *testing.T) {
|
||||||
assert.Equal(t, []string{"abc", "--foo", "xyz"}, args.Bar)
|
assert.Equal(t, []string{"abc", "--foo", "xyz"}, args.Bar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNoMoreOptionsBeforeHelp(t *testing.T) {
|
||||||
|
var args struct {
|
||||||
|
Foo int
|
||||||
|
}
|
||||||
|
err := parse("not_an_integer -- --help", &args)
|
||||||
|
assert.NotEqual(t, ErrHelp, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestHelpFlag(t *testing.T) {
|
func TestHelpFlag(t *testing.T) {
|
||||||
var args struct {
|
var args struct {
|
||||||
Foo string
|
Foo string
|
||||||
|
@ -1299,3 +1308,70 @@ func TestUnexportedFieldsSkipped(t *testing.T) {
|
||||||
_, err := NewParser(Config{}, &args)
|
_, err := NewParser(Config{}, &args)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMustParseInvalidParser(t *testing.T) {
|
||||||
|
originalExit := osExit
|
||||||
|
originalStdout := stdout
|
||||||
|
defer func() {
|
||||||
|
osExit = originalExit
|
||||||
|
stdout = originalStdout
|
||||||
|
}()
|
||||||
|
|
||||||
|
var exitCode int
|
||||||
|
osExit = func(code int) { exitCode = code }
|
||||||
|
stdout = &bytes.Buffer{}
|
||||||
|
|
||||||
|
var args struct {
|
||||||
|
CannotParse struct{}
|
||||||
|
}
|
||||||
|
parser := MustParse(&args)
|
||||||
|
assert.Nil(t, parser)
|
||||||
|
assert.Equal(t, -1, exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMustParsePrintsHelp(t *testing.T) {
|
||||||
|
originalExit := osExit
|
||||||
|
originalStdout := stdout
|
||||||
|
originalArgs := os.Args
|
||||||
|
defer func() {
|
||||||
|
osExit = originalExit
|
||||||
|
stdout = originalStdout
|
||||||
|
os.Args = originalArgs
|
||||||
|
}()
|
||||||
|
|
||||||
|
var exitCode *int
|
||||||
|
osExit = func(code int) { exitCode = &code }
|
||||||
|
os.Args = []string{"someprogram", "--help"}
|
||||||
|
stdout = &bytes.Buffer{}
|
||||||
|
|
||||||
|
var args struct{}
|
||||||
|
parser := MustParse(&args)
|
||||||
|
assert.NotNil(t, parser)
|
||||||
|
require.NotNil(t, exitCode)
|
||||||
|
assert.Equal(t, 0, *exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMustParsePrintsVersion(t *testing.T) {
|
||||||
|
originalExit := osExit
|
||||||
|
originalStdout := stdout
|
||||||
|
originalArgs := os.Args
|
||||||
|
defer func() {
|
||||||
|
osExit = originalExit
|
||||||
|
stdout = originalStdout
|
||||||
|
os.Args = originalArgs
|
||||||
|
}()
|
||||||
|
|
||||||
|
var exitCode *int
|
||||||
|
osExit = func(code int) { exitCode = &code }
|
||||||
|
os.Args = []string{"someprogram", "--version"}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
stdout = &b
|
||||||
|
|
||||||
|
var args versioned
|
||||||
|
parser := MustParse(&args)
|
||||||
|
require.NotNil(t, parser)
|
||||||
|
require.NotNil(t, exitCode)
|
||||||
|
assert.Equal(t, 0, *exitCode)
|
||||||
|
assert.Equal(t, "example 3.2.1\n", b.String())
|
||||||
|
}
|
||||||
|
|
12
reflect.go
12
reflect.go
|
@ -94,3 +94,15 @@ func isExported(field string) bool {
|
||||||
r, _ := utf8.DecodeRuneInString(field) // returns RuneError for empty string or invalid UTF8
|
r, _ := utf8.DecodeRuneInString(field) // returns RuneError for empty string or invalid UTF8
|
||||||
return unicode.IsLetter(r) && unicode.IsUpper(r)
|
return unicode.IsLetter(r) && unicode.IsUpper(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isZero returns true if v contains the zero value for its type
|
||||||
|
func isZero(v reflect.Value) bool {
|
||||||
|
t := v.Type()
|
||||||
|
if t.Kind() == reflect.Slice || t.Kind() == reflect.Map {
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
if !t.Comparable() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return v.Interface() == reflect.Zero(t).Interface()
|
||||||
|
}
|
||||||
|
|
|
@ -89,3 +89,24 @@ func TestCardinalityString(t *testing.T) {
|
||||||
assert.Equal(t, "unsupported", unsupported.String())
|
assert.Equal(t, "unsupported", unsupported.String())
|
||||||
assert.Equal(t, "unknown(42)", cardinality(42).String())
|
assert.Equal(t, "unknown(42)", cardinality(42).String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsZero(t *testing.T) {
|
||||||
|
var zero int
|
||||||
|
var notZero = 3
|
||||||
|
var nilSlice []int
|
||||||
|
var nonNilSlice = []int{1, 2, 3}
|
||||||
|
var nilMap map[string]string
|
||||||
|
var nonNilMap = map[string]string{"foo": "bar"}
|
||||||
|
var uncomparable = func() {}
|
||||||
|
|
||||||
|
assert.True(t, isZero(reflect.ValueOf(zero)))
|
||||||
|
assert.False(t, isZero(reflect.ValueOf(notZero)))
|
||||||
|
|
||||||
|
assert.True(t, isZero(reflect.ValueOf(nilSlice)))
|
||||||
|
assert.False(t, isZero(reflect.ValueOf(nonNilSlice)))
|
||||||
|
|
||||||
|
assert.True(t, isZero(reflect.ValueOf(nilMap)))
|
||||||
|
assert.False(t, isZero(reflect.ValueOf(nonNilMap)))
|
||||||
|
|
||||||
|
assert.False(t, isZero(reflect.ValueOf(uncomparable)))
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package arg
|
package arg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -48,6 +49,17 @@ func TestMinimalSubcommand(t *testing.T) {
|
||||||
assert.Equal(t, []string{"list"}, p.SubcommandNames())
|
assert.Equal(t, []string{"list"}, p.SubcommandNames())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSubcommandNamesBeforeParsing(t *testing.T) {
|
||||||
|
type listCmd struct{}
|
||||||
|
var args struct {
|
||||||
|
List *listCmd `arg:"subcommand"`
|
||||||
|
}
|
||||||
|
p, err := NewParser(Config{}, &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Nil(t, p.Subcommand())
|
||||||
|
assert.Nil(t, p.SubcommandNames())
|
||||||
|
}
|
||||||
|
|
||||||
func TestNoSuchSubcommand(t *testing.T) {
|
func TestNoSuchSubcommand(t *testing.T) {
|
||||||
type listCmd struct {
|
type listCmd struct {
|
||||||
}
|
}
|
||||||
|
@ -179,6 +191,36 @@ func TestSubcommandsWithOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSubcommandsWithEnvVars(t *testing.T) {
|
||||||
|
type getCmd struct {
|
||||||
|
Name string `arg:"env"`
|
||||||
|
}
|
||||||
|
type listCmd struct {
|
||||||
|
Limit int `arg:"env"`
|
||||||
|
}
|
||||||
|
type cmd struct {
|
||||||
|
Verbose bool
|
||||||
|
Get *getCmd `arg:"subcommand"`
|
||||||
|
List *listCmd `arg:"subcommand"`
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var args cmd
|
||||||
|
setenv(t, "LIMIT", "123")
|
||||||
|
err := parse("list", &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, args.List)
|
||||||
|
assert.Equal(t, 123, args.List.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var args cmd
|
||||||
|
setenv(t, "LIMIT", "not_an_integer")
|
||||||
|
err := parse("list", &args)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNestedSubcommands(t *testing.T) {
|
func TestNestedSubcommands(t *testing.T) {
|
||||||
type child struct{}
|
type child struct{}
|
||||||
type parent struct {
|
type parent struct {
|
||||||
|
@ -353,3 +395,19 @@ func TestSubcommandsWithMultiplePositionals(t *testing.T) {
|
||||||
assert.Equal(t, 5, args.Limit)
|
assert.Equal(t, 5, args.Limit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValForNilStruct(t *testing.T) {
|
||||||
|
type subcmd struct{}
|
||||||
|
var cmd struct {
|
||||||
|
Sub *subcmd `arg:"subcommand"`
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := NewParser(Config{}, &cmd)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
typ := reflect.TypeOf(cmd)
|
||||||
|
subField, _ := typ.FieldByName("Sub")
|
||||||
|
|
||||||
|
v := p.val(path{fields: []reflect.StructField{subField, subField}})
|
||||||
|
assert.False(t, v.IsValid())
|
||||||
|
}
|
||||||
|
|
6
usage.go
6
usage.go
|
@ -11,7 +11,11 @@ import (
|
||||||
const colWidth = 25
|
const colWidth = 25
|
||||||
|
|
||||||
// to allow monkey patching in tests
|
// to allow monkey patching in tests
|
||||||
var stderr = os.Stderr
|
var (
|
||||||
|
stdout io.Writer = os.Stdout
|
||||||
|
stderr io.Writer = os.Stderr
|
||||||
|
osExit = os.Exit
|
||||||
|
)
|
||||||
|
|
||||||
// Fail prints usage information to stderr and exits with non-zero status
|
// Fail prints usage information to stderr and exits with non-zero status
|
||||||
func (p *Parser) Fail(msg string) {
|
func (p *Parser) Fail(msg string) {
|
||||||
|
|
187
usage_test.go
187
usage_test.go
|
@ -33,9 +33,10 @@ func (n *NameDotName) MarshalText() (text []byte, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteUsage(t *testing.T) {
|
func TestWriteUsage(t *testing.T) {
|
||||||
expectedUsage := "Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--testenv TESTENV] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]\n"
|
expectedUsage := "Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--testenv TESTENV] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]"
|
||||||
|
|
||||||
expectedHelp := `Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--testenv TESTENV] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]
|
expectedHelp := `
|
||||||
|
Usage: example [--name NAME] [--value VALUE] [--verbose] [--dataset DATASET] [--optimize OPTIMIZE] [--ids IDS] [--values VALUES] [--workers WORKERS] [--testenv TESTENV] [--file FILE] INPUT [OUTPUT [OUTPUT ...]]
|
||||||
|
|
||||||
Positional arguments:
|
Positional arguments:
|
||||||
INPUT
|
INPUT
|
||||||
|
@ -56,6 +57,7 @@ Options:
|
||||||
--file FILE, -f FILE File with mandatory extension [default: scratch.txt]
|
--file FILE, -f FILE File with mandatory extension [default: scratch.txt]
|
||||||
--help, -h display this help and exit
|
--help, -h display this help and exit
|
||||||
`
|
`
|
||||||
|
|
||||||
var args struct {
|
var args struct {
|
||||||
Input string `arg:"positional"`
|
Input string `arg:"positional"`
|
||||||
Output []string `arg:"positional" help:"list of outputs"`
|
Output []string `arg:"positional" help:"list of outputs"`
|
||||||
|
@ -79,13 +81,13 @@ Options:
|
||||||
|
|
||||||
os.Args[0] = "example"
|
os.Args[0] = "example"
|
||||||
|
|
||||||
var usage bytes.Buffer
|
|
||||||
p.WriteUsage(&usage)
|
|
||||||
assert.Equal(t, expectedUsage, usage.String())
|
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
type MyEnum int
|
type MyEnum int
|
||||||
|
@ -99,7 +101,10 @@ func (n *MyEnum) MarshalText() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageWithDefaults(t *testing.T) {
|
func TestUsageWithDefaults(t *testing.T) {
|
||||||
expectedHelp := `Usage: example [--label LABEL] [--content CONTENT]
|
expectedUsage := "Usage: example [--label LABEL] [--content CONTENT]"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example [--label LABEL] [--content CONTENT]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--label LABEL [default: cat]
|
--label LABEL [default: cat]
|
||||||
|
@ -118,7 +123,11 @@ Options:
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageCannotMarshalToString(t *testing.T) {
|
func TestUsageCannotMarshalToString(t *testing.T) {
|
||||||
|
@ -132,7 +141,10 @@ func TestUsageCannotMarshalToString(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageLongPositionalWithHelp_legacyForm(t *testing.T) {
|
func TestUsageLongPositionalWithHelp_legacyForm(t *testing.T) {
|
||||||
expectedHelp := `Usage: example VERYLONGPOSITIONALWITHHELP
|
expectedUsage := "Usage: example VERYLONGPOSITIONALWITHHELP"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example VERYLONGPOSITIONALWITHHELP
|
||||||
|
|
||||||
Positional arguments:
|
Positional arguments:
|
||||||
VERYLONGPOSITIONALWITHHELP
|
VERYLONGPOSITIONALWITHHELP
|
||||||
|
@ -145,17 +157,23 @@ Options:
|
||||||
VeryLongPositionalWithHelp string `arg:"positional,help:this positional argument is very long but cannot include commas"`
|
VeryLongPositionalWithHelp string `arg:"positional,help:this positional argument is very long but cannot include commas"`
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := NewParser(Config{}, &args)
|
p, err := NewParser(Config{Program: "example"}, &args)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
os.Args[0] = "example"
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageLongPositionalWithHelp_newForm(t *testing.T) {
|
func TestUsageLongPositionalWithHelp_newForm(t *testing.T) {
|
||||||
expectedHelp := `Usage: example VERYLONGPOSITIONALWITHHELP
|
expectedUsage := "Usage: example VERYLONGPOSITIONALWITHHELP"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example VERYLONGPOSITIONALWITHHELP
|
||||||
|
|
||||||
Positional arguments:
|
Positional arguments:
|
||||||
VERYLONGPOSITIONALWITHHELP
|
VERYLONGPOSITIONALWITHHELP
|
||||||
|
@ -168,17 +186,23 @@ Options:
|
||||||
VeryLongPositionalWithHelp string `arg:"positional" help:"this positional argument is very long, and includes: commas, colons etc"`
|
VeryLongPositionalWithHelp string `arg:"positional" help:"this positional argument is very long, and includes: commas, colons etc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := NewParser(Config{}, &args)
|
p, err := NewParser(Config{Program: "example"}, &args)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
os.Args[0] = "example"
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageWithProgramName(t *testing.T) {
|
func TestUsageWithProgramName(t *testing.T) {
|
||||||
expectedHelp := `Usage: myprogram
|
expectedUsage := "Usage: myprogram"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: myprogram
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--help, -h display this help and exit
|
--help, -h display this help and exit
|
||||||
|
@ -190,9 +214,14 @@ Options:
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
os.Args[0] = "example"
|
os.Args[0] = "example"
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
type versioned struct{}
|
type versioned struct{}
|
||||||
|
@ -203,7 +232,10 @@ func (versioned) Version() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageWithVersion(t *testing.T) {
|
func TestUsageWithVersion(t *testing.T) {
|
||||||
expectedHelp := `example 3.2.1
|
expectedUsage := "example 3.2.1\nUsage: example"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
example 3.2.1
|
||||||
Usage: example
|
Usage: example
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
@ -216,12 +248,11 @@ Options:
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
actual := help.String()
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
if expectedHelp != actual {
|
|
||||||
t.Logf("Expected:\n%s", expectedHelp)
|
var usage bytes.Buffer
|
||||||
t.Logf("Actual:\n%s", actual)
|
p.WriteUsage(&usage)
|
||||||
t.Fail()
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type described struct{}
|
type described struct{}
|
||||||
|
@ -232,7 +263,10 @@ func (described) Description() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageWithDescription(t *testing.T) {
|
func TestUsageWithDescription(t *testing.T) {
|
||||||
expectedHelp := `this program does this and that
|
expectedUsage := "Usage: example"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
this program does this and that
|
||||||
Usage: example
|
Usage: example
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
@ -244,16 +278,18 @@ Options:
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
actual := help.String()
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
if expectedHelp != actual {
|
|
||||||
t.Logf("Expected:\n%s", expectedHelp)
|
var usage bytes.Buffer
|
||||||
t.Logf("Actual:\n%s", actual)
|
p.WriteUsage(&usage)
|
||||||
t.Fail()
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRequiredMultiplePositionals(t *testing.T) {
|
func TestRequiredMultiplePositionals(t *testing.T) {
|
||||||
expectedHelp := `Usage: example REQUIREDMULTIPLE [REQUIREDMULTIPLE ...]
|
expectedUsage := "Usage: example REQUIREDMULTIPLE [REQUIREDMULTIPLE ...]"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example REQUIREDMULTIPLE [REQUIREDMULTIPLE ...]
|
||||||
|
|
||||||
Positional arguments:
|
Positional arguments:
|
||||||
REQUIREDMULTIPLE required multiple positional
|
REQUIREDMULTIPLE required multiple positional
|
||||||
|
@ -270,11 +306,18 @@ Options:
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageWithNestedSubcommands(t *testing.T) {
|
func TestUsageWithNestedSubcommands(t *testing.T) {
|
||||||
expectedHelp := `Usage: example child nested [--enable] OUTPUT
|
expectedUsage := "Usage: example child nested [--enable] OUTPUT"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example child nested [--enable] OUTPUT
|
||||||
|
|
||||||
Positional arguments:
|
Positional arguments:
|
||||||
OUTPUT
|
OUTPUT
|
||||||
|
@ -307,11 +350,18 @@ Global options:
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageWithoutLongNames(t *testing.T) {
|
func TestUsageWithoutLongNames(t *testing.T) {
|
||||||
expectedHelp := `Usage: example [-a PLACEHOLDER] -b SHORTONLY2
|
expectedUsage := "Usage: example [-a PLACEHOLDER] -b SHORTONLY2"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example [-a PLACEHOLDER] -b SHORTONLY2
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-a PLACEHOLDER some help [default: some val]
|
-a PLACEHOLDER some help [default: some val]
|
||||||
|
@ -324,13 +374,21 @@ Options:
|
||||||
}
|
}
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
p, err := NewParser(Config{Program: "example"}, &args)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageWithShortFirst(t *testing.T) {
|
func TestUsageWithShortFirst(t *testing.T) {
|
||||||
expectedHelp := `Usage: example [-c CAT] [--dog DOG]
|
expectedUsage := "Usage: example [-c CAT] [--dog DOG]"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example [-c CAT] [--dog DOG]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-c CAT
|
-c CAT
|
||||||
|
@ -343,13 +401,21 @@ Options:
|
||||||
}
|
}
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
p, err := NewParser(Config{Program: "example"}, &args)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageWithEnvOptions(t *testing.T) {
|
func TestUsageWithEnvOptions(t *testing.T) {
|
||||||
expectedHelp := `Usage: example [-s SHORT]
|
expectedUsage := "Usage: example [-s SHORT]"
|
||||||
|
|
||||||
|
expectedHelp := `
|
||||||
|
Usage: example [-s SHORT]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-s SHORT [env: SHORT]
|
-s SHORT [env: SHORT]
|
||||||
|
@ -363,7 +429,42 @@ Options:
|
||||||
|
|
||||||
p, err := NewParser(Config{Program: "example"}, &args)
|
p, err := NewParser(Config{Program: "example"}, &args)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var help bytes.Buffer
|
var help bytes.Buffer
|
||||||
p.WriteHelp(&help)
|
p.WriteHelp(&help)
|
||||||
assert.Equal(t, expectedHelp, help.String())
|
assert.Equal(t, expectedHelp[1:], help.String())
|
||||||
|
|
||||||
|
var usage bytes.Buffer
|
||||||
|
p.WriteUsage(&usage)
|
||||||
|
assert.Equal(t, expectedUsage, strings.TrimSpace(usage.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFail(t *testing.T) {
|
||||||
|
originalStderr := stderr
|
||||||
|
originalExit := osExit
|
||||||
|
defer func() {
|
||||||
|
stderr = originalStderr
|
||||||
|
osExit = originalExit
|
||||||
|
}()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
stderr = &b
|
||||||
|
|
||||||
|
var exitCode int
|
||||||
|
osExit = func(code int) { exitCode = code }
|
||||||
|
|
||||||
|
expectedStdout := `
|
||||||
|
Usage: example [--foo FOO]
|
||||||
|
error: something went wrong
|
||||||
|
`
|
||||||
|
|
||||||
|
var args struct {
|
||||||
|
Foo int
|
||||||
|
}
|
||||||
|
p, err := NewParser(Config{Program: "example"}, &args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
p.Fail("something went wrong")
|
||||||
|
|
||||||
|
assert.Equal(t, expectedStdout[1:], b.String())
|
||||||
|
assert.Equal(t, -1, exitCode)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue