urfave-cli/flag_test.go
Mostyn Bramley-Moore 8d907b5329 Save the temp dir before clearing environment variables
In some windows setups, os.TempDir() will return something like
"C:\WINDOWS" if none of the expected environment variables (TMP, TEMP,
USERPROFILE) are set, and then functions that try to create temporary
files or directories will fail since this is not writable.

Several tests in flag_test.go clear the environment and then set a small
number of specific environment variables for the test, triggering the
following error in TestFlagFromFile when I run `go test ./...` on a new
windows machine:

flag_test.go:1667: open C:\WINDOWS\urfave_cli_test851254863: Access is denied.

To work around this, we can check the temp directory before calling
os.Clearenv() and use that directory explcitly in functions that
take the temp directory.

This fixes part of #1105.
2020-04-14 07:55:17 +02:00

1802 lines
51 KiB
Go

package cli
import (
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"regexp"
"runtime"
"strings"
"testing"
"time"
)
var osTempDir = os.TempDir()
var boolFlagTests = []struct {
name string
expected string
}{
{"help", "--help\t(default: false)"},
{"h", "-h\t(default: false)"},
}
func TestBoolFlagHelpOutput(t *testing.T) {
for _, test := range boolFlagTests {
fl := &BoolFlag{Name: test.name}
output := fl.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestBoolFlagApply_SetsAllNames(t *testing.T) {
v := false
fl := BoolFlag{Name: "wat", Aliases: []string{"W", "huh"}, Destination: &v}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--wat", "-W", "--huh"})
expect(t, err, nil)
expect(t, v, true)
}
func TestFlagsFromEnv(t *testing.T) {
newSetIntSlice := func(defaults ...int) IntSlice {
s := NewIntSlice(defaults...)
s.hasBeenSet = true
return *s
}
newSetInt64Slice := func(defaults ...int64) Int64Slice {
s := NewInt64Slice(defaults...)
s.hasBeenSet = true
return *s
}
newSetStringSlice := func(defaults ...string) StringSlice {
s := NewStringSlice(defaults...)
s.hasBeenSet = false
return *s
}
var flagTests = []struct {
input string
output interface{}
flag Flag
errRegexp string
}{
{"", false, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
{"1", true, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
{"false", false, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
{"foobar", true, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, `could not parse "foobar" as bool value for flag debug: .*`},
{"1s", 1 * time.Second, &DurationFlag{Name: "time", EnvVars: []string{"TIME"}}, ""},
{"foobar", false, &DurationFlag{Name: "time", EnvVars: []string{"TIME"}}, `could not parse "foobar" as duration value for flag time: .*`},
{"1.2", 1.2, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
{"1", 1.0, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
{"foobar", 0, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as float64 value for flag seconds: .*`},
{"1", int64(1), &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
{"1.2", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value for flag seconds: .*`},
{"foobar", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value for flag seconds: .*`},
{"1", 1, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
{"1.2", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value for flag seconds: .*`},
{"foobar", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value for flag seconds: .*`},
{"1,2", newSetIntSlice(1, 2), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
{"1.2,2", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int slice value for flag seconds: .*`},
{"foobar", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int slice value for flag seconds: .*`},
{"1,2", newSetInt64Slice(1, 2), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
{"1.2,2", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int64 slice value for flag seconds: .*`},
{"foobar", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int64 slice value for flag seconds: .*`},
{"foo", "foo", &StringFlag{Name: "name", EnvVars: []string{"NAME"}}, ""},
{"path", "path", &PathFlag{Name: "path", EnvVars: []string{"PATH"}}, ""},
{"foo,bar", newSetStringSlice("foo", "bar"), &StringSliceFlag{Name: "names", EnvVars: []string{"NAMES"}}, ""},
{"1", uint(1), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
{"1.2", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint value for flag seconds: .*`},
{"foobar", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint value for flag seconds: .*`},
{"1", uint64(1), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
{"1.2", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint64 value for flag seconds: .*`},
{"foobar", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint64 value for flag seconds: .*`},
{"foo,bar", &Parser{"foo", "bar"}, &GenericFlag{Name: "names", Value: &Parser{}, EnvVars: []string{"NAMES"}}, ""},
}
for i, test := range flagTests {
os.Clearenv()
envVarSlice := reflect.Indirect(reflect.ValueOf(test.flag)).FieldByName("EnvVars").Slice(0, 1)
_ = os.Setenv(envVarSlice.Index(0).String(), test.input)
a := App{
Flags: []Flag{test.flag},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Value(test.flag.Names()[0]), test.output) {
t.Errorf("ex:%01d expected %q to be parsed as %#v, instead was %#v", i, test.input, test.output, ctx.Value(test.flag.Names()[0]))
}
return nil
},
}
err := a.Run([]string{"run"})
if test.errRegexp != "" {
if err == nil {
t.Errorf("expected error to match %q, got none", test.errRegexp)
} else {
if matched, _ := regexp.MatchString(test.errRegexp, err.Error()); !matched {
t.Errorf("expected error to match %q, got error %s", test.errRegexp, err)
}
}
} else {
if err != nil && test.errRegexp == "" {
t.Errorf("expected no error got %q", err)
}
}
}
}
var stringFlagTests = []struct {
name string
aliases []string
usage string
value string
expected string
}{
{"foo", nil, "", "", "--foo value\t"},
{"f", nil, "", "", "-f value\t"},
{"f", nil, "The total `foo` desired", "all", "-f foo\tThe total foo desired (default: \"all\")"},
{"test", nil, "", "Something", "--test value\t(default: \"Something\")"},
{"config", []string{"c"}, "Load configuration from `FILE`", "", "--config FILE, -c FILE\tLoad configuration from FILE"},
{"config", []string{"c"}, "Load configuration from `CONFIG`", "config.json", "--config CONFIG, -c CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"},
}
func TestStringFlagHelpOutput(t *testing.T) {
for _, test := range stringFlagTests {
fl := &StringFlag{Name: test.name, Aliases: test.aliases, Usage: test.usage, Value: test.value}
output := fl.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestStringFlagDefaultText(t *testing.T) {
fl := &StringFlag{Name: "foo", Aliases: nil, Usage: "amount of `foo` requested", Value: "none", DefaultText: "all of it"}
expected := "--foo foo\tamount of foo requested (default: all of it)"
output := fl.String()
if output != expected {
t.Errorf("%q does not match %q", output, expected)
}
}
func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_FOO", "derp")
for _, test := range stringFlagTests {
fl := &StringFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_FOO"}}
output := fl.String()
expectedSuffix := " [$APP_FOO]"
if runtime.GOOS == "windows" {
expectedSuffix = " [%APP_FOO%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
var prefixStringFlagTests = []struct {
name string
aliases []string
usage string
value string
prefixer FlagNamePrefixFunc
expected string
}{
{name: "foo", usage: "", value: "", prefixer: func(a []string, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, expected: "name: foo, ph: value\t"},
{name: "f", usage: "", value: "", prefixer: func(a []string, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, expected: "name: f, ph: value\t"},
{name: "f", usage: "The total `foo` desired", value: "all", prefixer: func(a []string, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, expected: "name: f, ph: foo\tThe total foo desired (default: \"all\")"},
{name: "test", usage: "", value: "Something", prefixer: func(a []string, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, expected: "name: test, ph: value\t(default: \"Something\")"},
{name: "config", aliases: []string{"c"}, usage: "Load configuration from `FILE`", value: "", prefixer: func(a []string, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, expected: "name: config,c, ph: FILE\tLoad configuration from FILE"},
{name: "config", aliases: []string{"c"}, usage: "Load configuration from `CONFIG`", value: "config.json", prefixer: func(a []string, b string) string {
return fmt.Sprintf("name: %s, ph: %s", a, b)
}, expected: "name: config,c, ph: CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"},
}
func TestStringFlagApply_SetsAllNames(t *testing.T) {
v := "mmm"
fl := StringFlag{Name: "hay", Aliases: []string{"H", "hayyy"}, Destination: &v}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--hay", "u", "-H", "yuu", "--hayyy", "YUUUU"})
expect(t, err, nil)
expect(t, v, "YUUUU")
}
var pathFlagTests = []struct {
name string
aliases []string
usage string
value string
expected string
}{
{"f", nil, "", "", "-f value\t"},
{"f", nil, "Path is the `path` of file", "/path/to/file", "-f path\tPath is the path of file (default: \"/path/to/file\")"},
}
func TestPathFlagHelpOutput(t *testing.T) {
for _, test := range pathFlagTests {
fl := &PathFlag{Name: test.name, Aliases: test.aliases, Usage: test.usage, Value: test.value}
output := fl.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestPathFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_PATH", "/path/to/file")
for _, test := range pathFlagTests {
fl := &PathFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_PATH"}}
output := fl.String()
expectedSuffix := " [$APP_PATH]"
if runtime.GOOS == "windows" {
expectedSuffix = " [%APP_PATH%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
func TestPathFlagApply_SetsAllNames(t *testing.T) {
v := "mmm"
fl := PathFlag{Name: "path", Aliases: []string{"p", "PATH"}, Destination: &v}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--path", "/path/to/file/path", "-p", "/path/to/file/p", "--PATH", "/path/to/file/PATH"})
expect(t, err, nil)
expect(t, v, "/path/to/file/PATH")
}
var envHintFlagTests = []struct {
name string
env string
hinter FlagEnvHintFunc
expected string
}{
{"foo", "", func(a []string, b string) string {
return fmt.Sprintf("env: %s, str: %s", a, b)
}, "env: , str: --foo value\t"},
{"f", "", func(a []string, b string) string {
return fmt.Sprintf("env: %s, str: %s", a, b)
}, "env: , str: -f value\t"},
{"foo", "ENV_VAR", func(a []string, b string) string {
return fmt.Sprintf("env: %s, str: %s", a, b)
}, "env: ENV_VAR, str: --foo value\t"},
{"f", "ENV_VAR", func(a []string, b string) string {
return fmt.Sprintf("env: %s, str: %s", a, b)
}, "env: ENV_VAR, str: -f value\t"},
}
//func TestFlagEnvHinter(t *testing.T) {
// defer func() {
// FlagEnvHinter = withEnvHint
// }()
//
// for _, test := range envHintFlagTests {
// FlagEnvHinter = test.hinter
// fl := StringFlag{Name: test.name, EnvVars: []string{test.env}}
// output := fl.String()
// if output != test.expected {
// t.Errorf("%q does not match %q", output, test.expected)
// }
// }
//}
var stringSliceFlagTests = []struct {
name string
aliases []string
value *StringSlice
expected string
}{
{"foo", nil, NewStringSlice(""), "--foo value\t"},
{"f", nil, NewStringSlice(""), "-f value\t"},
{"f", nil, NewStringSlice("Lipstick"), "-f value\t(default: \"Lipstick\")"},
{"test", nil, NewStringSlice("Something"), "--test value\t(default: \"Something\")"},
{"dee", []string{"d"}, NewStringSlice("Inka", "Dinka", "dooo"), "--dee value, -d value\t(default: \"Inka\", \"Dinka\", \"dooo\")"},
}
func TestStringSliceFlagHelpOutput(t *testing.T) {
for _, test := range stringSliceFlagTests {
f := &StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
output := f.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_QWWX", "11,4")
for _, test := range stringSliceFlagTests {
fl := &StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_QWWX"}}
output := fl.String()
expectedSuffix := " [$APP_QWWX]"
if runtime.GOOS == "windows" {
expectedSuffix = " [%APP_QWWX%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%q does not end with"+expectedSuffix, output)
}
}
}
func TestStringSliceFlagApply_SetsAllNames(t *testing.T) {
fl := StringSliceFlag{Name: "goat", Aliases: []string{"G", "gooots"}}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--goat", "aaa", "-G", "bbb", "--gooots", "eeeee"})
expect(t, err, nil)
}
var intFlagTests = []struct {
name string
expected string
}{
{"hats", "--hats value\t(default: 9)"},
{"H", "-H value\t(default: 9)"},
}
func TestIntFlagHelpOutput(t *testing.T) {
for _, test := range intFlagTests {
fl := &IntFlag{Name: test.name, Value: 9}
output := fl.String()
if output != test.expected {
t.Errorf("%s does not match %s", output, test.expected)
}
}
}
func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_BAR", "2")
for _, test := range intFlagTests {
fl := &IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
output := fl.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)
}
}
}
func TestIntFlagApply_SetsAllNames(t *testing.T) {
v := 3
fl := IntFlag{Name: "banana", Aliases: []string{"B", "banannanana"}, Destination: &v}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--banana", "1", "-B", "2", "--banannanana", "5"})
expect(t, err, nil)
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 {
fl := Int64Flag{Name: test.name, Value: 8589934592}
output := fl.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 {
fl := IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
output := fl.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 {
fl := UintFlag{Name: test.name, Value: 41}
output := fl.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 {
fl := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
output := fl.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 {
fl := Uint64Flag{Name: test.name, Value: 8589934582}
output := fl.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 {
fl := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
output := fl.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 {
name string
expected string
}{
{"hooting", "--hooting value\t(default: 1s)"},
{"H", "-H value\t(default: 1s)"},
}
func TestDurationFlagHelpOutput(t *testing.T) {
for _, test := range durationFlagTests {
fl := &DurationFlag{Name: test.name, Value: 1 * time.Second}
output := fl.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_BAR", "2h3m6s")
for _, test := range durationFlagTests {
fl := &DurationFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
output := fl.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)
}
}
}
func TestDurationFlagApply_SetsAllNames(t *testing.T) {
v := time.Second * 20
fl := DurationFlag{Name: "howmuch", Aliases: []string{"H", "whyyy"}, Destination: &v}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--howmuch", "30s", "-H", "5m", "--whyyy", "30h"})
expect(t, err, nil)
expect(t, v, time.Hour*30)
}
var intSliceFlagTests = []struct {
name string
aliases []string
value *IntSlice
expected string
}{
{"heads", nil, NewIntSlice(), "--heads value\t"},
{"H", nil, NewIntSlice(), "-H value\t"},
{"H", []string{"heads"}, NewIntSlice(9, 3), "-H value, --heads value\t(default: 9, 3)"},
}
func TestIntSliceFlagHelpOutput(t *testing.T) {
for _, test := range intSliceFlagTests {
fl := &IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
output := fl.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_SMURF", "42,3")
for _, test := range intSliceFlagTests {
fl := &IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_SMURF"}}
output := fl.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)
}
}
}
func TestIntSliceFlagApply_SetsAllNames(t *testing.T) {
fl := IntSliceFlag{Name: "bits", Aliases: []string{"B", "bips"}}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--bits", "23", "-B", "3", "--bips", "99"})
expect(t, err, nil)
}
var int64SliceFlagTests = []struct {
name string
aliases []string
value *Int64Slice
expected string
}{
{"heads", nil, NewInt64Slice(), "--heads value\t"},
{"H", nil, NewInt64Slice(), "-H value\t"},
{"heads", []string{"H"}, NewInt64Slice(int64(2), int64(17179869184)),
"--heads value, -H value\t(default: 2, 17179869184)"},
}
func TestInt64SliceFlagHelpOutput(t *testing.T) {
for _, test := range int64SliceFlagTests {
fl := Int64SliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
output := fl.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 {
fl := Int64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
output := fl.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 {
name string
expected string
}{
{"hooting", "--hooting value\t(default: 0.1)"},
{"H", "-H value\t(default: 0.1)"},
}
func TestFloat64FlagHelpOutput(t *testing.T) {
for _, test := range float64FlagTests {
f := &Float64Flag{Name: test.name, Value: 0.1}
output := f.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_BAZ", "99.4")
for _, test := range float64FlagTests {
fl := &Float64Flag{Name: test.name, EnvVars: []string{"APP_BAZ"}}
output := fl.String()
expectedSuffix := " [$APP_BAZ]"
if runtime.GOOS == "windows" {
expectedSuffix = " [%APP_BAZ%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
func TestFloat64FlagApply_SetsAllNames(t *testing.T) {
v := 99.1
fl := Float64Flag{Name: "noodles", Aliases: []string{"N", "nurbles"}, Destination: &v}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--noodles", "1.3", "-N", "11", "--nurbles", "43.33333"})
expect(t, err, nil)
expect(t, v, float64(43.33333))
}
var float64SliceFlagTests = []struct {
name string
aliases []string
value *Float64Slice
expected string
}{
{"heads", nil, NewFloat64Slice(), "--heads value\t"},
{"H", nil, NewFloat64Slice(), "-H value\t"},
{"heads", []string{"H"}, NewFloat64Slice(0.1234, -10.5),
"--heads value, -H value\t(default: 0.1234, -10.5)"},
}
func TestFloat64SliceFlagHelpOutput(t *testing.T) {
for _, test := range float64SliceFlagTests {
fl := Float64SliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
output := fl.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestFloat64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_SMURF", "0.1234,-10.5")
for _, test := range float64SliceFlagTests {
fl := Float64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
output := fl.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 genericFlagTests = []struct {
name string
value Generic
expected string
}{
{"toads", &Parser{"abc", "def"}, "--toads value\ttest flag (default: abc,def)"},
{"t", &Parser{"abc", "def"}, "-t value\ttest flag (default: abc,def)"},
}
func TestGenericFlagHelpOutput(t *testing.T) {
for _, test := range genericFlagTests {
fl := &GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"}
output := fl.String()
if output != test.expected {
t.Errorf("%q does not match %q", output, test.expected)
}
}
}
func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_ZAP", "3")
for _, test := range genericFlagTests {
fl := &GenericFlag{Name: test.name, EnvVars: []string{"APP_ZAP"}}
output := fl.String()
expectedSuffix := " [$APP_ZAP]"
if runtime.GOOS == "windows" {
expectedSuffix = " [%APP_ZAP%]"
}
if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with"+expectedSuffix, output)
}
}
}
func TestGenericFlagApply_SetsAllNames(t *testing.T) {
fl := GenericFlag{Name: "orbs", Aliases: []string{"O", "obrs"}, Value: &Parser{}}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--orbs", "eleventy,3", "-O", "4,bloop", "--obrs", "19,s"})
expect(t, err, nil)
}
func TestParseMultiString(t *testing.T) {
_ = (&App{
Flags: []Flag{
&StringFlag{Name: "serve", Aliases: []string{"s"}},
},
Action: func(ctx *Context) error {
if ctx.String("serve") != "10" {
t.Errorf("main name not set")
}
if ctx.String("s") != "10" {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run", "-s", "10"})
}
func TestParseDestinationString(t *testing.T) {
var dest string
_ = (&App{
Flags: []Flag{
&StringFlag{
Name: "dest",
Destination: &dest,
},
},
Action: func(ctx *Context) error {
if dest != "10" {
t.Errorf("expected destination String 10")
}
return nil
},
}).Run([]string{"run", "--dest", "10"})
}
func TestParseMultiStringFromEnv(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_COUNT", "20")
_ = (&App{
Flags: []Flag{
&StringFlag{Name: "count", Aliases: []string{"c"}, EnvVars: []string{"APP_COUNT"}},
},
Action: func(ctx *Context) error {
if ctx.String("count") != "20" {
t.Errorf("main name not set")
}
if ctx.String("c") != "20" {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiStringFromEnvCascade(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_COUNT", "20")
_ = (&App{
Flags: []Flag{
&StringFlag{Name: "count", Aliases: []string{"c"}, EnvVars: []string{"COMPAT_COUNT", "APP_COUNT"}},
},
Action: func(ctx *Context) error {
if ctx.String("count") != "20" {
t.Errorf("main name not set")
}
if ctx.String("c") != "20" {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiStringSlice(t *testing.T) {
_ = (&App{
Flags: []Flag{
&StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice()},
},
Action: func(ctx *Context) error {
expected := []string{"10", "20"}
if !reflect.DeepEqual(ctx.StringSlice("serve"), expected) {
t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
}
if !reflect.DeepEqual(ctx.StringSlice("s"), expected) {
t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
}
return nil
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
func TestParseMultiStringSliceWithDefaults(t *testing.T) {
_ = (&App{
Flags: []Flag{
&StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice("9", "2")},
},
Action: func(ctx *Context) error {
expected := []string{"10", "20"}
if !reflect.DeepEqual(ctx.StringSlice("serve"), expected) {
t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
}
if !reflect.DeepEqual(ctx.StringSlice("s"), expected) {
t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
}
return nil
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
func TestParseMultiStringSliceWithDestination(t *testing.T) {
dest := &StringSlice{}
_ = (&App{
Flags: []Flag{
&StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Destination: dest},
},
Action: func(ctx *Context) error {
expected := []string{"10", "20"}
if !reflect.DeepEqual(dest.slice, expected) {
t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
}
if !reflect.DeepEqual(dest.slice, expected) {
t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
}
return nil
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
func TestParseMultiStringSliceWithDestinationAndEnv(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "20,30,40")
dest := &StringSlice{}
_ = (&App{
Flags: []Flag{
&StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Destination: dest, EnvVars: []string{"APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
expected := []string{"10", "20"}
if !reflect.DeepEqual(dest.slice, expected) {
t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
}
if !reflect.DeepEqual(dest.slice, expected) {
t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
}
return nil
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
func TestParseMultiStringSliceWithDefaultsUnset(t *testing.T) {
_ = (&App{
Flags: []Flag{
&StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice("9", "2")},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"9", "2"}) {
t.Errorf("main name not set: %v", ctx.StringSlice("serve"))
}
if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"9", "2"}) {
t.Errorf("short name not set: %v", ctx.StringSlice("s"))
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiStringSliceFromEnv(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "20,30,40")
_ = (&App{
Flags: []Flag{
&StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice(), EnvVars: []string{"APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiStringSliceFromEnvWithDefaults(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "20,30,40")
_ = (&App{
Flags: []Flag{
&StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice("1", "2", "5"), EnvVars: []string{"APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "20,30,40")
_ = (&App{
Flags: []Flag{
&StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice(), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "20,30,40")
_ = (&App{
Flags: []Flag{
&StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice("1", "2", "5"), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiStringSliceFromEnvWithDestination(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "20,30,40")
dest := &StringSlice{}
_ = (&App{
Flags: []Flag{
&StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Destination: dest, EnvVars: []string{"APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(dest.slice, []string{"20", "30", "40"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(dest.slice, []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiInt(t *testing.T) {
_ = (&App{
Flags: []Flag{
&IntFlag{Name: "serve", Aliases: []string{"s"}},
},
Action: func(ctx *Context) error {
if ctx.Int("serve") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("s") != 10 {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run", "-s", "10"})
}
func TestParseDestinationInt(t *testing.T) {
var dest int
_ = (&App{
Flags: []Flag{
&IntFlag{
Name: "dest",
Destination: &dest,
},
},
Action: func(ctx *Context) error {
if dest != 10 {
t.Errorf("expected destination Int 10")
}
return nil
},
}).Run([]string{"run", "--dest", "10"})
}
func TestParseMultiIntFromEnv(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_TIMEOUT_SECONDS", "10")
_ = (&App{
Flags: []Flag{
&IntFlag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"APP_TIMEOUT_SECONDS"}},
},
Action: func(ctx *Context) error {
if ctx.Int("timeout") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("t") != 10 {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiIntFromEnvCascade(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_TIMEOUT_SECONDS", "10")
_ = (&App{
Flags: []Flag{
&IntFlag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"COMPAT_TIMEOUT_SECONDS", "APP_TIMEOUT_SECONDS"}},
},
Action: func(ctx *Context) error {
if ctx.Int("timeout") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("t") != 10 {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiIntSlice(t *testing.T) {
_ = (&App{
Flags: []Flag{
&IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewIntSlice()},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
func TestParseMultiIntSliceWithDefaults(t *testing.T) {
_ = (&App{
Flags: []Flag{
&IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewIntSlice(9, 2)},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
func TestParseMultiIntSliceWithDefaultsUnset(t *testing.T) {
_ = (&App{
Flags: []Flag{
&IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewIntSlice(9, 2)},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{9, 2}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.IntSlice("s"), []int{9, 2}) {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiIntSliceFromEnv(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "20,30,40")
_ = (&App{
Flags: []Flag{
&IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(), EnvVars: []string{"APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiIntSliceFromEnvWithDefaults(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "20,30,40")
_ = (&App{
Flags: []Flag{
&IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(1, 2, 5), EnvVars: []string{"APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "20,30,40")
_ = (&App{
Flags: []Flag{
&IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiInt64Slice(t *testing.T) {
_ = (&App{
Flags: []Flag{
&Int64SliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewInt64Slice()},
},
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", Aliases: []string{"i"}, Value: NewInt64Slice(), EnvVars: []string{"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", Aliases: []string{"i"}, Value: NewInt64Slice(), EnvVars: []string{"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) {
_ = (&App{
Flags: []Flag{
&Float64Flag{Name: "serve", Aliases: []string{"s"}},
},
Action: func(ctx *Context) error {
if ctx.Float64("serve") != 10.2 {
t.Errorf("main name not set")
}
if ctx.Float64("s") != 10.2 {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run", "-s", "10.2"})
}
func TestParseDestinationFloat64(t *testing.T) {
var dest float64
_ = (&App{
Flags: []Flag{
&Float64Flag{
Name: "dest",
Destination: &dest,
},
},
Action: func(ctx *Context) error {
if dest != 10.2 {
t.Errorf("expected destination Float64 10.2")
}
return nil
},
}).Run([]string{"run", "--dest", "10.2"})
}
func TestParseMultiFloat64FromEnv(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
_ = (&App{
Flags: []Flag{
&Float64Flag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"APP_TIMEOUT_SECONDS"}},
},
Action: func(ctx *Context) error {
if ctx.Float64("timeout") != 15.5 {
t.Errorf("main name not set")
}
if ctx.Float64("t") != 15.5 {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
_ = (&App{
Flags: []Flag{
&Float64Flag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"COMPAT_TIMEOUT_SECONDS", "APP_TIMEOUT_SECONDS"}},
},
Action: func(ctx *Context) error {
if ctx.Float64("timeout") != 15.5 {
t.Errorf("main name not set")
}
if ctx.Float64("t") != 15.5 {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiFloat64SliceFromEnv(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "0.1,-10.5")
_ = (&App{
Flags: []Flag{
&Float64SliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewFloat64Slice(), EnvVars: []string{"APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Float64Slice("intervals"), []float64{0.1, -10.5}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.Float64Slice("i"), []float64{0.1, -10.5}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiFloat64SliceFromEnvCascade(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_INTERVALS", "0.1234,-10.5")
_ = (&App{
Flags: []Flag{
&Float64SliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewFloat64Slice(), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Float64Slice("intervals"), []float64{0.1234, -10.5}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.Float64Slice("i"), []float64{0.1234, -10.5}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiBool(t *testing.T) {
_ = (&App{
Flags: []Flag{
&BoolFlag{Name: "serve", Aliases: []string{"s"}},
},
Action: func(ctx *Context) error {
if ctx.Bool("serve") != true {
t.Errorf("main name not set")
}
if ctx.Bool("s") != true {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run", "--serve"})
}
func TestParseBoolShortOptionHandle(t *testing.T) {
_ = (&App{
Commands: []*Command{
{
Name: "foobar",
UseShortOptionHandling: true,
Action: func(ctx *Context) error {
if ctx.Bool("serve") != true {
t.Errorf("main name not set")
}
if ctx.Bool("option") != true {
t.Errorf("short name not set")
}
return nil
},
Flags: []Flag{
&BoolFlag{Name: "serve", Aliases: []string{"s"}},
&BoolFlag{Name: "option", Aliases: []string{"o"}},
},
},
},
}).Run([]string{"run", "foobar", "-so"})
}
func TestParseDestinationBool(t *testing.T) {
var dest bool
_ = (&App{
Flags: []Flag{
&BoolFlag{
Name: "dest",
Destination: &dest,
},
},
Action: func(ctx *Context) error {
if dest != true {
t.Errorf("expected destination Bool true")
}
return nil
},
}).Run([]string{"run", "--dest"})
}
func TestParseMultiBoolFromEnv(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_DEBUG", "1")
_ = (&App{
Flags: []Flag{
&BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"APP_DEBUG"}},
},
Action: func(ctx *Context) error {
if ctx.Bool("debug") != true {
t.Errorf("main name not set from env")
}
if ctx.Bool("d") != true {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseMultiBoolFromEnvCascade(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_DEBUG", "1")
_ = (&App{
Flags: []Flag{
&BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"COMPAT_DEBUG", "APP_DEBUG"}},
},
Action: func(ctx *Context) error {
if ctx.Bool("debug") != true {
t.Errorf("main name not set from env")
}
if ctx.Bool("d") != true {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseBoolFromEnv(t *testing.T) {
var boolFlagTests = []struct {
input string
output bool
}{
{"", false},
{"1", true},
{"false", false},
{"true", true},
}
for _, test := range boolFlagTests {
os.Clearenv()
_ = os.Setenv("DEBUG", test.input)
_ = (&App{
Flags: []Flag{
&BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"DEBUG"}},
},
Action: func(ctx *Context) error {
if ctx.Bool("debug") != test.output {
t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("debug"))
}
if ctx.Bool("d") != test.output {
t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("d"))
}
return nil
},
}).Run([]string{"run"})
}
}
func TestParseMultiBoolT(t *testing.T) {
_ = (&App{
Flags: []Flag{
&BoolFlag{Name: "implode", Aliases: []string{"i"}, Value: true},
},
Action: func(ctx *Context) error {
if ctx.Bool("implode") {
t.Errorf("main name not set")
}
if ctx.Bool("i") {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run", "--implode=false"})
}
type Parser [2]string
func (p *Parser) Set(value string) error {
parts := strings.Split(value, ",")
if len(parts) != 2 {
return fmt.Errorf("invalid format")
}
(*p)[0] = parts[0]
(*p)[1] = parts[1]
return nil
}
func (p *Parser) String() string {
return fmt.Sprintf("%s,%s", p[0], p[1])
}
func (p *Parser) Get() interface{} {
return p
}
func TestParseGeneric(t *testing.T) {
_ = (&App{
Flags: []Flag{
&GenericFlag{Name: "serve", Aliases: []string{"s"}, Value: &Parser{}},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) {
t.Errorf("short name not set")
}
return nil
},
}).Run([]string{"run", "-s", "10,20"})
}
func TestParseGenericFromEnv(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_SERVE", "20,30")
_ = (&App{
Flags: []Flag{
&GenericFlag{
Name: "serve",
Aliases: []string{"s"},
Value: &Parser{},
EnvVars: []string{"APP_SERVE"},
},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) {
t.Errorf("short name not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestParseGenericFromEnvCascade(t *testing.T) {
os.Clearenv()
_ = os.Setenv("APP_FOO", "99,2000")
_ = (&App{
Flags: []Flag{
&GenericFlag{
Name: "foos",
Value: &Parser{},
EnvVars: []string{"COMPAT_FOO", "APP_FOO"},
},
},
Action: func(ctx *Context) error {
if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) {
t.Errorf("value not set from env")
}
return nil
},
}).Run([]string{"run"})
}
func TestFlagFromFile(t *testing.T) {
os.Clearenv()
os.Setenv("APP_FOO", "123")
temp, err := ioutil.TempFile(osTempDir, "urfave_cli_test")
if err != nil {
t.Error(err)
return
}
_, _ = io.WriteString(temp, "abc")
_ = temp.Close()
defer func() {
_ = os.Remove(temp.Name())
}()
var filePathTests = []struct {
path string
name []string
expected string
}{
{"file-does-not-exist", []string{"APP_BAR"}, ""},
{"file-does-not-exist", []string{"APP_FOO"}, "123"},
{"file-does-not-exist", []string{"APP_FOO", "APP_BAR"}, "123"},
{temp.Name(), []string{"APP_FOO"}, "123"},
{temp.Name(), []string{"APP_BAR"}, "abc"},
}
for _, filePathTest := range filePathTests {
got, _ := flagFromEnvOrFile(filePathTest.name, filePathTest.path)
if want := filePathTest.expected; got != want {
t.Errorf("Did not expect %v - Want %v", got, want)
}
}
}
func TestStringSlice_Serialized_Set(t *testing.T) {
sl0 := NewStringSlice("a", "b")
ser0 := sl0.Serialize()
if len(ser0) < len(slPfx) {
t.Fatalf("serialized shorter than expected: %q", ser0)
}
sl1 := NewStringSlice("c", "d")
_ = sl1.Set(ser0)
if sl0.String() != sl1.String() {
t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
}
}
func TestIntSlice_Serialized_Set(t *testing.T) {
sl0 := NewIntSlice(1, 2)
ser0 := sl0.Serialize()
if len(ser0) < len(slPfx) {
t.Fatalf("serialized shorter than expected: %q", ser0)
}
sl1 := NewIntSlice(3, 4)
_ = sl1.Set(ser0)
if sl0.String() != sl1.String() {
t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
}
}
func TestInt64Slice_Serialized_Set(t *testing.T) {
sl0 := NewInt64Slice(int64(1), int64(2))
ser0 := sl0.Serialize()
if len(ser0) < len(slPfx) {
t.Fatalf("serialized shorter than expected: %q", ser0)
}
sl1 := NewInt64Slice(int64(3), int64(4))
_ = sl1.Set(ser0)
if sl0.String() != sl1.String() {
t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
}
}
func TestTimestamp_set(t *testing.T) {
ts := Timestamp{
timestamp: nil,
hasBeenSet: false,
layout: "Jan 2, 2006 at 3:04pm (MST)",
}
time1 := "Feb 3, 2013 at 7:54pm (PST)"
if err := ts.Set(time1); err != nil {
t.Fatalf("Failed to parse time %s with layout %s", time1, ts.layout)
}
if ts.hasBeenSet == false {
t.Fatalf("hasBeenSet is not true after setting a time")
}
ts.hasBeenSet = false
ts.SetLayout(time.RFC3339)
time2 := "2006-01-02T15:04:05Z"
if err := ts.Set(time2); err != nil {
t.Fatalf("Failed to parse time %s with layout %s", time2, ts.layout)
}
if ts.hasBeenSet == false {
t.Fatalf("hasBeenSet is not true after setting a time")
}
}
func TestTimestampFlagApply(t *testing.T) {
expectedResult, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: time.RFC3339}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
expect(t, err, nil)
expect(t, *fl.Value.timestamp, expectedResult)
}
func TestTimestampFlagApply_Fail_Parse_Wrong_Layout(t *testing.T) {
fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: "randomlayout"}
set := flag.NewFlagSet("test", 0)
set.SetOutput(ioutil.Discard)
_ = fl.Apply(set)
err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"randomlayout\": cannot parse \"2006-01-02T15:04:05Z\" as \"randomlayout\""))
}
func TestTimestampFlagApply_Fail_Parse_Wrong_Time(t *testing.T) {
fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: "Jan 2, 2006 at 3:04pm (MST)"}
set := flag.NewFlagSet("test", 0)
set.SetOutput(ioutil.Discard)
_ = fl.Apply(set)
err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"Jan 2, 2006 at 3:04pm (MST)\": cannot parse \"2006-01-02T15:04:05Z\" as \"Jan\""))
}