Skip to content

Commit 0be8b6c

Browse files
committed
merge master changes
1 parent 74b6ee1 commit 0be8b6c

8 files changed

+140
-23
lines changed

flaggy_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ func TestComplexNesting(t *testing.T) {
137137
t.Log("testE", testE)
138138
t.FailNow()
139139
}
140+
if subcommandName := flaggy.DefaultParser.TrailingSubcommand().Name; subcommandName != "scD" {
141+
t.Fatal("Used subcommand was incorrect:", subcommandName)
142+
}
140143

141144
}
142145

@@ -206,5 +209,8 @@ func TestParsePositionalsA(t *testing.T) {
206209
if parser.TrailingArguments[1] != "trailingB" {
207210
t.Fatal("Trailing argumentB was incorrect:", parser.TrailingArguments[1])
208211
}
212+
if subcommandName := parser.TrailingSubcommand().Name; subcommandName != "subcommand" {
213+
t.Fatal("Used subcommand was incorrect:", subcommandName)
214+
}
209215

210216
}

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
module github.com/integrii/flaggy
22

33
go 1.12
4+
5+
require github.com/google/go-cmp v0.5.6 // for tests only

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
2+
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
3+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
4+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

helpValues.go

-3
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ type HelpFlag struct {
5252
// parser. The parser is required in order to detect default flag settings
5353
// for help and version output.
5454
func (h *Help) ExtractValues(p *Parser, message string) {
55-
5655
// accept message string for output
5756
h.Message = message
5857

@@ -187,13 +186,11 @@ func (h *Help) ExtractValues(p *Parser, message string) {
187186
}
188187

189188
h.UsageString = usageString
190-
191189
}
192190

193191
// parseFlagsToHelpFlags parses the specified slice of flags into
194192
// help flags on the the calling help command
195193
func (h *Help) parseFlagsToHelpFlags(flags []*Flag, maxLength int) {
196-
197194
for _, f := range flags {
198195
if f.Hidden {
199196
continue

helpValues_blackbox_test.go

+79-3
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,59 @@
11
package flaggy_test
22

33
import (
4+
"os"
5+
"strings"
46
"testing"
57
"time"
68

9+
"github.com/google/go-cmp/cmp"
710
"github.com/integrii/flaggy"
811
)
912

1013
func TestMinimalHelpOutput(t *testing.T) {
1114
p := flaggy.NewParser("TestMinimalHelpOutput")
15+
16+
rd, wr, err := os.Pipe()
17+
if err != nil {
18+
t.Fatalf("pipe: error: %s", err)
19+
}
20+
savedStderr := os.Stderr
21+
os.Stderr = wr
22+
23+
defer func() {
24+
os.Stderr = savedStderr
25+
}()
26+
1227
p.ShowHelp()
28+
29+
buf := make([]byte, 1024)
30+
n, err := rd.Read(buf)
31+
if err != nil {
32+
t.Fatalf("read: error: %s", err)
33+
}
34+
got := strings.Split(string(buf[:n]), "\n")
35+
want := []string{
36+
"",
37+
"",
38+
" Flags: ",
39+
" --version Displays the program version string.",
40+
" -h --help Displays help with available flag, subcommand, and positional value parameters.",
41+
"",
42+
"",
43+
}
44+
45+
if diff := cmp.Diff(want, got); diff != "" {
46+
t.Errorf("help mismatch (-want +got):\n%s", diff)
47+
}
1348
}
1449

1550
func TestHelpWithMissingSCName(t *testing.T) {
1651
defer func() {
1752
r := recover()
18-
if r == nil {
19-
t.Fatal("Expected panic with subcommand avilability at position, but did not get one")
53+
gotMsg := r.(string)
54+
wantMsg := "Panic instead of exit with code: 2"
55+
if gotMsg != wantMsg {
56+
t.Fatalf("error: got: %s; want: %s", gotMsg, wantMsg)
2057
}
2158
}()
2259
flaggy.ResetParser()
@@ -62,6 +99,45 @@ func TestHelpOutput(t *testing.T) {
6299
p.Duration(&durationFlag, "d", "durationFlag", "This is a test duration flag that does some untimely stuff.")
63100
p.AdditionalHelpPrepend = "This is a prepend for help"
64101
p.AdditionalHelpAppend = "This is an append for help"
65-
p.ParseArgs([]string{"subcommandA", "subcommandB", "hiddenPositional1"})
102+
103+
rd, wr, err := os.Pipe()
104+
if err != nil {
105+
t.Fatalf("pipe: error: %s", err)
106+
}
107+
savedStderr := os.Stderr
108+
os.Stderr = wr
109+
110+
defer func() {
111+
os.Stderr = savedStderr
112+
}()
113+
114+
if err := p.ParseArgs([]string{"subcommandA", "subcommandB", "hiddenPositional1"}); err != nil {
115+
t.Fatalf("got: %s; want: no error", err)
116+
}
66117
p.ShowHelpWithMessage("This is a help message on exit")
118+
119+
buf := make([]byte, 1024)
120+
n, err := rd.Read(buf)
121+
if err != nil {
122+
t.Fatalf("read: error: %s", err)
123+
}
124+
got := strings.Split(string(buf[:n]), "\n")
125+
want := []string{
126+
"subcommandB - Subcommand B is a command that does other stuff",
127+
"",
128+
" Flags: ",
129+
" --version Displays the program version string.",
130+
" -h --help Displays help with available flag, subcommand, and positional value parameters.",
131+
" -s --stringFlag This is a test string flag that does some stringy string stuff. (default: defaultStringHere)",
132+
" -i --intFlg This is a test int flag that does some interesting int stuff. (default: 0)",
133+
" -b --boolFlag This is a test bool flag that does some booly bool stuff.",
134+
" -d --durationFlag This is a test duration flag that does some untimely stuff. (default: 0s)",
135+
"",
136+
"This is a help message on exit",
137+
"",
138+
}
139+
140+
if diff := cmp.Diff(want, got); diff != "" {
141+
t.Errorf("help mismatch (-want +got):\n%s", diff)
142+
}
67143
}

parser.go

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ type Parser struct {
2626
subcommandContext *Subcommand // points to the most specific subcommand being used
2727
}
2828

29+
// TrailingSubcommand returns the last and most specific subcommand invoked.
30+
func (p *Parser) TrailingSubcommand() *Subcommand {
31+
return p.subcommandContext
32+
}
33+
2934
// NewParser creates a new ArgumentParser ready to parse inputs
3035
func NewParser(name string) *Parser {
3136
// this can not be done inline because of struct embedding

subCommand.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -666,14 +666,14 @@ func (sc *Subcommand) AddPositionalValue(assignmentVar *string, name string, rel
666666
// ensure no other positionals are at this depth
667667
for _, other := range sc.PositionalFlags {
668668
if relativePosition == other.Position {
669-
log.Panicln("Unable to add positional value because one already exists at position: " + strconv.Itoa(relativePosition))
669+
log.Panicln("Unable to add positional value " + name + " because " + other.Name + " already exists at position: " + strconv.Itoa(relativePosition))
670670
}
671671
}
672672

673673
// ensure no subcommands at this depth
674674
for _, other := range sc.Subcommands {
675675
if relativePosition == other.Position {
676-
log.Panicln("Unable to add positional value a subcommand already exists at position: " + strconv.Itoa(relativePosition))
676+
log.Panicln("Unable to add positional value " + name + "because a subcommand, " + other.Name + ", already exists at position: " + strconv.Itoa(relativePosition))
677677
}
678678
}
679679

@@ -699,7 +699,9 @@ func (sc *Subcommand) SetValueForKey(key string, value string) (bool, error) {
699699
// debugPrint("Evaluating string flag", f.ShortName, "==", key, "||", f.LongName, "==", key)
700700
if f.ShortName == key || f.LongName == key {
701701
// debugPrint("Setting string value for", key, "to", value)
702-
f.identifyAndAssignValue(value)
702+
if err := f.identifyAndAssignValue(value); err != nil {
703+
return false, err
704+
}
703705
return true, nil
704706
}
705707
}

subcommand_test.go

+39-14
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ func TestSCNameExists(t *testing.T) {
2121
scB := flaggy.NewSubcommand("test")
2222
flaggy.AttachSubcommand(scA, 1)
2323
flaggy.AttachSubcommand(scB, 1)
24-
2524
}
2625

2726
func TestFlagExists(t *testing.T) {
@@ -37,7 +36,6 @@ func TestFlagExists(t *testing.T) {
3736
if e == false {
3837
t.Fatal("Flag does not exist on a subcommand that should")
3938
}
40-
4139
}
4240

4341
// TestExitOnUnknownFlag tests that when an unknown flag is supplied and the
@@ -157,7 +155,9 @@ func TestTypoSubcommand(t *testing.T) {
157155
newSCB := flaggy.NewSubcommand("TestTypoSubcommandB")
158156
p.AttachSubcommand(newSCA, 1)
159157
p.AttachSubcommand(newSCB, 1)
160-
p.ParseArgs(args)
158+
if err := p.ParseArgs(args); err != nil {
159+
t.Fatalf("got: %s; want: no error", err)
160+
}
161161
}
162162

163163
// TestIgnoreUnexpected tests what happens when an invalid subcommand is passed but should be ignored
@@ -167,7 +167,9 @@ func TestIgnoreUnexpected(t *testing.T) {
167167
args := []string{"unexpectedArg"}
168168
newSCA := flaggy.NewSubcommand("TestTypoSubcommandA")
169169
p.AttachSubcommand(newSCA, 1)
170-
p.ParseArgs(args)
170+
if err := p.ParseArgs(args); err != nil {
171+
t.Fatalf("got: %s; want: no error", err)
172+
}
171173
}
172174

173175
// TestSubcommandHelp tests displaying of help on unspecified commands
@@ -181,7 +183,9 @@ func TestSubcommandHelp(t *testing.T) {
181183
p := flaggy.NewParser("TestSubcommandHelp")
182184
p.ShowHelpOnUnexpected = true
183185
args := []string{"unexpectedArg"}
184-
p.ParseArgs(args)
186+
if err := p.ParseArgs(args); err != nil {
187+
t.Fatalf("got: %s; want: no error", err)
188+
}
185189
}
186190

187191
func TestHelpWithHFlagA(t *testing.T) {
@@ -194,7 +198,9 @@ func TestHelpWithHFlagA(t *testing.T) {
194198
p := flaggy.NewParser("TestHelpWithHFlag")
195199
p.ShowHelpWithHFlag = true
196200
args := []string{"-h"}
197-
p.ParseArgs(args)
201+
if err := p.ParseArgs(args); err != nil {
202+
t.Fatalf("got: %s; want: no error", err)
203+
}
198204
}
199205

200206
func TestHelpWithHFlagB(t *testing.T) {
@@ -207,7 +213,9 @@ func TestHelpWithHFlagB(t *testing.T) {
207213
p := flaggy.NewParser("TestHelpWithHFlag")
208214
p.ShowHelpWithHFlag = true
209215
args := []string{"--help"}
210-
p.ParseArgs(args)
216+
if err := p.ParseArgs(args); err != nil {
217+
t.Fatalf("got: %s; want: no error", err)
218+
}
211219
}
212220

213221
func TestVersionWithVFlagB(t *testing.T) {
@@ -221,7 +229,9 @@ func TestVersionWithVFlagB(t *testing.T) {
221229
p.ShowVersionWithVersionFlag = true
222230
p.Version = "TestVersionWithVFlagB 0.0.0a"
223231
args := []string{"--version"}
224-
p.ParseArgs(args)
232+
if err := p.ParseArgs(args); err != nil {
233+
t.Fatalf("got: %s; want: no error", err)
234+
}
225235
}
226236

227237
// TestSubcommandParse tests paring of a single subcommand
@@ -243,7 +253,9 @@ func TestSubcommandParse(t *testing.T) {
243253

244254
// override os args and parse them
245255
os.Args = []string{"binaryName", "testSubcommand", "testPositional"}
246-
p.Parse()
256+
if err := p.Parse(); err != nil {
257+
t.Fatalf("got: %s; want: no error", err)
258+
}
247259

248260
// ensure subcommand and positional used
249261
if !newSC.Used {
@@ -255,7 +267,6 @@ func TestSubcommandParse(t *testing.T) {
255267
}
256268

257269
func TestBadSubcommand(t *testing.T) {
258-
259270
// create the argument parser
260271
p := flaggy.NewParser("TestBadSubcommand")
261272

@@ -265,11 +276,12 @@ func TestBadSubcommand(t *testing.T) {
265276

266277
// test what happens if you add a bad subcommand
267278
os.Args = []string{"test"}
268-
p.Parse()
279+
if err := p.Parse(); err != nil {
280+
t.Fatalf("got: %s; want: no error", err)
281+
}
269282
}
270283

271284
func TestBadPositional(t *testing.T) {
272-
273285
// create the argument parser
274286
p := flaggy.NewParser("TestBadPositional")
275287

@@ -310,7 +322,6 @@ func debugOff() {
310322
// BenchmarkSubcommandParse benchmarks the creation and parsing of
311323
// a basic subcommand
312324
func BenchmarkSubcommandParse(b *testing.B) {
313-
314325
// catch errors that may occur
315326
defer func(b *testing.B) {
316327
err := recover()
@@ -342,7 +353,6 @@ func BenchmarkSubcommandParse(b *testing.B) {
342353
b.Fatal("Error parsing args: " + err.Error())
343354
}
344355
}
345-
346356
}
347357

348358
// TestSCInputParsing tests all flag types on subcommands
@@ -804,3 +814,18 @@ func TestNestedSCBoolFlag(t *testing.T) {
804814
t.Fatal("Error parsing args: " + err.Error())
805815
}
806816
}
817+
818+
func TestParseErrorsAreReportedRegression(t *testing.T) {
819+
defer func() {
820+
r := recover()
821+
if r == nil {
822+
t.Fatal("Expected crash on invalid syntax")
823+
}
824+
}()
825+
826+
flaggy.ResetParser()
827+
intFlag := 42
828+
flaggy.Int(&intFlag, "i", "int", "dummy")
829+
os.Args = []string{"prog", "--int", "abc"}
830+
flaggy.Parse()
831+
}

0 commit comments

Comments
 (0)