Skip to content

Commit 0491e57

Browse files
authored
Merge pull request #448 from thaJeztah/fix_go_version
remove uses of errors.Is, which requires go1.13, move go1.16/go1.21 tests to separate file
2 parents 72abab1 + 7e4dfb1 commit 0491e57

File tree

6 files changed

+215
-197
lines changed

6 files changed

+215
-197
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
strategy:
1414
fail-fast: false
1515
matrix:
16-
go: ["1.21", "1.22", "1.23"]
16+
go: ["1.12", "1.21", "1.22", "1.23"]
1717

1818
steps:
1919
- name: Checkout repository

bool_func_go1.21_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//go:build go1.21
2+
// +build go1.21
3+
4+
package pflag
5+
6+
import (
7+
"errors"
8+
"flag"
9+
"io"
10+
"strings"
11+
"testing"
12+
)
13+
14+
func TestBoolFuncCompat(t *testing.T) {
15+
// compare behavior with the stdlib 'flag' package
16+
type BoolFuncFlagSet interface {
17+
BoolFunc(name string, usage string, fn func(string) error)
18+
Parse([]string) error
19+
}
20+
21+
unitTestErr := errors.New("unit test error")
22+
runCase := func(f BoolFuncFlagSet, name string, args []string) (values []string, err error) {
23+
fn := func(s string) error {
24+
values = append(values, s)
25+
if s == "err" {
26+
return unitTestErr
27+
}
28+
return nil
29+
}
30+
f.BoolFunc(name, "Callback function", fn)
31+
32+
err = f.Parse(args)
33+
return values, err
34+
}
35+
36+
t.Run("regular parsing", func(t *testing.T) {
37+
flagName := "bflag"
38+
args := []string{"--bflag", "--bflag=false", "--bflag=1", "--bflag=bar", "--bflag="}
39+
40+
// It turns out that, even though the function is called "BoolFunc",
41+
// the standard flag package does not try to parse the value assigned to
42+
// that cli flag as a boolean. The string provided on the command line is
43+
// passed as is to the callback.
44+
// e.g: with "--bflag=not_a_bool" on the command line, the FlagSet does not
45+
// generate an error stating "invalid boolean value", and `fn` will be called
46+
// with "not_a_bool" as an argument.
47+
48+
stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
49+
stdValues, err := runCase(stdFSet, flagName, args)
50+
if err != nil {
51+
t.Fatalf("std flag: expected no error, got %v", err)
52+
}
53+
expected := []string{"true", "false", "1", "bar", ""}
54+
if !cmpLists(expected, stdValues) {
55+
t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
56+
}
57+
58+
fset := NewFlagSet("pflag test", ContinueOnError)
59+
pflagValues, err := runCase(fset, flagName, args)
60+
if err != nil {
61+
t.Fatalf("pflag: expected no error, got %v", err)
62+
}
63+
if !cmpLists(stdValues, pflagValues) {
64+
t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
65+
}
66+
})
67+
68+
t.Run("error triggered by callback", func(t *testing.T) {
69+
flagName := "bflag"
70+
args := []string{"--bflag", "--bflag=err", "--bflag=after"}
71+
72+
// test behavior of standard flag.Fset with an error triggered by the callback:
73+
// (note: as can be seen in 'runCase()', if the callback sees "err" as a value
74+
// for the bool flag, it will return an error)
75+
stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
76+
stdFSet.SetOutput(io.Discard) // suppress output
77+
78+
// run test case with standard flag.Fset
79+
stdValues, err := runCase(stdFSet, flagName, args)
80+
81+
// double check the standard behavior:
82+
// - .Parse() should return an error, which contains the error message
83+
if err == nil {
84+
t.Fatalf("std flag: expected an error triggered by callback, got no error instead")
85+
}
86+
if !strings.HasSuffix(err.Error(), unitTestErr.Error()) {
87+
t.Fatalf("std flag: expected unittest error, got unexpected error value: %T %v", err, err)
88+
}
89+
// - the function should have been called twice, with the first two values,
90+
// the final "=after" should not be recorded
91+
expected := []string{"true", "err"}
92+
if !cmpLists(expected, stdValues) {
93+
t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
94+
}
95+
96+
// now run the test case on a pflag FlagSet:
97+
fset := NewFlagSet("pflag test", ContinueOnError)
98+
pflagValues, err := runCase(fset, flagName, args)
99+
100+
// check that there is a similar error (note: pflag will _wrap_ the error, while the stdlib
101+
// currently keeps the original message but creates a flat errors.Error)
102+
if !errors.Is(err, unitTestErr) {
103+
t.Fatalf("pflag: got unexpected error value: %T %v", err, err)
104+
}
105+
// the callback should be called the same number of times, with the same values:
106+
if !cmpLists(stdValues, pflagValues) {
107+
t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
108+
}
109+
})
110+
}

bool_func_test.go

Lines changed: 0 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package pflag
22

33
import (
4-
"errors"
5-
"flag"
6-
"io"
74
"strings"
85
"testing"
96
)
@@ -48,104 +45,6 @@ func TestBoolFuncP(t *testing.T) {
4845
}
4946
}
5047

51-
func TestBoolFuncCompat(t *testing.T) {
52-
// compare behavior with the stdlib 'flag' package
53-
type BoolFuncFlagSet interface {
54-
BoolFunc(name string, usage string, fn func(string) error)
55-
Parse([]string) error
56-
}
57-
58-
unitTestErr := errors.New("unit test error")
59-
runCase := func(f BoolFuncFlagSet, name string, args []string) (values []string, err error) {
60-
fn := func(s string) error {
61-
values = append(values, s)
62-
if s == "err" {
63-
return unitTestErr
64-
}
65-
return nil
66-
}
67-
f.BoolFunc(name, "Callback function", fn)
68-
69-
err = f.Parse(args)
70-
return values, err
71-
}
72-
73-
t.Run("regular parsing", func(t *testing.T) {
74-
flagName := "bflag"
75-
args := []string{"--bflag", "--bflag=false", "--bflag=1", "--bflag=bar", "--bflag="}
76-
77-
// It turns out that, even though the function is called "BoolFunc",
78-
// the standard flag package does not try to parse the value assigned to
79-
// that cli flag as a boolean. The string provided on the command line is
80-
// passed as is to the callback.
81-
// e.g: with "--bflag=not_a_bool" on the command line, the FlagSet does not
82-
// generate an error stating "invalid boolean value", and `fn` will be called
83-
// with "not_a_bool" as an argument.
84-
85-
stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
86-
stdValues, err := runCase(stdFSet, flagName, args)
87-
if err != nil {
88-
t.Fatalf("std flag: expected no error, got %v", err)
89-
}
90-
expected := []string{"true", "false", "1", "bar", ""}
91-
if !cmpLists(expected, stdValues) {
92-
t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
93-
}
94-
95-
fset := NewFlagSet("pflag test", ContinueOnError)
96-
pflagValues, err := runCase(fset, flagName, args)
97-
if err != nil {
98-
t.Fatalf("pflag: expected no error, got %v", err)
99-
}
100-
if !cmpLists(stdValues, pflagValues) {
101-
t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
102-
}
103-
})
104-
105-
t.Run("error triggered by callback", func(t *testing.T) {
106-
flagName := "bflag"
107-
args := []string{"--bflag", "--bflag=err", "--bflag=after"}
108-
109-
// test behavior of standard flag.Fset with an error triggered by the callback:
110-
// (note: as can be seen in 'runCase()', if the callback sees "err" as a value
111-
// for the bool flag, it will return an error)
112-
stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
113-
stdFSet.SetOutput(io.Discard) // suppress output
114-
115-
// run test case with standard flag.Fset
116-
stdValues, err := runCase(stdFSet, flagName, args)
117-
118-
// double check the standard behavior:
119-
// - .Parse() should return an error, which contains the error message
120-
if err == nil {
121-
t.Fatalf("std flag: expected an error triggered by callback, got no error instead")
122-
}
123-
if !strings.HasSuffix(err.Error(), unitTestErr.Error()) {
124-
t.Fatalf("std flag: expected unittest error, got unexpected error value: %T %v", err, err)
125-
}
126-
// - the function should have been called twice, with the first two values,
127-
// the final "=after" should not be recorded
128-
expected := []string{"true", "err"}
129-
if !cmpLists(expected, stdValues) {
130-
t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
131-
}
132-
133-
// now run the test case on a pflag FlagSet:
134-
fset := NewFlagSet("pflag test", ContinueOnError)
135-
pflagValues, err := runCase(fset, flagName, args)
136-
137-
// check that there is a similar error (note: pflag will _wrap_ the error, while the stdlib
138-
// currently keeps the original message but creates a flat errors.Error)
139-
if !errors.Is(err, unitTestErr) {
140-
t.Fatalf("pflag: got unexpected error value: %T %v", err, err)
141-
}
142-
// the callback should be called the same number of times, with the same values:
143-
if !cmpLists(stdValues, pflagValues) {
144-
t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
145-
}
146-
})
147-
}
148-
14948
func TestBoolFuncUsage(t *testing.T) {
15049
t.Run("regular func flag", func(t *testing.T) {
15150
// regular boolfunc flag:

flag.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,7 +1187,7 @@ func (f *FlagSet) Parse(arguments []string) error {
11871187
case ContinueOnError:
11881188
return err
11891189
case ExitOnError:
1190-
if errors.Is(err, ErrHelp) {
1190+
if err == ErrHelp {
11911191
os.Exit(0)
11921192
}
11931193
fmt.Fprintln(f.Output(), err)
@@ -1216,7 +1216,7 @@ func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string)
12161216
case ContinueOnError:
12171217
return err
12181218
case ExitOnError:
1219-
if errors.Is(err, ErrHelp) {
1219+
if err == ErrHelp {
12201220
os.Exit(0)
12211221
}
12221222
fmt.Fprintln(f.Output(), err)

func_go1.21_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//go:build go1.21
2+
// +build go1.21
3+
4+
package pflag
5+
6+
import (
7+
"errors"
8+
"flag"
9+
"io"
10+
"strings"
11+
"testing"
12+
)
13+
14+
func TestFuncCompat(t *testing.T) {
15+
// compare behavior with the stdlib 'flag' package
16+
type FuncFlagSet interface {
17+
Func(name string, usage string, fn func(string) error)
18+
Parse([]string) error
19+
}
20+
21+
unitTestErr := errors.New("unit test error")
22+
runCase := func(f FuncFlagSet, name string, args []string) (values []string, err error) {
23+
fn := func(s string) error {
24+
values = append(values, s)
25+
if s == "err" {
26+
return unitTestErr
27+
}
28+
return nil
29+
}
30+
f.Func(name, "Callback function", fn)
31+
32+
err = f.Parse(args)
33+
return values, err
34+
}
35+
36+
t.Run("regular parsing", func(t *testing.T) {
37+
flagName := "fnflag"
38+
args := []string{"--fnflag=xx", "--fnflag", "yy", "--fnflag=zz"}
39+
40+
stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
41+
stdValues, err := runCase(stdFSet, flagName, args)
42+
if err != nil {
43+
t.Fatalf("std flag: expected no error, got %v", err)
44+
}
45+
expected := []string{"xx", "yy", "zz"}
46+
if !cmpLists(expected, stdValues) {
47+
t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
48+
}
49+
50+
fset := NewFlagSet("pflag test", ContinueOnError)
51+
pflagValues, err := runCase(fset, flagName, args)
52+
if err != nil {
53+
t.Fatalf("pflag: expected no error, got %v", err)
54+
}
55+
if !cmpLists(stdValues, pflagValues) {
56+
t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
57+
}
58+
})
59+
60+
t.Run("error triggered by callback", func(t *testing.T) {
61+
flagName := "fnflag"
62+
args := []string{"--fnflag", "before", "--fnflag", "err", "--fnflag", "after"}
63+
64+
// test behavior of standard flag.Fset with an error triggered by the callback:
65+
// (note: as can be seen in 'runCase()', if the callback sees "err" as a value
66+
// for the flag, it will return an error)
67+
stdFSet := flag.NewFlagSet("std test", flag.ContinueOnError)
68+
stdFSet.SetOutput(io.Discard) // suppress output
69+
70+
// run test case with standard flag.Fset
71+
stdValues, err := runCase(stdFSet, flagName, args)
72+
73+
// double check the standard behavior:
74+
// - .Parse() should return an error, which contains the error message
75+
if err == nil {
76+
t.Fatalf("std flag: expected an error triggered by callback, got no error instead")
77+
}
78+
if !strings.HasSuffix(err.Error(), unitTestErr.Error()) {
79+
t.Fatalf("std flag: expected unittest error, got unexpected error value: %T %v", err, err)
80+
}
81+
// - the function should have been called twice, with the first two values,
82+
// the final "=after" should not be recorded
83+
expected := []string{"before", "err"}
84+
if !cmpLists(expected, stdValues) {
85+
t.Fatalf("std flag: expected %v, got %v", expected, stdValues)
86+
}
87+
88+
// now run the test case on a pflag FlagSet:
89+
fset := NewFlagSet("pflag test", ContinueOnError)
90+
pflagValues, err := runCase(fset, flagName, args)
91+
92+
// check that there is a similar error (note: pflag will _wrap_ the error, while the stdlib
93+
// currently keeps the original message but creates a flat errors.Error)
94+
if !errors.Is(err, unitTestErr) {
95+
t.Fatalf("pflag: got unexpected error value: %T %v", err, err)
96+
}
97+
// the callback should be called the same number of times, with the same values:
98+
if !cmpLists(stdValues, pflagValues) {
99+
t.Fatalf("pflag: expected %v, got %v", stdValues, pflagValues)
100+
}
101+
})
102+
}

0 commit comments

Comments
 (0)