aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastiaan van Stijn <[email protected]>2025-09-01 12:48:45 +0200
committerSebastiaan van Stijn <[email protected]>2025-09-01 23:10:11 +0200
commit18a9d17d0ee8bd64d5c2071fc031be86fa2cd328 (patch)
treeb9ff208cf79a488329fddf768cd832b21cbaaa94
parentc5b9e989df31c5d19573e50d6188550ad51a971e (diff)
move Func, BoolFunc, tests as they require go1.21
Commit 69bc3bd5b37fa90e994be9acecf7430269591713 added support for Func() and BoolFunc() to match stdlib. However, the Func method was added in [go1.16.0], and BoolFunc in [go1.21.0], so running the tests on older versions of Go would fail; docker run -it --rm -v ./:/pflag -w /pflag golang:1.21 sh -c 'go test -v ./...' # github.com/spf13/pflag [github.com/spf13/pflag.test] ./bool_func_test.go:86:28: cannot use stdFSet (type *flag.FlagSet) as type BoolFuncFlagSet in argument to runCase: *flag.FlagSet does not implement BoolFuncFlagSet (missing BoolFunc method) ./bool_func_test.go:113:21: undefined: io.Discard ./bool_func_test.go:116:28: cannot use stdFSet (type *flag.FlagSet) as type BoolFuncFlagSet in argument to runCase: *flag.FlagSet does not implement BoolFuncFlagSet (missing BoolFunc method) ./bool_func_test.go:139:7: undefined: errors.Is ./func_test.go:92:28: cannot use stdFSet (type *flag.FlagSet) as type FuncFlagSet in argument to runCase: *flag.FlagSet does not implement FuncFlagSet (missing Func method) ./func_test.go:119:21: undefined: io.Discard ./func_test.go:122:28: cannot use stdFSet (type *flag.FlagSet) as type FuncFlagSet in argument to runCase: *flag.FlagSet does not implement FuncFlagSet (missing Func method) ./func_test.go:145:7: undefined: errors.Is ./func_test.go:145:7: too many errors FAIL github.com/spf13/pflag [build failed] This patch moves the tests to a separate file that is not built for older versions of Go. [go1.16.0]: https://pkg.go.dev/[email protected]#Func [go1.21.0]: https://pkg.go.dev/[email protected]#BoolFunc Signed-off-by: Sebastiaan van Stijn <[email protected]>
-rw-r--r--bool_func_go1.21_test.go110
-rw-r--r--bool_func_test.go101
-rw-r--r--func_go1.21_test.go102
-rw-r--r--func_test.go93
4 files changed, 212 insertions, 194 deletions
diff --git a/bool_func_go1.21_test.go b/bool_func_go1.21_test.go
new file mode 100644
index 0000000..5b1bd55
--- /dev/null
+++ b/bool_func_go1.21_test.go
@@ -0,0 +1,110 @@
+//go:build go1.21
+// +build go1.21
+
+package pflag
+
+import (
+ "errors"
+ "flag"
+ "io"
+ "strings"
+ "testing"
+)
+
+func TestBoolFuncCompat(t *testing.T) {
+ // compare behavior with the stdlib 'flag' package
+ type BoolFuncFlagSet interface {
+ BoolFunc(name string, usage string, fn func(string) error)
+ Parse([]string) error
+ }
+
+ unitTestErr := errors.New("unit test error")
+ runCase := func(f BoolFuncFlagSet, name string, args []string) (values []string, err error) {
+ fn := func(s string) error {
+ values = append(values, s)
+ if s == "err" {
+ return unitTestErr
+ }
+ return nil
+ }
+ f.BoolFunc(name, "Callback function", fn)
+
+ err = f.Parse(args)
+ return values, err
+ }
+
+ t.Run("regular parsing", func(t *testing.T) {
+ flagName := "bflag"
+ args := []string{"--bflag", "--bflag=false", "--bflag=1", "--bflag=bar", "--bflag="}
+
+ // It turns out that, even though the function is called "BoolFunc",
+ // the standard flag package does not try to parse the value assigned to
+ // that cli flag as a boolean. The string provided on the command line is
+ // passed as is to the callback.
+ // e.g: with "--bflag=not_a_bool" on the command line, the FlagSet does not
+ // generate an error stating "invalid boolean value", and `fn` will be called
+ // with "not_a_bool" as an argument.
+
+ stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
+ stdValues, err := runCase(stdFSet, flagName, args)
+ if err != nil {
+ t.Fatalf("std flag: expected no error, got %v", err)
+ }
+ expected := []string{"true", "false", "1", "bar", ""}
+ if !cmpLists(expected, stdValues) {
+ t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
+ }
+
+ fset := NewFlagSet("pflag test", ContinueOnError)
+ pflagValues, err := runCase(fset, flagName, args)
+ if err != nil {
+ t.Fatalf("pflag: expected no error, got %v", err)
+ }
+ if !cmpLists(stdValues, pflagValues) {
+ t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
+ }
+ })
+
+ t.Run("error triggered by callback", func(t *testing.T) {
+ flagName := "bflag"
+ args := []string{"--bflag", "--bflag=err", "--bflag=after"}
+
+ // test behavior of standard flag.Fset with an error triggered by the callback:
+ // (note: as can be seen in 'runCase()', if the callback sees "err" as a value
+ // for the bool flag, it will return an error)
+ stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
+ stdFSet.SetOutput(io.Discard) // suppress output
+
+ // run test case with standard flag.Fset
+ stdValues, err := runCase(stdFSet, flagName, args)
+
+ // double check the standard behavior:
+ // - .Parse() should return an error, which contains the error message
+ if err == nil {
+ t.Fatalf("std flag: expected an error triggered by callback, got no error instead")
+ }
+ if !strings.HasSuffix(err.Error(), unitTestErr.Error()) {
+ t.Fatalf("std flag: expected unittest error, got unexpected error value: %T %v", err, err)
+ }
+ // - the function should have been called twice, with the first two values,
+ // the final "=after" should not be recorded
+ expected := []string{"true", "err"}
+ if !cmpLists(expected, stdValues) {
+ t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
+ }
+
+ // now run the test case on a pflag FlagSet:
+ fset := NewFlagSet("pflag test", ContinueOnError)
+ pflagValues, err := runCase(fset, flagName, args)
+
+ // check that there is a similar error (note: pflag will _wrap_ the error, while the stdlib
+ // currently keeps the original message but creates a flat errors.Error)
+ if !errors.Is(err, unitTestErr) {
+ t.Fatalf("pflag: got unexpected error value: %T %v", err, err)
+ }
+ // the callback should be called the same number of times, with the same values:
+ if !cmpLists(stdValues, pflagValues) {
+ t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
+ }
+ })
+}
diff --git a/bool_func_test.go b/bool_func_test.go
index c16be83..765c9c0 100644
--- a/bool_func_test.go
+++ b/bool_func_test.go
@@ -1,9 +1,6 @@
package pflag
import (
- "errors"
- "flag"
- "io"
"strings"
"testing"
)
@@ -48,104 +45,6 @@ func TestBoolFuncP(t *testing.T) {
}
}
-func TestBoolFuncCompat(t *testing.T) {
- // compare behavior with the stdlib 'flag' package
- type BoolFuncFlagSet interface {
- BoolFunc(name string, usage string, fn func(string) error)
- Parse([]string) error
- }
-
- unitTestErr := errors.New("unit test error")
- runCase := func(f BoolFuncFlagSet, name string, args []string) (values []string, err error) {
- fn := func(s string) error {
- values = append(values, s)
- if s == "err" {
- return unitTestErr
- }
- return nil
- }
- f.BoolFunc(name, "Callback function", fn)
-
- err = f.Parse(args)
- return values, err
- }
-
- t.Run("regular parsing", func(t *testing.T) {
- flagName := "bflag"
- args := []string{"--bflag", "--bflag=false", "--bflag=1", "--bflag=bar", "--bflag="}
-
- // It turns out that, even though the function is called "BoolFunc",
- // the standard flag package does not try to parse the value assigned to
- // that cli flag as a boolean. The string provided on the command line is
- // passed as is to the callback.
- // e.g: with "--bflag=not_a_bool" on the command line, the FlagSet does not
- // generate an error stating "invalid boolean value", and `fn` will be called
- // with "not_a_bool" as an argument.
-
- stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
- stdValues, err := runCase(stdFSet, flagName, args)
- if err != nil {
- t.Fatalf("std flag: expected no error, got %v", err)
- }
- expected := []string{"true", "false", "1", "bar", ""}
- if !cmpLists(expected, stdValues) {
- t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
- }
-
- fset := NewFlagSet("pflag test", ContinueOnError)
- pflagValues, err := runCase(fset, flagName, args)
- if err != nil {
- t.Fatalf("pflag: expected no error, got %v", err)
- }
- if !cmpLists(stdValues, pflagValues) {
- t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
- }
- })
-
- t.Run("error triggered by callback", func(t *testing.T) {
- flagName := "bflag"
- args := []string{"--bflag", "--bflag=err", "--bflag=after"}
-
- // test behavior of standard flag.Fset with an error triggered by the callback:
- // (note: as can be seen in 'runCase()', if the callback sees "err" as a value
- // for the bool flag, it will return an error)
- stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
- stdFSet.SetOutput(io.Discard) // suppress output
-
- // run test case with standard flag.Fset
- stdValues, err := runCase(stdFSet, flagName, args)
-
- // double check the standard behavior:
- // - .Parse() should return an error, which contains the error message
- if err == nil {
- t.Fatalf("std flag: expected an error triggered by callback, got no error instead")
- }
- if !strings.HasSuffix(err.Error(), unitTestErr.Error()) {
- t.Fatalf("std flag: expected unittest error, got unexpected error value: %T %v", err, err)
- }
- // - the function should have been called twice, with the first two values,
- // the final "=after" should not be recorded
- expected := []string{"true", "err"}
- if !cmpLists(expected, stdValues) {
- t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
- }
-
- // now run the test case on a pflag FlagSet:
- fset := NewFlagSet("pflag test", ContinueOnError)
- pflagValues, err := runCase(fset, flagName, args)
-
- // check that there is a similar error (note: pflag will _wrap_ the error, while the stdlib
- // currently keeps the original message but creates a flat errors.Error)
- if !errors.Is(err, unitTestErr) {
- t.Fatalf("pflag: got unexpected error value: %T %v", err, err)
- }
- // the callback should be called the same number of times, with the same values:
- if !cmpLists(stdValues, pflagValues) {
- t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
- }
- })
-}
-
func TestBoolFuncUsage(t *testing.T) {
t.Run("regular func flag", func(t *testing.T) {
// regular boolfunc flag:
diff --git a/func_go1.21_test.go b/func_go1.21_test.go
new file mode 100644
index 0000000..2d5ea31
--- /dev/null
+++ b/func_go1.21_test.go
@@ -0,0 +1,102 @@
+//go:build go1.21
+// +build go1.21
+
+package pflag
+
+import (
+ "errors"
+ "flag"
+ "io"
+ "strings"
+ "testing"
+)
+
+func TestFuncCompat(t *testing.T) {
+ // compare behavior with the stdlib 'flag' package
+ type FuncFlagSet interface {
+ Func(name string, usage string, fn func(string) error)
+ Parse([]string) error
+ }
+
+ unitTestErr := errors.New("unit test error")
+ runCase := func(f FuncFlagSet, name string, args []string) (values []string, err error) {
+ fn := func(s string) error {
+ values = append(values, s)
+ if s == "err" {
+ return unitTestErr
+ }
+ return nil
+ }
+ f.Func(name, "Callback function", fn)
+
+ err = f.Parse(args)
+ return values, err
+ }
+
+ t.Run("regular parsing", func(t *testing.T) {
+ flagName := "fnflag"
+ args := []string{"--fnflag=xx", "--fnflag", "yy", "--fnflag=zz"}
+
+ stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
+ stdValues, err := runCase(stdFSet, flagName, args)
+ if err != nil {
+ t.Fatalf("std flag: expected no error, got %v", err)
+ }
+ expected := []string{"xx", "yy", "zz"}
+ if !cmpLists(expected, stdValues) {
+ t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
+ }
+
+ fset := NewFlagSet("pflag test", ContinueOnError)
+ pflagValues, err := runCase(fset, flagName, args)
+ if err != nil {
+ t.Fatalf("pflag: expected no error, got %v", err)
+ }
+ if !cmpLists(stdValues, pflagValues) {
+ t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
+ }
+ })
+
+ t.Run("error triggered by callback", func(t *testing.T) {
+ flagName := "fnflag"
+ args := []string{"--fnflag", "before", "--fnflag", "err", "--fnflag", "after"}
+
+ // test behavior of standard flag.Fset with an error triggered by the callback:
+ // (note: as can be seen in 'runCase()', if the callback sees "err" as a value
+ // for the flag, it will return an error)
+ stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
+ stdFSet.SetOutput(io.Discard) // suppress output
+
+ // run test case with standard flag.Fset
+ stdValues, err := runCase(stdFSet, flagName, args)
+
+ // double check the standard behavior:
+ // - .Parse() should return an error, which contains the error message
+ if err == nil {
+ t.Fatalf("std flag: expected an error triggered by callback, got no error instead")
+ }
+ if !strings.HasSuffix(err.Error(), unitTestErr.Error()) {
+ t.Fatalf("std flag: expected unittest error, got unexpected error value: %T %v", err, err)
+ }
+ // - the function should have been called twice, with the first two values,
+ // the final "=after" should not be recorded
+ expected := []string{"before", "err"}
+ if !cmpLists(expected, stdValues) {
+ t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
+ }
+
+ // now run the test case on a pflag FlagSet:
+ fset := NewFlagSet("pflag test", ContinueOnError)
+ pflagValues, err := runCase(fset, flagName, args)
+
+ // check that there is a similar error (note: pflag will _wrap_ the error, while the stdlib
+ // currently keeps the original message but creates a flat errors.Error)
+ if !errors.Is(err, unitTestErr) {
+ t.Fatalf("pflag: got unexpected error value: %T %v", err, err)
+ }
+ // the callback should be called the same number of times, with the same values:
+ if !cmpLists(stdValues, pflagValues) {
+ t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
+ }
+ })
+}
diff --git a/func_test.go b/func_test.go
index d492b48..4badf93 100644
--- a/func_test.go
+++ b/func_test.go
@@ -1,9 +1,6 @@
package pflag
import (
- "errors"
- "flag"
- "io"
"strings"
"testing"
)
@@ -62,96 +59,6 @@ func TestFuncP(t *testing.T) {
}
}
-func TestFuncCompat(t *testing.T) {
- // compare behavior with the stdlib 'flag' package
- type FuncFlagSet interface {
- Func(name string, usage string, fn func(string) error)
- Parse([]string) error
- }
-
- unitTestErr := errors.New("unit test error")
- runCase := func(f FuncFlagSet, name string, args []string) (values []string, err error) {
- fn := func(s string) error {
- values = append(values, s)
- if s == "err" {
- return unitTestErr
- }
- return nil
- }
- f.Func(name, "Callback function", fn)
-
- err = f.Parse(args)
- return values, err
- }
-
- t.Run("regular parsing", func(t *testing.T) {
- flagName := "fnflag"
- args := []string{"--fnflag=xx", "--fnflag", "yy", "--fnflag=zz"}
-
- stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
- stdValues, err := runCase(stdFSet, flagName, args)
- if err != nil {
- t.Fatalf("std flag: expected no error, got %v", err)
- }
- expected := []string{"xx", "yy", "zz"}
- if !cmpLists(expected, stdValues) {
- t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
- }
-
- fset := NewFlagSet("pflag test", ContinueOnError)
- pflagValues, err := runCase(fset, flagName, args)
- if err != nil {
- t.Fatalf("pflag: expected no error, got %v", err)
- }
- if !cmpLists(stdValues, pflagValues) {
- t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
- }
- })
-
- t.Run("error triggered by callback", func(t *testing.T) {
- flagName := "fnflag"
- args := []string{"--fnflag", "before", "--fnflag", "err", "--fnflag", "after"}
-
- // test behavior of standard flag.Fset with an error triggered by the callback:
- // (note: as can be seen in 'runCase()', if the callback sees "err" as a value
- // for the flag, it will return an error)
- stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
- stdFSet.SetOutput(io.Discard) // suppress output
-
- // run test case with standard flag.Fset
- stdValues, err := runCase(stdFSet, flagName, args)
-
- // double check the standard behavior:
- // - .Parse() should return an error, which contains the error message
- if err == nil {
- t.Fatalf("std flag: expected an error triggered by callback, got no error instead")
- }
- if !strings.HasSuffix(err.Error(), unitTestErr.Error()) {
- t.Fatalf("std flag: expected unittest error, got unexpected error value: %T %v", err, err)
- }
- // - the function should have been called twice, with the first two values,
- // the final "=after" should not be recorded
- expected := []string{"before", "err"}
- if !cmpLists(expected, stdValues) {
- t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
- }
-
- // now run the test case on a pflag FlagSet:
- fset := NewFlagSet("pflag test", ContinueOnError)
- pflagValues, err := runCase(fset, flagName, args)
-
- // check that there is a similar error (note: pflag will _wrap_ the error, while the stdlib
- // currently keeps the original message but creates a flat errors.Error)
- if !errors.Is(err, unitTestErr) {
- t.Fatalf("pflag: got unexpected error value: %T %v", err, err)
- }
- // the callback should be called the same number of times, with the same values:
- if !cmpLists(stdValues, pflagValues) {
- t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
- }
- })
-}
-
func TestFuncUsage(t *testing.T) {
t.Run("regular func flag", func(t *testing.T) {
// regular func flag: