From d3bb346fce967dc7d4e692c10a220ba6a8c62228 Mon Sep 17 00:00:00 2001 From: Igor Postelnik Date: Fri, 12 Feb 2021 09:48:06 -0600 Subject: [PATCH] Improved consistency in implementation and testing of file-based input sources. - Updated unit tests for atlsrc handling to match expected usage via cli.App. Previously, the tests tried to mock cli.Context which caused differences in behavior. - The types of altsrc flags are now limited to the subset that provide FlagInputSourceExtension implementation and have matching methods in InputSourceContext interface. Removed types where never updated from the input source. - Added script to generate altsrc flag definitions, restricted list of types to the ones that provide implementation of FlagInputSourceExtension. The original program to generate altsrc flags is nowhere to be found, so replaced with fg.py. --- altsrc/fg.py | 45 ++++ altsrc/flag.go | 34 +-- altsrc/flag_generated.go | 124 ++------- altsrc/flag_test.go | 567 ++++++++++++++++++++++++--------------- flag_generic.go | 2 +- 5 files changed, 436 insertions(+), 336 deletions(-) create mode 100644 altsrc/fg.py diff --git a/altsrc/fg.py b/altsrc/fg.py new file mode 100644 index 0000000000..f0d150f1be --- /dev/null +++ b/altsrc/fg.py @@ -0,0 +1,45 @@ +# Only types that provide implementation of FlagInputSourceExtension can be listed here +# please keep list sorted alphabetically +flag_types = [ + "Bool", + "Duration", + "Float64", + "Generic", + "Int", + "IntSlice", + "Path", + "String", + "StringSlice", +] + +print('''// Code generated by fg.py; DO NOT EDIT. + +package altsrc + +import ( + "flag" + + "github.com/urfave/cli/v2" +)''') + +for t in flag_types: + print(f''' +// {t}Flag is the flag type that wraps cli.{t}Flag to allow +// for other values to be specified +type {t}Flag struct {{ + *cli.{t}Flag + set *flag.FlagSet +}} +var _ FlagInputSourceExtension = (*{t}Flag)(nil) + +// New{t}Flag creates a new {t}Flag +func New{t}Flag(fl *cli.{t}Flag) *{t}Flag {{ + return &{t}Flag{{{t}Flag: fl, set: nil}} +}} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped {t}Flag.Apply +func (f *{t}Flag) Apply(set *flag.FlagSet) error {{ + f.set = set + return f.{t}Flag.Apply(set) +}}''') \ No newline at end of file diff --git a/altsrc/flag.go b/altsrc/flag.go index 31b8a04e87..36413216ea 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -64,7 +64,7 @@ func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context // ApplyInputSourceValue applies a generic value to the flagSet if required func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { - if f.set != nil { + if context != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.Generic(f.GenericFlag.Name) if err != nil { @@ -72,7 +72,7 @@ func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourc } if value != nil { for _, name := range f.Names() { - _ = f.set.Set(name, value.String()) + _ = context.Set(name, value.String()) } } } @@ -83,7 +83,7 @@ func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourc // ApplyInputSourceValue applies a StringSlice value to the flagSet if required func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { - if f.set != nil { + if context != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.StringSlice(f.StringSliceFlag.Name) if err != nil { @@ -94,6 +94,7 @@ func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputS for _, name := range f.Names() { underlyingFlag := f.set.Lookup(name) if underlyingFlag != nil { + context.Set(name, sliceValue.Serialize()) underlyingFlag.Value = &sliceValue } } @@ -105,7 +106,7 @@ func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputS // ApplyInputSourceValue applies a IntSlice value if required func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { - if f.set != nil { + if context != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.IntSlice(f.IntSliceFlag.Name) if err != nil { @@ -116,6 +117,7 @@ func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSour for _, name := range f.Names() { underlyingFlag := f.set.Lookup(name) if underlyingFlag != nil { + context.Set(name, sliceValue.Serialize()) underlyingFlag.Value = &sliceValue } } @@ -127,7 +129,7 @@ func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSour // ApplyInputSourceValue applies a Bool value to the flagSet if required func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { - if f.set != nil { + if context != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.Bool(f.BoolFlag.Name) if err != nil { @@ -135,7 +137,7 @@ func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo } if value { for _, name := range f.Names() { - _ = f.set.Set(name, strconv.FormatBool(value)) + _ = context.Set(name, strconv.FormatBool(value)) } } } @@ -145,7 +147,7 @@ func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo // ApplyInputSourceValue applies a String value to the flagSet if required func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { - if f.set != nil { + if context != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.String(f.StringFlag.Name) if err != nil { @@ -153,7 +155,7 @@ func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSource } if value != "" { for _, name := range f.Names() { - _ = f.set.Set(name, value) + _ = context.Set(name, value) } } } @@ -163,7 +165,7 @@ func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSource // ApplyInputSourceValue applies a Path value to the flagSet if required func (f *PathFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { - if f.set != nil { + if context != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.String(f.PathFlag.Name) if err != nil { @@ -181,7 +183,7 @@ func (f *PathFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo value = filepath.Join(filepath.Dir(basePathAbs), value) } - _ = f.set.Set(name, value) + _ = context.Set(name, value) } } } @@ -191,7 +193,7 @@ func (f *PathFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo // ApplyInputSourceValue applies a int value to the flagSet if required func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { - if f.set != nil { + if context != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Int(f.IntFlag.Name) if err != nil { @@ -199,7 +201,7 @@ func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCon } if value > 0 { for _, name := range f.Names() { - _ = f.set.Set(name, strconv.FormatInt(int64(value), 10)) + _ = context.Set(name, strconv.FormatInt(int64(value), 10)) } } } @@ -209,7 +211,7 @@ func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCon // ApplyInputSourceValue applies a Duration value to the flagSet if required func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { - if f.set != nil { + if context != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Duration(f.DurationFlag.Name) if err != nil { @@ -217,7 +219,7 @@ func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSour } if value > 0 { for _, name := range f.Names() { - _ = f.set.Set(name, value.String()) + _ = context.Set(name, value.String()) } } } @@ -227,7 +229,7 @@ func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSour // ApplyInputSourceValue applies a Float64 value to the flagSet if required func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { - if f.set != nil { + if context != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Float64(f.Float64Flag.Name) if err != nil { @@ -236,7 +238,7 @@ func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourc if value > 0 { floatStr := float64ToString(value) for _, name := range f.Names() { - _ = f.set.Set(name, floatStr) + _ = context.Set(name, floatStr) } } } diff --git a/altsrc/flag_generated.go b/altsrc/flag_generated.go index c9ecea82e9..bdaf5a5ff0 100644 --- a/altsrc/flag_generated.go +++ b/altsrc/flag_generated.go @@ -1,4 +1,4 @@ -// Code generated by fg; DO NOT EDIT. +// Code generated by fg.py; DO NOT EDIT. package altsrc @@ -14,6 +14,7 @@ type BoolFlag struct { *cli.BoolFlag set *flag.FlagSet } +var _ FlagInputSourceExtension = (*BoolFlag)(nil) // NewBoolFlag creates a new BoolFlag func NewBoolFlag(fl *cli.BoolFlag) *BoolFlag { @@ -33,6 +34,7 @@ type DurationFlag struct { *cli.DurationFlag set *flag.FlagSet } +var _ FlagInputSourceExtension = (*DurationFlag)(nil) // NewDurationFlag creates a new DurationFlag func NewDurationFlag(fl *cli.DurationFlag) *DurationFlag { @@ -52,6 +54,7 @@ type Float64Flag struct { *cli.Float64Flag set *flag.FlagSet } +var _ FlagInputSourceExtension = (*Float64Flag)(nil) // NewFloat64Flag creates a new Float64Flag func NewFloat64Flag(fl *cli.Float64Flag) *Float64Flag { @@ -71,6 +74,7 @@ type GenericFlag struct { *cli.GenericFlag set *flag.FlagSet } +var _ FlagInputSourceExtension = (*GenericFlag)(nil) // NewGenericFlag creates a new GenericFlag func NewGenericFlag(fl *cli.GenericFlag) *GenericFlag { @@ -84,31 +88,13 @@ func (f *GenericFlag) Apply(set *flag.FlagSet) error { return f.GenericFlag.Apply(set) } -// Int64Flag is the flag type that wraps cli.Int64Flag to allow -// for other values to be specified -type Int64Flag struct { - *cli.Int64Flag - set *flag.FlagSet -} - -// NewInt64Flag creates a new Int64Flag -func NewInt64Flag(fl *cli.Int64Flag) *Int64Flag { - return &Int64Flag{Int64Flag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped Int64Flag.Apply -func (f *Int64Flag) Apply(set *flag.FlagSet) error { - f.set = set - return f.Int64Flag.Apply(set) -} - // IntFlag is the flag type that wraps cli.IntFlag to allow // for other values to be specified type IntFlag struct { *cli.IntFlag set *flag.FlagSet } +var _ FlagInputSourceExtension = (*IntFlag)(nil) // NewIntFlag creates a new IntFlag func NewIntFlag(fl *cli.IntFlag) *IntFlag { @@ -128,6 +114,7 @@ type IntSliceFlag struct { *cli.IntSliceFlag set *flag.FlagSet } +var _ FlagInputSourceExtension = (*IntSliceFlag)(nil) // NewIntSliceFlag creates a new IntSliceFlag func NewIntSliceFlag(fl *cli.IntSliceFlag) *IntSliceFlag { @@ -141,42 +128,24 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { return f.IntSliceFlag.Apply(set) } -// Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow +// PathFlag is the flag type that wraps cli.PathFlag to allow // for other values to be specified -type Int64SliceFlag struct { - *cli.Int64SliceFlag +type PathFlag struct { + *cli.PathFlag set *flag.FlagSet } +var _ FlagInputSourceExtension = (*PathFlag)(nil) -// NewInt64SliceFlag creates a new Int64SliceFlag -func NewInt64SliceFlag(fl *cli.Int64SliceFlag) *Int64SliceFlag { - return &Int64SliceFlag{Int64SliceFlag: fl, set: nil} +// NewPathFlag creates a new PathFlag +func NewPathFlag(fl *cli.PathFlag) *PathFlag { + return &PathFlag{PathFlag: fl, set: nil} } // Apply saves the flagSet for later usage calls, then calls -// the wrapped Int64SliceFlag.Apply -func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { - f.set = set - return f.Int64SliceFlag.Apply(set) -} - -// Float64SliceFlag is the flag type that wraps cli.Float64SliceFlag to allow -// for other values to be specified -type Float64SliceFlag struct { - *cli.Float64SliceFlag - set *flag.FlagSet -} - -// NewFloat64SliceFlag creates a new Float64SliceFlag -func NewFloat64SliceFlag(fl *cli.Float64SliceFlag) *Float64SliceFlag { - return &Float64SliceFlag{Float64SliceFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls the -// wrapped Float64SliceFlag.Apply -func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { +// the wrapped PathFlag.Apply +func (f *PathFlag) Apply(set *flag.FlagSet) error { f.set = set - return f.Float64SliceFlag.Apply(set) + return f.PathFlag.Apply(set) } // StringFlag is the flag type that wraps cli.StringFlag to allow @@ -185,6 +154,7 @@ type StringFlag struct { *cli.StringFlag set *flag.FlagSet } +var _ FlagInputSourceExtension = (*StringFlag)(nil) // NewStringFlag creates a new StringFlag func NewStringFlag(fl *cli.StringFlag) *StringFlag { @@ -198,31 +168,13 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error { return f.StringFlag.Apply(set) } -// PathFlag is the flag type that wraps cli.PathFlag to allow -// for other values to be specified -type PathFlag struct { - *cli.PathFlag - set *flag.FlagSet -} - -// NewPathFlag creates a new PathFlag -func NewPathFlag(fl *cli.PathFlag) *PathFlag { - return &PathFlag{PathFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls the -// wrapped PathFlag.Apply -func (f *PathFlag) Apply(set *flag.FlagSet) error { - f.set = set - return f.PathFlag.Apply(set) -} - // StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow // for other values to be specified type StringSliceFlag struct { *cli.StringSliceFlag set *flag.FlagSet } +var _ FlagInputSourceExtension = (*StringSliceFlag)(nil) // NewStringSliceFlag creates a new StringSliceFlag func NewStringSliceFlag(fl *cli.StringSliceFlag) *StringSliceFlag { @@ -235,41 +187,3 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { f.set = set return f.StringSliceFlag.Apply(set) } - -// Uint64Flag is the flag type that wraps cli.Uint64Flag to allow -// for other values to be specified -type Uint64Flag struct { - *cli.Uint64Flag - set *flag.FlagSet -} - -// NewUint64Flag creates a new Uint64Flag -func NewUint64Flag(fl *cli.Uint64Flag) *Uint64Flag { - return &Uint64Flag{Uint64Flag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped Uint64Flag.Apply -func (f *Uint64Flag) Apply(set *flag.FlagSet) error { - f.set = set - return f.Uint64Flag.Apply(set) -} - -// UintFlag is the flag type that wraps cli.UintFlag to allow -// for other values to be specified -type UintFlag struct { - *cli.UintFlag - set *flag.FlagSet -} - -// NewUintFlag creates a new UintFlag -func NewUintFlag(fl *cli.UintFlag) *UintFlag { - return &UintFlag{UintFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped UintFlag.Apply -func (f *UintFlag) Apply(set *flag.FlagSet) error { - f.set = set - return f.UintFlag.Apply(set) -} diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index 204833118f..275a7cde31 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -1,7 +1,6 @@ package altsrc import ( - "flag" "fmt" "os" "path/filepath" @@ -19,7 +18,6 @@ type testApplyInputSource struct { FlagSetName string Expected string ContextValueString string - ContextValue flag.Value EnvVarValue string EnvVarName string SourcePath string @@ -28,167 +26,236 @@ type testApplyInputSource struct { func TestGenericApplyInputSourceValue(t *testing.T) { v := &Parser{"abc", "def"} - c := runTest(t, testApplyInputSource{ - Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Value: &Parser{}}), - FlagName: "test", - MapValue: v, - }) - expect(t, v, c.Generic("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Value: &Parser{}}), + FlagName: "test", + MapValue: v, + }, + func(c *cli.Context) { + expect(t, v, c.Generic("test")) + }, + ) } func TestGenericApplyInputSourceMethodContextSet(t *testing.T) { p := &Parser{"abc", "def"} - c := runTest(t, testApplyInputSource{ - Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Value: &Parser{}}), - FlagName: "test", - MapValue: &Parser{"efg", "hig"}, - ContextValueString: p.String(), - }) - expect(t, p, c.Generic("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Value: &Parser{}}), + FlagName: "test", + MapValue: &Parser{"efg", "hig"}, + ContextValueString: p.String(), + }, + func(c *cli.Context) { + expect(t, p, c.Generic("test")) + }, + ) } func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewGenericFlag(&cli.GenericFlag{ - Name: "test", - Value: &Parser{}, - EnvVars: []string{"TEST"}, - }), - FlagName: "test", - MapValue: &Parser{"efg", "hij"}, - EnvVarName: "TEST", - EnvVarValue: "abc,def", - }) - expect(t, &Parser{"abc", "def"}, c.Generic("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewGenericFlag(&cli.GenericFlag{ + Name: "test", + Value: &Parser{}, + EnvVars: []string{"TEST"}, + }), + FlagName: "test", + MapValue: &Parser{"efg", "hij"}, + EnvVarName: "TEST", + EnvVarValue: "abc,def", + }, + func(c *cli.Context) { + expect(t, &Parser{"abc", "def"}, c.Generic("test")) + }, + ) } func TestStringSliceApplyInputSourceValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}), - FlagName: "test", - MapValue: []interface{}{"hello", "world"}, - }) - expect(t, c.StringSlice("test"), []string{"hello", "world"}) + runTest( + t, + testApplyInputSource{ + Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}), + FlagName: "test", + MapValue: []interface{}{"hello", "world"}, + }, + func(c *cli.Context) { + expect(t, []string{"hello", "world"}, c.StringSlice("test")) + }, + ) } func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}), - FlagName: "test", - MapValue: []interface{}{"hello", "world"}, - ContextValueString: "ohno", - }) - expect(t, c.StringSlice("test"), []string{"ohno"}) + runTest( + t, + testApplyInputSource{ + Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}), + FlagName: "test", + MapValue: []interface{}{"hello", "world"}, + ContextValueString: "ohno", + }, + func(c *cli.Context) { + expect(t, []string{"ohno"}, c.StringSlice("test")) + }, + ) } func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), - FlagName: "test", - MapValue: []interface{}{"hello", "world"}, - EnvVarName: "TEST", - EnvVarValue: "oh,no", - }) - expect(t, c.StringSlice("test"), []string{"oh", "no"}) + runTest( + t, + testApplyInputSource{ + Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), + FlagName: "test", + MapValue: []interface{}{"hello", "world"}, + EnvVarName: "TEST", + EnvVarValue: "oh,no", + }, + func(c *cli.Context) { + expect(t, []string{"oh", "no"}, c.StringSlice("test")) + }, + ) } func TestIntSliceApplyInputSourceValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), - FlagName: "test", - MapValue: []interface{}{1, 2}, - }) - expect(t, c.IntSlice("test"), []int{1, 2}) + runTest( + t, + testApplyInputSource{ + Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), + FlagName: "test", + MapValue: []interface{}{1, 2}, + }, + func(c *cli.Context) { + expect(t, []int{1, 2}, c.IntSlice("test")) + }, + ) } func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), - FlagName: "test", - MapValue: []interface{}{1, 2}, - ContextValueString: "3", - }) - expect(t, c.IntSlice("test"), []int{3}) + runTest( + t, + testApplyInputSource{ + Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), + FlagName: "test", + MapValue: []interface{}{1, 2}, + ContextValueString: "3", + }, + func(c *cli.Context) { + expect(t, []int{3}, c.IntSlice("test")) + }, + ) } func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), - FlagName: "test", - MapValue: []interface{}{1, 2}, - EnvVarName: "TEST", - EnvVarValue: "3,4", - }) - expect(t, c.IntSlice("test"), []int{3, 4}) + runTest( + t, + testApplyInputSource{ + Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), + FlagName: "test", + MapValue: []interface{}{1, 2}, + EnvVarName: "TEST", + EnvVarValue: "3,4", + }, + func(c *cli.Context) { + expect(t, []int{3, 4}, c.IntSlice("test")) + }, + ) } func TestBoolApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), - FlagName: "test", - MapValue: true, - }) - expect(t, true, c.Bool("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), + FlagName: "test", + MapValue: true, + }, + func(c *cli.Context) { + expect(t, true, c.Bool("test")) + }, + ) } func TestBoolApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), - FlagName: "test", - MapValue: false, - ContextValueString: "true", - }) - expect(t, true, c.Bool("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), + FlagName: "test", + MapValue: false, + ContextValueString: "true", + }, + func(c *cli.Context) { + expect(t, true, c.Bool("test")) + }, + ) } func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewBoolFlag(&cli.BoolFlag{Name: "test", EnvVars: []string{"TEST"}}), - FlagName: "test", - MapValue: false, - EnvVarName: "TEST", - EnvVarValue: "true", - }) - expect(t, true, c.Bool("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewBoolFlag(&cli.BoolFlag{Name: "test", EnvVars: []string{"TEST"}}), + FlagName: "test", + MapValue: false, + EnvVarName: "TEST", + EnvVarValue: "true", + }, + func(c *cli.Context) { + expect(t, true, c.Bool("test")) + }, + ) } func TestStringApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), - FlagName: "test", - MapValue: "hello", - }) - expect(t, "hello", c.String("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), + FlagName: "test", + MapValue: "hello", + }, + func(c *cli.Context) { + expect(t, "hello", c.String("test")) + }, + ) } func TestStringApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), - FlagName: "test", - MapValue: "hello", - ContextValueString: "goodbye", - }) - expect(t, "goodbye", c.String("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), + FlagName: "test", + MapValue: "hello", + ContextValueString: "goodbye", + }, + func(c *cli.Context) { + expect(t, "goodbye", c.String("test")) + }, + ) } func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewStringFlag(&cli.StringFlag{Name: "test", EnvVars: []string{"TEST"}}), - FlagName: "test", - MapValue: "hello", - EnvVarName: "TEST", - EnvVarValue: "goodbye", - }) - expect(t, "goodbye", c.String("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewStringFlag(&cli.StringFlag{Name: "test", EnvVars: []string{"TEST"}}), + FlagName: "test", + MapValue: "hello", + EnvVarName: "TEST", + EnvVarValue: "goodbye", + }, + func(c *cli.Context) { + expect(t, "goodbye", c.String("test")) + }, + ) } -func TestPathApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewPathFlag(&cli.PathFlag{Name: "test"}), - FlagName: "test", - MapValue: "hello", - SourcePath: "/path/to/source/file", - }) +func TestPathApplyInputSourceMethodSet(t *testing.T) { expected := "/path/to/source/hello" if runtime.GOOS == "windows" { var err error @@ -199,145 +266,217 @@ func TestPathApplyInputSourceMethodSet(t *testing.T) { t.Fatal(err) } } - expect(t, expected, c.String("test")) + + runTest( + t, + testApplyInputSource{ + Flag: NewPathFlag(&cli.PathFlag{Name: "test"}), + FlagName: "test", + MapValue: "hello", + SourcePath: "/path/to/source/file", + }, + func(c *cli.Context) { + expect(t, expected, c.String("test")) + }, + ) } func TestPathApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewPathFlag(&cli.PathFlag{Name: "test"}), - FlagName: "test", - MapValue: "hello", - ContextValueString: "goodbye", - SourcePath: "/path/to/source/file", - }) - expect(t, "goodbye", c.String("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewPathFlag(&cli.PathFlag{Name: "test"}), + FlagName: "test", + MapValue: "hello", + ContextValueString: "goodbye", + SourcePath: "/path/to/source/file", + }, + func(c *cli.Context) { + expect(t, "goodbye", c.String("test")) + }, + ) } func TestPathApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewPathFlag(&cli.PathFlag{Name: "test", EnvVars: []string{"TEST"}}), - FlagName: "test", - MapValue: "hello", - EnvVarName: "TEST", - EnvVarValue: "goodbye", - SourcePath: "/path/to/source/file", - }) - expect(t, "goodbye", c.String("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewPathFlag(&cli.PathFlag{Name: "test", EnvVars: []string{"TEST"}}), + FlagName: "test", + MapValue: "hello", + EnvVarName: "TEST", + EnvVarValue: "goodbye", + SourcePath: "/path/to/source/file", + }, + func(c *cli.Context) { + expect(t, "goodbye", c.String("test")) + }, + ) } func TestIntApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), - FlagName: "test", - MapValue: 15, - }) - expect(t, 15, c.Int("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), + FlagName: "test", + MapValue: 15, + }, + func(c *cli.Context) { + expect(t, 15, c.Int("test")) + }, + ) } - func TestIntApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), - FlagName: "test", - MapValue: 15, - ContextValueString: "7", - }) - expect(t, 7, c.Int("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), + FlagName: "test", + MapValue: 15, + ContextValueString: "7", + }, + func(c *cli.Context) { + expect(t, 7, c.Int("test")) + }, + ) } func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewIntFlag(&cli.IntFlag{Name: "test", EnvVars: []string{"TEST"}}), - FlagName: "test", - MapValue: 15, - EnvVarName: "TEST", - EnvVarValue: "12", - }) - expect(t, 12, c.Int("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewIntFlag(&cli.IntFlag{Name: "test", EnvVars: []string{"TEST"}}), + FlagName: "test", + MapValue: 15, + EnvVarName: "TEST", + EnvVarValue: "12", + }, + func(c *cli.Context) { + expect(t, 12, c.Int("test")) + }, + ) } func TestDurationApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), - FlagName: "test", - MapValue: 30 * time.Second, - }) - expect(t, 30*time.Second, c.Duration("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), + FlagName: "test", + MapValue: 30 * time.Second, + }, + func(c *cli.Context) { + expect(t, 30*time.Second, c.Duration("test")) + }, + ) } func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), - FlagName: "test", - MapValue: 30 * time.Second, - ContextValueString: (15 * time.Second).String(), - }) - expect(t, 15*time.Second, c.Duration("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), + FlagName: "test", + MapValue: 30 * time.Second, + ContextValueString: (15 * time.Second).String(), + }, + func(c *cli.Context) { + expect(t, 15*time.Second, c.Duration("test")) + }, + ) } func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewDurationFlag(&cli.DurationFlag{Name: "test", EnvVars: []string{"TEST"}}), - FlagName: "test", - MapValue: 30 * time.Second, - EnvVarName: "TEST", - EnvVarValue: (15 * time.Second).String(), - }) - expect(t, 15*time.Second, c.Duration("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewDurationFlag(&cli.DurationFlag{Name: "test", EnvVars: []string{"TEST"}}), + FlagName: "test", + MapValue: 30 * time.Second, + EnvVarName: "TEST", + EnvVarValue: (15 * time.Second).String(), + }, + func(c *cli.Context) { + expect(t, 15*time.Second, c.Duration("test")) + }, + ) } func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), - FlagName: "test", - MapValue: 1.3, - }) - expect(t, 1.3, c.Float64("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), + FlagName: "test", + MapValue: 1.3, + }, + func(c *cli.Context) { + expect(t, 1.3, c.Float64("test")) + }, + ) } func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), - FlagName: "test", - MapValue: 1.3, - ContextValueString: fmt.Sprintf("%v", 1.4), - }) - expect(t, 1.4, c.Float64("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), + FlagName: "test", + MapValue: 1.3, + ContextValueString: fmt.Sprintf("%v", 1.4), + }, + func(c *cli.Context) { + expect(t, 1.4, c.Float64("test")) + }, + ) } func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ - Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test", EnvVars: []string{"TEST"}}), - FlagName: "test", - MapValue: 1.3, - EnvVarName: "TEST", - EnvVarValue: fmt.Sprintf("%v", 1.4), - }) - expect(t, 1.4, c.Float64("test")) + runTest( + t, + testApplyInputSource{ + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test", EnvVars: []string{"TEST"}}), + FlagName: "test", + MapValue: 1.3, + EnvVarName: "TEST", + EnvVarValue: fmt.Sprintf("%v", 1.4), + }, + func(c *cli.Context) { + expect(t, 1.4, c.Float64("test")) + }, + ) } -func runTest(t *testing.T, test testApplyInputSource) *cli.Context { +func runTest(t *testing.T, test testApplyInputSource, checkVal func(*cli.Context)) { inputSource := &MapInputSource{ file: test.SourcePath, valueMap: map[interface{}]interface{}{test.FlagName: test.MapValue}, } - set := flag.NewFlagSet(test.FlagSetName, flag.ContinueOnError) - c := cli.NewContext(nil, set, nil) + + app := cli.App{ + Flags: []cli.Flag{ + test.Flag, + }, + Action: func(c *cli.Context) error { + if test.ContextValueString != "" { + _ = c.Set(test.FlagName, test.ContextValueString) + } + _ = test.Flag.ApplyInputSourceValue(c, inputSource) + + expect(t, c.IsSet(test.FlagName), true) + checkVal(c) + + return nil + }, + } + if test.EnvVarName != "" && test.EnvVarValue != "" { _ = os.Setenv(test.EnvVarName, test.EnvVarValue) defer os.Setenv(test.EnvVarName, "") } - _ = test.Flag.Apply(set) - if test.ContextValue != nil { - f := set.Lookup(test.FlagName) - f.Value = test.ContextValue - } - if test.ContextValueString != "" { - _ = set.Set(test.FlagName, test.ContextValueString) - } - _ = test.Flag.ApplyInputSourceValue(c, inputSource) - - return c + _ = app.Run([]string{"the-app"}) } type Parser [2]string diff --git a/flag_generic.go b/flag_generic.go index b0c8ff44d2..5d1f815c95 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -68,7 +68,7 @@ func (f *GenericFlag) GetValue() string { // Apply takes the flagset and calls Set on the generic flag with the value // provided by the user for parsing by the flag -func (f GenericFlag) Apply(set *flag.FlagSet) error { +func (f *GenericFlag) Apply(set *flag.FlagSet) error { if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { if val != "" { if err := f.Value.Set(val); err != nil {