Merge remote-tracking branch 'origin/v1' into merging-from-v1

This commit is contained in:
Dan Buch 2016-06-17 09:11:36 -04:00
commit 6c628a1a88
No known key found for this signature in database
GPG Key ID: FAEF12936DD3E3EC
9 changed files with 624 additions and 13 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.coverprofile *.coverprofile
node_modules/

View File

@ -2,6 +2,10 @@ language: go
sudo: false sudo: false
cache:
directories:
- node_modules
go: go:
- 1.2.2 - 1.2.2
- 1.3.3 - 1.3.3
@ -29,6 +33,9 @@ matrix:
before_script: before_script:
- go get github.com/urfave/gfmxr/... - go get github.com/urfave/gfmxr/...
- $pip_install flake8 - $pip_install flake8
- if [ ! -f node_modules/.bin/markdown-toc ] ; then
npm install markdown-toc ;
fi
script: script:
- flake8 runtests cli-v1-to-v2 - flake8 runtests cli-v1-to-v2
@ -37,3 +44,4 @@ script:
- ./runtests gfmxr - ./runtests gfmxr
- ./cli-v1-to-v2 --selftest - ./cli-v1-to-v2 --selftest
- ./runtests migrations - ./runtests migrations
- ./runtests toc

View File

@ -36,6 +36,7 @@
- `./runtests` test runner with coverage tracking by default - `./runtests` test runner with coverage tracking by default
- testing on OS X - testing on OS X
- testing on Windows - testing on Windows
- `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code
### Changed ### Changed
- Use spaces for alignment in help/usage output instead of tabs, making the - Use spaces for alignment in help/usage output instead of tabs, making the

View File

@ -1,3 +1,6 @@
cli
===
[![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli) [![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli)
[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli)
@ -6,9 +9,6 @@
[![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) / [![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) /
[![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc) [![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc)
# cli
**Notice:** This is the library formerly known as **Notice:** This is the library formerly known as
`github.com/codegangsta/cli` -- Github will automatically redirect requests `github.com/codegangsta/cli` -- Github will automatically redirect requests
to this repository, but we recommend updating your references for clarity. to this repository, but we recommend updating your references for clarity.
@ -17,6 +17,37 @@ cli is a simple, fast, and fun package for building command line apps in Go. The
goal is to enable developers to write fast and distributable command line goal is to enable developers to write fast and distributable command line
applications in an expressive way. applications in an expressive way.
<!-- toc -->
- [Overview](#overview)
- [Installation](#installation)
* [Supported platforms](#supported-platforms)
* [Using the `v2` branch](#using-the-v2-branch)
* [Pinning to the `v1` branch](#pinning-to-the-v1-branch)
- [Getting Started](#getting-started)
- [Examples](#examples)
* [Arguments](#arguments)
* [Flags](#flags)
+ [Placeholder Values](#placeholder-values)
+ [Alternate Names](#alternate-names)
+ [Values from the Environment](#values-from-the-environment)
+ [Values from alternate input sources (YAML and others)](#values-from-alternate-input-sources-yaml-and-others)
* [Subcommands](#subcommands)
* [Subcommands categories](#subcommands-categories)
* [Exit code](#exit-code)
* [Bash Completion](#bash-completion)
+ [Enabling](#enabling)
+ [Distribution](#distribution)
+ [Customization](#customization)
* [Generated Help Text](#generated-help-text)
+ [Customization](#customization-1)
* [Version Flag](#version-flag)
+ [Customization](#customization-2)
+ [Full API Example](#full-api-example)
- [Contribution Guidelines](#contribution-guidelines)
<!-- tocstop -->
## Overview ## Overview
Command line apps are usually so tiny that there is absolutely no reason why Command line apps are usually so tiny that there is absolutely no reason why
@ -1044,6 +1075,19 @@ func (w *hexWriter) Write(p []byte) (int, error) {
return len(p), nil return len(p), nil
} }
type genericType struct {
s string
}
func (g *genericType) Set(value string) error {
g.s = value
return nil
}
func (g *genericType) String() string {
return g.s
}
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = "kənˈtrīv" app.Name = "kənˈtrīv"
@ -1113,7 +1157,17 @@ func main() {
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
&cli.BoolFlag{Name: "fancy"}, &cli.BoolFlag{Name: "fancy"},
&cli.BoolFlag{Value: true, Name: "fancier"}, &cli.BoolFlag{Value: true, Name: "fancier"},
&cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}}, &cli.DurationFlag{Name: "howlong, H", Value: time.Second * 3},
&cli.Float64Flag{Name: "howmuch"},
&cli.GenericFlag{Name: "wat", Value: &genericType{}},
&cli.Int64Flag{Name: "longdistance"},
&cli.Int64SliceFlag{Name: "intervals"},
&cli.IntFlag{Name: "distance"},
&cli.IntSliceFlag{Name: "times"},
&cli.StringFlag{Name: "dance-move, d"},
&cli.StringSliceFlag{Name: "names, N"},
&cli.UintFlag{Name: "age"},
&cli.Uint64Flag{Name: "bigage"},
} }
app.EnableBashCompletion = true app.EnableBashCompletion = true
app.HideHelp = false app.HideHelp = false
@ -1190,10 +1244,14 @@ func main() {
fmt.Printf("%#v\n", nc.Duration("howlong")) fmt.Printf("%#v\n", nc.Duration("howlong"))
fmt.Printf("%#v\n", nc.Float64("hay")) fmt.Printf("%#v\n", nc.Float64("hay"))
fmt.Printf("%#v\n", nc.Generic("bloop")) fmt.Printf("%#v\n", nc.Generic("bloop"))
fmt.Printf("%#v\n", nc.Int64("bonk"))
fmt.Printf("%#v\n", nc.Int64Slice("burnks"))
fmt.Printf("%#v\n", nc.Int("bips")) fmt.Printf("%#v\n", nc.Int("bips"))
fmt.Printf("%#v\n", nc.IntSlice("blups")) fmt.Printf("%#v\n", nc.IntSlice("blups"))
fmt.Printf("%#v\n", nc.String("snurt")) fmt.Printf("%#v\n", nc.String("snurt"))
fmt.Printf("%#v\n", nc.StringSlice("snurkles")) fmt.Printf("%#v\n", nc.StringSlice("snurkles"))
fmt.Printf("%#v\n", nc.Uint("flub"))
fmt.Printf("%#v\n", nc.Uint64("florb"))
fmt.Printf("%#v\n", nc.FlagNames()) fmt.Printf("%#v\n", nc.FlagNames())
fmt.Printf("%#v\n", nc.IsSet("wat")) fmt.Printf("%#v\n", nc.IsSet("wat"))

View File

@ -33,6 +33,30 @@ func (c *Context) Int(name string) int {
return 0 return 0
} }
// Int64 looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Int64(name string) int64 {
if fs := lookupFlagSet(name, c); fs != nil {
return lookupInt64(name, fs)
}
return 0
}
// Uint looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Uint(name string) uint {
if fs := lookupFlagSet(name, c); fs != nil {
return lookupUint(name, fs)
}
return 0
}
// Uint64 looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Uint64(name string) uint64 {
if fs := lookupFlagSet(name, c); fs != nil {
return lookupUint64(name, fs)
}
return 0
}
// Duration looks up the value of a local time.Duration flag, returns 0 if no // Duration looks up the value of a local time.Duration flag, returns 0 if no
// time.Duration flag exists // time.Duration flag exists
func (c *Context) Duration(name string) time.Duration { func (c *Context) Duration(name string) time.Duration {
@ -51,6 +75,15 @@ func (c *Context) Float64(name string) float64 {
return 0 return 0
} }
// Int64Slice looks up the value of a local int slice flag, returns nil if no int
// slice flag exists
func (c *Context) Int64Slice(name string) []int64 {
if fs := lookupFlagSet(name, c); fs != nil {
return lookupInt64Slice(name, fs)
}
return nil
}
// Bool looks up the value of a local bool flag, returns false if no bool flag exists // Bool looks up the value of a local bool flag, returns false if no bool flag exists
func (c *Context) Bool(name string) bool { func (c *Context) Bool(name string) bool {
if fs := lookupFlagSet(name, c); fs != nil { if fs := lookupFlagSet(name, c); fs != nil {
@ -171,7 +204,46 @@ func lookupFlagSet(name string, ctx *Context) *flag.FlagSet {
func lookupInt(name string, set *flag.FlagSet) int { func lookupInt(name string, set *flag.FlagSet) int {
f := set.Lookup(name) f := set.Lookup(name)
if f != nil { if f != nil {
val, err := strconv.Atoi(f.Value.String()) val, err := strconv.ParseInt(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return int(val)
}
return 0
}
func lookupInt64(name string, set *flag.FlagSet) int64 {
f := set.Lookup(name)
if f != nil {
val, err := strconv.ParseInt(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return val
}
return 0
}
func lookupUint(name string, set *flag.FlagSet) uint {
f := set.Lookup(name)
if f != nil {
val, err := strconv.ParseUint(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return uint(val)
}
return 0
}
func lookupUint64(name string, set *flag.FlagSet) uint64 {
f := set.Lookup(name)
if f != nil {
val, err := strconv.ParseUint(f.Value.String(), 0, 64)
if err != nil { if err != nil {
return 0 return 0
} }
@ -235,6 +307,16 @@ func lookupIntSlice(name string, set *flag.FlagSet) []int {
return nil return nil
} }
func lookupInt64Slice(name string, set *flag.FlagSet) []int64 {
f := set.Lookup(name)
if f != nil {
return (f.Value.(*Int64Slice)).Value()
}
return nil
}
func lookupGeneric(name string, set *flag.FlagSet) interface{} { func lookupGeneric(name string, set *flag.FlagSet) interface{} {
f := set.Lookup(name) f := set.Lookup(name)
if f != nil { if f != nil {

View File

@ -10,15 +10,24 @@ import (
func TestNewContext(t *testing.T) { func TestNewContext(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Int("myflag", 12, "doc") set.Int("myflag", 12, "doc")
set.Int64("myflagInt64", int64(12), "doc")
set.Uint("myflagUint", uint(93), "doc")
set.Uint64("myflagUint64", uint64(93), "doc")
set.Float64("myflag64", float64(17), "doc") set.Float64("myflag64", float64(17), "doc")
globalSet := flag.NewFlagSet("test", 0) globalSet := flag.NewFlagSet("test", 0)
globalSet.Int("myflag", 42, "doc") globalSet.Int("myflag", 42, "doc")
globalSet.Int64("myflagInt64", int64(42), "doc")
globalSet.Uint("myflagUint", uint(33), "doc")
globalSet.Uint64("myflagUint64", uint64(33), "doc")
globalSet.Float64("myflag64", float64(47), "doc") globalSet.Float64("myflag64", float64(47), "doc")
globalCtx := NewContext(nil, globalSet, nil) globalCtx := NewContext(nil, globalSet, nil)
command := &Command{Name: "mycommand"} command := &Command{Name: "mycommand"}
c := NewContext(nil, set, globalCtx) c := NewContext(nil, set, globalCtx)
c.Command = command c.Command = command
expect(t, c.Int("myflag"), 12) expect(t, c.Int("myflag"), 12)
expect(t, c.Int64("myflagInt64"), int64(12))
expect(t, c.Uint("myflagUint"), uint(93))
expect(t, c.Uint64("myflagUint64"), uint64(93))
expect(t, c.Float64("myflag64"), float64(17)) expect(t, c.Float64("myflag64"), float64(17))
expect(t, c.Command.Name, "mycommand") expect(t, c.Command.Name, "mycommand")
} }
@ -30,6 +39,27 @@ func TestContext_Int(t *testing.T) {
expect(t, c.Int("myflag"), 12) expect(t, c.Int("myflag"), 12)
} }
func TestContext_Int64(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Int64("myflagInt64", 12, "doc")
c := NewContext(nil, set, nil)
expect(t, c.Int64("myflagInt64"), int64(12))
}
func TestContext_Uint(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Uint("myflagUint", uint(13), "doc")
c := NewContext(nil, set, nil)
expect(t, c.Uint("myflagUint"), uint(13))
}
func TestContext_Uint64(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.Uint64("myflagUint64", uint64(9), "doc")
c := NewContext(nil, set, nil)
expect(t, c.Uint64("myflagUint64"), uint64(9))
}
func TestContext_Float64(t *testing.T) { func TestContext_Float64(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Float64("myflag", float64(17), "doc") set.Float64("myflag", float64(17), "doc")

229
flag.go
View File

@ -253,8 +253,8 @@ func (i *IntSlice) Set(value string) error {
} }
// String returns a readable representation of this value (for usage defaults) // String returns a readable representation of this value (for usage defaults)
func (i *IntSlice) String() string { func (f *IntSlice) String() string {
return fmt.Sprintf("%v", i.slice) return fmt.Sprintf("%#v", f.slice)
} }
// Serialized allows IntSlice to fulfill Serializeder // Serialized allows IntSlice to fulfill Serializeder
@ -317,6 +317,77 @@ func (f *IntSliceFlag) Names() []string {
return flagNames(f) return flagNames(f)
} }
// Int64Slice is an opaque type for []int to satisfy flag.Value
type Int64Slice []int64
// Set parses the value into an integer and appends it to the list of values
func (f *Int64Slice) Set(value string) error {
tmp, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
*f = append(*f, tmp)
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (f *Int64Slice) String() string {
return fmt.Sprintf("%#v", *f)
}
// Value returns the slice of ints set by this flag
func (f *Int64Slice) Value() []int64 {
return *f
}
// Int64SliceFlag is an int flag that can be specified multiple times on the
// command-line
type Int64SliceFlag struct {
Name string
Value *Int64Slice
Usage string
EnvVar string
Hidden bool
}
// String returns the usage
func (f Int64SliceFlag) String() string {
return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
newVal := &Int64Slice{}
for _, s := range strings.Split(envVal, ",") {
s = strings.TrimSpace(s)
err := newVal.Set(s)
if err != nil {
fmt.Fprintf(ErrWriter, err.Error())
}
}
f.Value = newVal
break
}
}
}
eachName(f.Name, func(name string) {
if f.Value == nil {
f.Value = &Int64Slice{}
}
set.Var(f.Value, name, f.Usage)
})
}
// GetName returns the name of the flag.
func (f Int64SliceFlag) GetName() string {
return f.Name
}
// BoolFlag is a switch that defaults to false // BoolFlag is a switch that defaults to false
type BoolFlag struct { type BoolFlag struct {
Name string Name string
@ -403,7 +474,6 @@ func (f *StringFlag) Names() []string {
} }
// IntFlag is a flag that takes an integer // IntFlag is a flag that takes an integer
// Errors if the value provided cannot be parsed
type IntFlag struct { type IntFlag struct {
Name string Name string
Aliases []string Aliases []string
@ -447,6 +517,138 @@ func (f *IntFlag) Names() []string {
return flagNames(f) return flagNames(f)
} }
// Int64Flag is a flag that takes a 64-bit integer
type Int64Flag struct {
Name string
Value int64
Usage string
EnvVar string
Destination *int64
Hidden bool
}
// String returns the usage
func (f Int64Flag) String() string {
return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
func (f Int64Flag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
envValInt, err := strconv.ParseInt(envVal, 0, 64)
if err == nil {
f.Value = envValInt
break
}
}
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.Int64Var(f.Destination, name, f.Value, f.Usage)
return
}
set.Int64(name, f.Value, f.Usage)
})
}
// GetName returns the name of the flag.
func (f Int64Flag) GetName() string {
return f.Name
}
// UintFlag is a flag that takes an unsigned integer
type UintFlag struct {
Name string
Value uint
Usage string
EnvVar string
Destination *uint
Hidden bool
}
// String returns the usage
func (f UintFlag) String() string {
return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
func (f UintFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
envValInt, err := strconv.ParseUint(envVal, 0, 64)
if err == nil {
f.Value = uint(envValInt)
break
}
}
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.UintVar(f.Destination, name, f.Value, f.Usage)
return
}
set.Uint(name, f.Value, f.Usage)
})
}
// GetName returns the name of the flag.
func (f UintFlag) GetName() string {
return f.Name
}
// Uint64Flag is a flag that takes an unsigned 64-bit integer
type Uint64Flag struct {
Name string
Value uint64
Usage string
EnvVar string
Destination *uint64
Hidden bool
}
// String returns the usage
func (f Uint64Flag) String() string {
return FlagStringer(f)
}
// Apply populates the flag given the flag set and environment
func (f Uint64Flag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
envValInt, err := strconv.ParseUint(envVal, 0, 64)
if err == nil {
f.Value = uint64(envValInt)
break
}
}
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.Uint64Var(f.Destination, name, f.Value, f.Usage)
return
}
set.Uint64(name, f.Value, f.Usage)
})
}
// GetName returns the name of the flag.
func (f Uint64Flag) GetName() string {
return f.Name
}
// DurationFlag is a flag that takes a duration specified in Go's duration // DurationFlag is a flag that takes a duration specified in Go's duration
// format: https://golang.org/pkg/time/#ParseDuration // format: https://golang.org/pkg/time/#ParseDuration
type DurationFlag struct { type DurationFlag struct {
@ -493,7 +695,6 @@ func (f *DurationFlag) Names() []string {
} }
// Float64Flag is a flag that takes an float value // Float64Flag is a flag that takes an float value
// Errors if the value provided cannot be parsed
type Float64Flag struct { type Float64Flag struct {
Name string Name string
Aliases []string Aliases []string
@ -659,9 +860,14 @@ func stringifyFlag(f Flag) string {
switch f.(type) { switch f.(type) {
case *IntSliceFlag: case *IntSliceFlag:
return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyIntSliceFlag(f.(*IntSliceFlag))) return withEnvHint(fv.FieldByName("EnvVar").String(),
stringifyIntSliceFlag(f.(*IntSliceFlag)))
case *Int64SliceFlag:
return withEnvHint(fv.FieldByName("EnvVar").String(),
stringifyInt64SliceFlag(f.(*Int64SliceFlag)))
case *StringSliceFlag: case *StringSliceFlag:
return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyStringSliceFlag(f.(*StringSliceFlag))) return withEnvHint(fv.FieldByName("EnvVar").String(),
stringifyStringSliceFlag(f.(*StringSliceFlag)))
} }
placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String())
@ -704,6 +910,17 @@ func stringifyIntSliceFlag(f *IntSliceFlag) string {
return stringifySliceFlag(f.Usage, append([]string{f.Name}, f.Aliases...), defaultVals) return stringifySliceFlag(f.Usage, append([]string{f.Name}, f.Aliases...), defaultVals)
} }
func stringifyInt64SliceFlag(f Int64SliceFlag) string {
defaultVals := []string{}
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, fmt.Sprintf("%d", i))
}
}
return stringifySliceFlag(f.Usage, f.Name, defaultVals)
}
func stringifyStringSliceFlag(f *StringSliceFlag) string { func stringifyStringSliceFlag(f *StringSliceFlag) string {
defaultVals := []string{} defaultVals := []string{}
if f.Value != nil && len(f.Value.Value()) > 0 { if f.Value != nil && len(f.Value.Value()) > 0 {

View File

@ -192,6 +192,114 @@ func TestIntFlagApply_SetsAllNames(t *testing.T) {
expect(t, v, 5) expect(t, v, 5)
} }
var int64FlagTests = []struct {
name string
expected string
}{
{"hats", "--hats value\t(default: 8589934592)"},
{"H", "-H value\t(default: 8589934592)"},
}
func TestInt64FlagHelpOutput(t *testing.T) {
for _, test := range int64FlagTests {
flag := Int64Flag{Name: test.name, Value: 8589934592}
output := flag.String()
if output != test.expected {
t.Errorf("%s does not match %s", output, test.expected)
}
}
}
func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_BAR", "2")
for _, test := range int64FlagTests {
flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"}
output := flag.String()
expectedSuffix := " [$APP_BAR]"
if runtime.GOOS == "windows" {
expectedSuffix = " [%APP_BAR%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
var uintFlagTests = []struct {
name string
expected string
}{
{"nerfs", "--nerfs value\t(default: 41)"},
{"N", "-N value\t(default: 41)"},
}
func TestUintFlagHelpOutput(t *testing.T) {
for _, test := range uintFlagTests {
flag := UintFlag{Name: test.name, Value: 41}
output := flag.String()
if output != test.expected {
t.Errorf("%s does not match %s", output, test.expected)
}
}
}
func TestUintFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_BAR", "2")
for _, test := range uintFlagTests {
flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"}
output := flag.String()
expectedSuffix := " [$APP_BAR]"
if runtime.GOOS == "windows" {
expectedSuffix = " [%APP_BAR%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
var uint64FlagTests = []struct {
name string
expected string
}{
{"gerfs", "--gerfs value\t(default: 8589934582)"},
{"G", "-G value\t(default: 8589934582)"},
}
func TestUint64FlagHelpOutput(t *testing.T) {
for _, test := range uint64FlagTests {
flag := Uint64Flag{Name: test.name, Value: 8589934582}
output := flag.String()
if output != test.expected {
t.Errorf("%s does not match %s", output, test.expected)
}
}
}
func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_BAR", "2")
for _, test := range uint64FlagTests {
flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"}
output := flag.String()
expectedSuffix := " [$APP_BAR]"
if runtime.GOOS == "windows" {
expectedSuffix = " [%APP_BAR%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
var durationFlagTests = []struct { var durationFlagTests = []struct {
name string name string
expected string expected string
@ -287,6 +395,49 @@ func TestIntSliceFlagApply_SetsAllNames(t *testing.T) {
expect(t, err, nil) expect(t, err, nil)
} }
var int64SliceFlagTests = []struct {
name string
value *Int64Slice
expected string
}{
{"heads", &Int64Slice{}, "--heads value\t"},
{"H", &Int64Slice{}, "-H value\t"},
{"H, heads", func() *Int64Slice {
i := &Int64Slice{}
i.Set("2")
i.Set("17179869184")
return i
}(), "-H value, --heads value\t(default: 2, 17179869184)"},
}
func TestInt64SliceFlagHelpOutput(t *testing.T) {
for _, test := range int64SliceFlagTests {
flag := Int64SliceFlag{Name: test.name, Value: test.value}
output := flag.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
os.Setenv("APP_SMURF", "42,17179869184")
for _, test := range int64SliceFlagTests {
flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"}
output := flag.String()
expectedSuffix := " [$APP_SMURF]"
if runtime.GOOS == "windows" {
expectedSuffix = " [%APP_SMURF%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%q does not end with"+expectedSuffix, output)
}
}
}
var float64FlagTests = []struct { var float64FlagTests = []struct {
name string name string
expected string expected string
@ -775,6 +926,63 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
}).Run([]string{"run"}) }).Run([]string{"run"})
} }
func TestParseMultiInt64Slice(t *testing.T) {
(&App{
Flags: []Flag{
Int64SliceFlag{Name: "serve, s", Value: &Int64Slice{}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Int64Slice("serve"), []int64{10, 17179869184}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.Int64Slice("s"), []int64{10, 17179869184}) {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run", "-s", "10", "-s", "17179869184"})
}
func TestParseMultiInt64SliceFromEnv(t *testing.T) {
os.Clearenv()
os.Setenv("APP_INTERVALS", "20,30,17179869184")
(&App{
Flags: []Flag{
Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "APP_INTERVALS"},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) {
os.Clearenv()
os.Setenv("APP_INTERVALS", "20,30,17179869184")
(&App{
Flags: []Flag{
Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiFloat64(t *testing.T) { func TestParseMultiFloat64(t *testing.T) {
a := App{ a := App{
Flags: []Flag{ Flags: []Flag{

View File

@ -92,6 +92,12 @@ def _migrations():
shutil.rmtree(tmpdir, ignore_errors=True) shutil.rmtree(tmpdir, ignore_errors=True)
@_target
def _toc():
_run(['node_modules/.bin/markdown-toc', '-i', 'README.md'])
_run(['git', 'diff', '--quiet'])
def _run(command): def _run(command):
print('runtests: {}'.format(' '.join(command)), file=sys.stderr) print('runtests: {}'.format(' '.join(command)), file=sys.stderr)
check_call(command) check_call(command)