1
2
3
4
5 package pflag
6
7 import (
8 "bytes"
9 "fmt"
10 "io"
11 "io/ioutil"
12 "net"
13 "os"
14 "reflect"
15 "sort"
16 "strconv"
17 "strings"
18 "testing"
19 "time"
20 )
21
22 var (
23 testBool = Bool("test_bool", false, "bool value")
24 testInt = Int("test_int", 0, "int value")
25 testInt64 = Int64("test_int64", 0, "int64 value")
26 testUint = Uint("test_uint", 0, "uint value")
27 testUint64 = Uint64("test_uint64", 0, "uint64 value")
28 testString = String("test_string", "0", "string value")
29 testFloat = Float64("test_float64", 0, "float64 value")
30 testDuration = Duration("test_duration", 0, "time.Duration value")
31 testOptionalInt = Int("test_optional_int", 0, "optional int value")
32 normalizeFlagNameInvocations = 0
33 )
34
35 func boolString(s string) string {
36 if s == "0" {
37 return "false"
38 }
39 return "true"
40 }
41
42 func TestEverything(t *testing.T) {
43 m := make(map[string]*Flag)
44 desired := "0"
45 visitor := func(f *Flag) {
46 if len(f.Name) > 5 && f.Name[0:5] == "test_" {
47 m[f.Name] = f
48 ok := false
49 switch {
50 case f.Value.String() == desired:
51 ok = true
52 case f.Name == "test_bool" && f.Value.String() == boolString(desired):
53 ok = true
54 case f.Name == "test_duration" && f.Value.String() == desired+"s":
55 ok = true
56 }
57 if !ok {
58 t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
59 }
60 }
61 }
62 VisitAll(visitor)
63 if len(m) != 9 {
64 t.Error("VisitAll misses some flags")
65 for k, v := range m {
66 t.Log(k, *v)
67 }
68 }
69 m = make(map[string]*Flag)
70 Visit(visitor)
71 if len(m) != 0 {
72 t.Errorf("Visit sees unset flags")
73 for k, v := range m {
74 t.Log(k, *v)
75 }
76 }
77
78 Set("test_bool", "true")
79 Set("test_int", "1")
80 Set("test_int64", "1")
81 Set("test_uint", "1")
82 Set("test_uint64", "1")
83 Set("test_string", "1")
84 Set("test_float64", "1")
85 Set("test_duration", "1s")
86 Set("test_optional_int", "1")
87 desired = "1"
88 Visit(visitor)
89 if len(m) != 9 {
90 t.Error("Visit fails after set")
91 for k, v := range m {
92 t.Log(k, *v)
93 }
94 }
95
96 var flagNames []string
97 Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) })
98 if !sort.StringsAreSorted(flagNames) {
99 t.Errorf("flag names not sorted: %v", flagNames)
100 }
101 }
102
103 func TestUsage(t *testing.T) {
104 called := false
105 ResetForTesting(func() { called = true })
106 if GetCommandLine().Parse([]string{"--x"}) == nil {
107 t.Error("parse did not fail for unknown flag")
108 }
109 if called {
110 t.Error("did call Usage while using ContinueOnError")
111 }
112 }
113
114 func TestAddFlagSet(t *testing.T) {
115 oldSet := NewFlagSet("old", ContinueOnError)
116 newSet := NewFlagSet("new", ContinueOnError)
117
118 oldSet.String("flag1", "flag1", "flag1")
119 oldSet.String("flag2", "flag2", "flag2")
120
121 newSet.String("flag2", "flag2", "flag2")
122 newSet.String("flag3", "flag3", "flag3")
123
124 oldSet.AddFlagSet(newSet)
125
126 if len(oldSet.formal) != 3 {
127 t.Errorf("Unexpected result adding a FlagSet to a FlagSet %v", oldSet)
128 }
129 }
130
131 func TestAnnotation(t *testing.T) {
132 f := NewFlagSet("shorthand", ContinueOnError)
133
134 if err := f.SetAnnotation("missing-flag", "key", nil); err == nil {
135 t.Errorf("Expected error setting annotation on non-existent flag")
136 }
137
138 f.StringP("stringa", "a", "", "string value")
139 if err := f.SetAnnotation("stringa", "key", nil); err != nil {
140 t.Errorf("Unexpected error setting new nil annotation: %v", err)
141 }
142 if annotation := f.Lookup("stringa").Annotations["key"]; annotation != nil {
143 t.Errorf("Unexpected annotation: %v", annotation)
144 }
145
146 f.StringP("stringb", "b", "", "string2 value")
147 if err := f.SetAnnotation("stringb", "key", []string{"value1"}); err != nil {
148 t.Errorf("Unexpected error setting new annotation: %v", err)
149 }
150 if annotation := f.Lookup("stringb").Annotations["key"]; !reflect.DeepEqual(annotation, []string{"value1"}) {
151 t.Errorf("Unexpected annotation: %v", annotation)
152 }
153
154 if err := f.SetAnnotation("stringb", "key", []string{"value2"}); err != nil {
155 t.Errorf("Unexpected error updating annotation: %v", err)
156 }
157 if annotation := f.Lookup("stringb").Annotations["key"]; !reflect.DeepEqual(annotation, []string{"value2"}) {
158 t.Errorf("Unexpected annotation: %v", annotation)
159 }
160 }
161
162 func testParse(f *FlagSet, t *testing.T) {
163 if f.Parsed() {
164 t.Error("f.Parse() = true before Parse")
165 }
166 boolFlag := f.Bool("bool", false, "bool value")
167 bool2Flag := f.Bool("bool2", false, "bool2 value")
168 bool3Flag := f.Bool("bool3", false, "bool3 value")
169 intFlag := f.Int("int", 0, "int value")
170 int8Flag := f.Int8("int8", 0, "int value")
171 int16Flag := f.Int16("int16", 0, "int value")
172 int32Flag := f.Int32("int32", 0, "int value")
173 int64Flag := f.Int64("int64", 0, "int64 value")
174 uintFlag := f.Uint("uint", 0, "uint value")
175 uint8Flag := f.Uint8("uint8", 0, "uint value")
176 uint16Flag := f.Uint16("uint16", 0, "uint value")
177 uint32Flag := f.Uint32("uint32", 0, "uint value")
178 uint64Flag := f.Uint64("uint64", 0, "uint64 value")
179 stringFlag := f.String("string", "0", "string value")
180 float32Flag := f.Float32("float32", 0, "float32 value")
181 float64Flag := f.Float64("float64", 0, "float64 value")
182 ipFlag := f.IP("ip", net.ParseIP("127.0.0.1"), "ip value")
183 maskFlag := f.IPMask("mask", ParseIPv4Mask("0.0.0.0"), "mask value")
184 durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
185 optionalIntNoValueFlag := f.Int("optional-int-no-value", 0, "int value")
186 f.Lookup("optional-int-no-value").NoOptDefVal = "9"
187 optionalIntWithValueFlag := f.Int("optional-int-with-value", 0, "int value")
188 f.Lookup("optional-int-no-value").NoOptDefVal = "9"
189 extra := "one-extra-argument"
190 args := []string{
191 "--bool",
192 "--bool2=true",
193 "--bool3=false",
194 "--int=22",
195 "--int8=-8",
196 "--int16=-16",
197 "--int32=-32",
198 "--int64=0x23",
199 "--uint", "24",
200 "--uint8=8",
201 "--uint16=16",
202 "--uint32=32",
203 "--uint64=25",
204 "--string=hello",
205 "--float32=-172e12",
206 "--float64=2718e28",
207 "--ip=10.11.12.13",
208 "--mask=255.255.255.0",
209 "--duration=2m",
210 "--optional-int-no-value",
211 "--optional-int-with-value=42",
212 extra,
213 }
214 if err := f.Parse(args); err != nil {
215 t.Fatal(err)
216 }
217 if !f.Parsed() {
218 t.Error("f.Parse() = false after Parse")
219 }
220 if *boolFlag != true {
221 t.Error("bool flag should be true, is ", *boolFlag)
222 }
223 if v, err := f.GetBool("bool"); err != nil || v != *boolFlag {
224 t.Error("GetBool does not work.")
225 }
226 if *bool2Flag != true {
227 t.Error("bool2 flag should be true, is ", *bool2Flag)
228 }
229 if *bool3Flag != false {
230 t.Error("bool3 flag should be false, is ", *bool2Flag)
231 }
232 if *intFlag != 22 {
233 t.Error("int flag should be 22, is ", *intFlag)
234 }
235 if v, err := f.GetInt("int"); err != nil || v != *intFlag {
236 t.Error("GetInt does not work.")
237 }
238 if *int8Flag != -8 {
239 t.Error("int8 flag should be 0x23, is ", *int8Flag)
240 }
241 if *int16Flag != -16 {
242 t.Error("int16 flag should be -16, is ", *int16Flag)
243 }
244 if v, err := f.GetInt8("int8"); err != nil || v != *int8Flag {
245 t.Error("GetInt8 does not work.")
246 }
247 if v, err := f.GetInt16("int16"); err != nil || v != *int16Flag {
248 t.Error("GetInt16 does not work.")
249 }
250 if *int32Flag != -32 {
251 t.Error("int32 flag should be 0x23, is ", *int32Flag)
252 }
253 if v, err := f.GetInt32("int32"); err != nil || v != *int32Flag {
254 t.Error("GetInt32 does not work.")
255 }
256 if *int64Flag != 0x23 {
257 t.Error("int64 flag should be 0x23, is ", *int64Flag)
258 }
259 if v, err := f.GetInt64("int64"); err != nil || v != *int64Flag {
260 t.Error("GetInt64 does not work.")
261 }
262 if *uintFlag != 24 {
263 t.Error("uint flag should be 24, is ", *uintFlag)
264 }
265 if v, err := f.GetUint("uint"); err != nil || v != *uintFlag {
266 t.Error("GetUint does not work.")
267 }
268 if *uint8Flag != 8 {
269 t.Error("uint8 flag should be 8, is ", *uint8Flag)
270 }
271 if v, err := f.GetUint8("uint8"); err != nil || v != *uint8Flag {
272 t.Error("GetUint8 does not work.")
273 }
274 if *uint16Flag != 16 {
275 t.Error("uint16 flag should be 16, is ", *uint16Flag)
276 }
277 if v, err := f.GetUint16("uint16"); err != nil || v != *uint16Flag {
278 t.Error("GetUint16 does not work.")
279 }
280 if *uint32Flag != 32 {
281 t.Error("uint32 flag should be 32, is ", *uint32Flag)
282 }
283 if v, err := f.GetUint32("uint32"); err != nil || v != *uint32Flag {
284 t.Error("GetUint32 does not work.")
285 }
286 if *uint64Flag != 25 {
287 t.Error("uint64 flag should be 25, is ", *uint64Flag)
288 }
289 if v, err := f.GetUint64("uint64"); err != nil || v != *uint64Flag {
290 t.Error("GetUint64 does not work.")
291 }
292 if *stringFlag != "hello" {
293 t.Error("string flag should be `hello`, is ", *stringFlag)
294 }
295 if v, err := f.GetString("string"); err != nil || v != *stringFlag {
296 t.Error("GetString does not work.")
297 }
298 if *float32Flag != -172e12 {
299 t.Error("float32 flag should be -172e12, is ", *float32Flag)
300 }
301 if v, err := f.GetFloat32("float32"); err != nil || v != *float32Flag {
302 t.Errorf("GetFloat32 returned %v but float32Flag was %v", v, *float32Flag)
303 }
304 if *float64Flag != 2718e28 {
305 t.Error("float64 flag should be 2718e28, is ", *float64Flag)
306 }
307 if v, err := f.GetFloat64("float64"); err != nil || v != *float64Flag {
308 t.Errorf("GetFloat64 returned %v but float64Flag was %v", v, *float64Flag)
309 }
310 if !(*ipFlag).Equal(net.ParseIP("10.11.12.13")) {
311 t.Error("ip flag should be 10.11.12.13, is ", *ipFlag)
312 }
313 if v, err := f.GetIP("ip"); err != nil || !v.Equal(*ipFlag) {
314 t.Errorf("GetIP returned %v but ipFlag was %v", v, *ipFlag)
315 }
316 if (*maskFlag).String() != ParseIPv4Mask("255.255.255.0").String() {
317 t.Error("mask flag should be 255.255.255.0, is ", (*maskFlag).String())
318 }
319 if v, err := f.GetIPv4Mask("mask"); err != nil || v.String() != (*maskFlag).String() {
320 t.Errorf("GetIP returned %v maskFlag was %v error was %v", v, *maskFlag, err)
321 }
322 if *durationFlag != 2*time.Minute {
323 t.Error("duration flag should be 2m, is ", *durationFlag)
324 }
325 if v, err := f.GetDuration("duration"); err != nil || v != *durationFlag {
326 t.Error("GetDuration does not work.")
327 }
328 if _, err := f.GetInt("duration"); err == nil {
329 t.Error("GetInt parsed a time.Duration?!?!")
330 }
331 if *optionalIntNoValueFlag != 9 {
332 t.Error("optional int flag should be the default value, is ", *optionalIntNoValueFlag)
333 }
334 if *optionalIntWithValueFlag != 42 {
335 t.Error("optional int flag should be 42, is ", *optionalIntWithValueFlag)
336 }
337 if len(f.Args()) != 1 {
338 t.Error("expected one argument, got", len(f.Args()))
339 } else if f.Args()[0] != extra {
340 t.Errorf("expected argument %q got %q", extra, f.Args()[0])
341 }
342 }
343
344 func testParseAll(f *FlagSet, t *testing.T) {
345 if f.Parsed() {
346 t.Error("f.Parse() = true before Parse")
347 }
348 f.BoolP("boola", "a", false, "bool value")
349 f.BoolP("boolb", "b", false, "bool2 value")
350 f.BoolP("boolc", "c", false, "bool3 value")
351 f.BoolP("boold", "d", false, "bool4 value")
352 f.StringP("stringa", "s", "0", "string value")
353 f.StringP("stringz", "z", "0", "string value")
354 f.StringP("stringx", "x", "0", "string value")
355 f.StringP("stringy", "y", "0", "string value")
356 f.Lookup("stringx").NoOptDefVal = "1"
357 args := []string{
358 "-ab",
359 "-cs=xx",
360 "--stringz=something",
361 "-d=true",
362 "-x",
363 "-y",
364 "ee",
365 }
366 want := []string{
367 "boola", "true",
368 "boolb", "true",
369 "boolc", "true",
370 "stringa", "xx",
371 "stringz", "something",
372 "boold", "true",
373 "stringx", "1",
374 "stringy", "ee",
375 }
376 got := []string{}
377 store := func(flag *Flag, value string) error {
378 got = append(got, flag.Name)
379 if len(value) > 0 {
380 got = append(got, value)
381 }
382 return nil
383 }
384 if err := f.ParseAll(args, store); err != nil {
385 t.Errorf("expected no error, got %s", err)
386 }
387 if !f.Parsed() {
388 t.Errorf("f.Parse() = false after Parse")
389 }
390 if !reflect.DeepEqual(got, want) {
391 t.Errorf("f.ParseAll() fail to restore the args")
392 t.Errorf("Got: %v", got)
393 t.Errorf("Want: %v", want)
394 }
395 }
396
397 func testParseWithUnknownFlags(f *FlagSet, t *testing.T) {
398 if f.Parsed() {
399 t.Error("f.Parse() = true before Parse")
400 }
401 f.ParseErrorsWhitelist.UnknownFlags = true
402
403 f.BoolP("boola", "a", false, "bool value")
404 f.BoolP("boolb", "b", false, "bool2 value")
405 f.BoolP("boolc", "c", false, "bool3 value")
406 f.BoolP("boold", "d", false, "bool4 value")
407 f.BoolP("boole", "e", false, "bool4 value")
408 f.StringP("stringa", "s", "0", "string value")
409 f.StringP("stringz", "z", "0", "string value")
410 f.StringP("stringx", "x", "0", "string value")
411 f.StringP("stringy", "y", "0", "string value")
412 f.StringP("stringo", "o", "0", "string value")
413 f.Lookup("stringx").NoOptDefVal = "1"
414 args := []string{
415 "-ab",
416 "-cs=xx",
417 "--stringz=something",
418 "--unknown1",
419 "unknown1Value",
420 "-d=true",
421 "-x",
422 "--unknown2=unknown2Value",
423 "-u=unknown3Value",
424 "-p",
425 "unknown4Value",
426 "-q",
427 "-y",
428 "ee",
429 "--unknown7=unknown7value",
430 "--stringo=ovalue",
431 "--unknown8=unknown8value",
432 "--boole",
433 "--unknown6",
434 "",
435 "-uuuuu",
436 "",
437 "--unknown10",
438 "--unknown11",
439 }
440 want := []string{
441 "boola", "true",
442 "boolb", "true",
443 "boolc", "true",
444 "stringa", "xx",
445 "stringz", "something",
446 "boold", "true",
447 "stringx", "1",
448 "stringy", "ee",
449 "stringo", "ovalue",
450 "boole", "true",
451 }
452 got := []string{}
453 store := func(flag *Flag, value string) error {
454 got = append(got, flag.Name)
455 if len(value) > 0 {
456 got = append(got, value)
457 }
458 return nil
459 }
460 if err := f.ParseAll(args, store); err != nil {
461 t.Errorf("expected no error, got %s", err)
462 }
463 if !f.Parsed() {
464 t.Errorf("f.Parse() = false after Parse")
465 }
466 if !reflect.DeepEqual(got, want) {
467 t.Errorf("f.ParseAll() fail to restore the args")
468 t.Errorf("Got: %v", got)
469 t.Errorf("Want: %v", want)
470 }
471 }
472
473 func TestShorthand(t *testing.T) {
474 f := NewFlagSet("shorthand", ContinueOnError)
475 if f.Parsed() {
476 t.Error("f.Parse() = true before Parse")
477 }
478 boolaFlag := f.BoolP("boola", "a", false, "bool value")
479 boolbFlag := f.BoolP("boolb", "b", false, "bool2 value")
480 boolcFlag := f.BoolP("boolc", "c", false, "bool3 value")
481 booldFlag := f.BoolP("boold", "d", false, "bool4 value")
482 stringaFlag := f.StringP("stringa", "s", "0", "string value")
483 stringzFlag := f.StringP("stringz", "z", "0", "string value")
484 extra := "interspersed-argument"
485 notaflag := "--i-look-like-a-flag"
486 args := []string{
487 "-ab",
488 extra,
489 "-cs",
490 "hello",
491 "-z=something",
492 "-d=true",
493 "--",
494 notaflag,
495 }
496 f.SetOutput(ioutil.Discard)
497 if err := f.Parse(args); err != nil {
498 t.Error("expected no error, got ", err)
499 }
500 if !f.Parsed() {
501 t.Error("f.Parse() = false after Parse")
502 }
503 if *boolaFlag != true {
504 t.Error("boola flag should be true, is ", *boolaFlag)
505 }
506 if *boolbFlag != true {
507 t.Error("boolb flag should be true, is ", *boolbFlag)
508 }
509 if *boolcFlag != true {
510 t.Error("boolc flag should be true, is ", *boolcFlag)
511 }
512 if *booldFlag != true {
513 t.Error("boold flag should be true, is ", *booldFlag)
514 }
515 if *stringaFlag != "hello" {
516 t.Error("stringa flag should be `hello`, is ", *stringaFlag)
517 }
518 if *stringzFlag != "something" {
519 t.Error("stringz flag should be `something`, is ", *stringzFlag)
520 }
521 if len(f.Args()) != 2 {
522 t.Error("expected one argument, got", len(f.Args()))
523 } else if f.Args()[0] != extra {
524 t.Errorf("expected argument %q got %q", extra, f.Args()[0])
525 } else if f.Args()[1] != notaflag {
526 t.Errorf("expected argument %q got %q", notaflag, f.Args()[1])
527 }
528 if f.ArgsLenAtDash() != 1 {
529 t.Errorf("expected argsLenAtDash %d got %d", f.ArgsLenAtDash(), 1)
530 }
531 }
532
533 func TestShorthandLookup(t *testing.T) {
534 f := NewFlagSet("shorthand", ContinueOnError)
535 if f.Parsed() {
536 t.Error("f.Parse() = true before Parse")
537 }
538 f.BoolP("boola", "a", false, "bool value")
539 f.BoolP("boolb", "b", false, "bool2 value")
540 args := []string{
541 "-ab",
542 }
543 f.SetOutput(ioutil.Discard)
544 if err := f.Parse(args); err != nil {
545 t.Error("expected no error, got ", err)
546 }
547 if !f.Parsed() {
548 t.Error("f.Parse() = false after Parse")
549 }
550 flag := f.ShorthandLookup("a")
551 if flag == nil {
552 t.Errorf("f.ShorthandLookup(\"a\") returned nil")
553 }
554 if flag.Name != "boola" {
555 t.Errorf("f.ShorthandLookup(\"a\") found %q instead of \"boola\"", flag.Name)
556 }
557 flag = f.ShorthandLookup("")
558 if flag != nil {
559 t.Errorf("f.ShorthandLookup(\"\") did not return nil")
560 }
561 defer func() {
562 recover()
563 }()
564 flag = f.ShorthandLookup("ab")
565
566 t.Errorf("f.ShorthandLookup(\"ab\") did not panic")
567 }
568
569 func TestParse(t *testing.T) {
570 ResetForTesting(func() { t.Error("bad parse") })
571 testParse(GetCommandLine(), t)
572 }
573
574 func TestParseAll(t *testing.T) {
575 ResetForTesting(func() { t.Error("bad parse") })
576 testParseAll(GetCommandLine(), t)
577 }
578
579 func TestIgnoreUnknownFlags(t *testing.T) {
580 ResetForTesting(func() { t.Error("bad parse") })
581 testParseWithUnknownFlags(GetCommandLine(), t)
582 }
583
584 func TestFlagSetParse(t *testing.T) {
585 testParse(NewFlagSet("test", ContinueOnError), t)
586 }
587
588 func TestChangedHelper(t *testing.T) {
589 f := NewFlagSet("changedtest", ContinueOnError)
590 f.Bool("changed", false, "changed bool")
591 f.Bool("settrue", true, "true to true")
592 f.Bool("setfalse", false, "false to false")
593 f.Bool("unchanged", false, "unchanged bool")
594
595 args := []string{"--changed", "--settrue", "--setfalse=false"}
596 if err := f.Parse(args); err != nil {
597 t.Error("f.Parse() = false after Parse")
598 }
599 if !f.Changed("changed") {
600 t.Errorf("--changed wasn't changed!")
601 }
602 if !f.Changed("settrue") {
603 t.Errorf("--settrue wasn't changed!")
604 }
605 if !f.Changed("setfalse") {
606 t.Errorf("--setfalse wasn't changed!")
607 }
608 if f.Changed("unchanged") {
609 t.Errorf("--unchanged was changed!")
610 }
611 if f.Changed("invalid") {
612 t.Errorf("--invalid was changed!")
613 }
614 if f.ArgsLenAtDash() != -1 {
615 t.Errorf("Expected argsLenAtDash: %d but got %d", -1, f.ArgsLenAtDash())
616 }
617 }
618
619 func replaceSeparators(name string, from []string, to string) string {
620 result := name
621 for _, sep := range from {
622 result = strings.Replace(result, sep, to, -1)
623 }
624
625 return result
626 }
627
628 func wordSepNormalizeFunc(f *FlagSet, name string) NormalizedName {
629 seps := []string{"-", "_"}
630 name = replaceSeparators(name, seps, ".")
631 normalizeFlagNameInvocations++
632
633 return NormalizedName(name)
634 }
635
636 func testWordSepNormalizedNames(args []string, t *testing.T) {
637 f := NewFlagSet("normalized", ContinueOnError)
638 if f.Parsed() {
639 t.Error("f.Parse() = true before Parse")
640 }
641 withDashFlag := f.Bool("with-dash-flag", false, "bool value")
642
643 f.SetNormalizeFunc(wordSepNormalizeFunc)
644 withUnderFlag := f.Bool("with_under_flag", false, "bool value")
645 withBothFlag := f.Bool("with-both_flag", false, "bool value")
646 if err := f.Parse(args); err != nil {
647 t.Fatal(err)
648 }
649 if !f.Parsed() {
650 t.Error("f.Parse() = false after Parse")
651 }
652 if *withDashFlag != true {
653 t.Error("withDashFlag flag should be true, is ", *withDashFlag)
654 }
655 if *withUnderFlag != true {
656 t.Error("withUnderFlag flag should be true, is ", *withUnderFlag)
657 }
658 if *withBothFlag != true {
659 t.Error("withBothFlag flag should be true, is ", *withBothFlag)
660 }
661 }
662
663 func TestWordSepNormalizedNames(t *testing.T) {
664 args := []string{
665 "--with-dash-flag",
666 "--with-under-flag",
667 "--with-both-flag",
668 }
669 testWordSepNormalizedNames(args, t)
670
671 args = []string{
672 "--with_dash_flag",
673 "--with_under_flag",
674 "--with_both_flag",
675 }
676 testWordSepNormalizedNames(args, t)
677
678 args = []string{
679 "--with-dash_flag",
680 "--with-under_flag",
681 "--with-both_flag",
682 }
683 testWordSepNormalizedNames(args, t)
684 }
685
686 func aliasAndWordSepFlagNames(f *FlagSet, name string) NormalizedName {
687 seps := []string{"-", "_"}
688
689 oldName := replaceSeparators("old-valid_flag", seps, ".")
690 newName := replaceSeparators("valid-flag", seps, ".")
691
692 name = replaceSeparators(name, seps, ".")
693 switch name {
694 case oldName:
695 name = newName
696 }
697
698 return NormalizedName(name)
699 }
700
701 func TestCustomNormalizedNames(t *testing.T) {
702 f := NewFlagSet("normalized", ContinueOnError)
703 if f.Parsed() {
704 t.Error("f.Parse() = true before Parse")
705 }
706
707 validFlag := f.Bool("valid-flag", false, "bool value")
708 f.SetNormalizeFunc(aliasAndWordSepFlagNames)
709 someOtherFlag := f.Bool("some-other-flag", false, "bool value")
710
711 args := []string{"--old_valid_flag", "--some-other_flag"}
712 if err := f.Parse(args); err != nil {
713 t.Fatal(err)
714 }
715
716 if *validFlag != true {
717 t.Errorf("validFlag is %v even though we set the alias --old_valid_falg", *validFlag)
718 }
719 if *someOtherFlag != true {
720 t.Error("someOtherFlag should be true, is ", *someOtherFlag)
721 }
722 }
723
724
725 func TestNormalizationFuncShouldChangeFlagName(t *testing.T) {
726
727 f := NewFlagSet("normalized", ContinueOnError)
728
729 f.Bool("valid_flag", false, "bool value")
730 if f.Lookup("valid_flag").Name != "valid_flag" {
731 t.Error("The new flag should have the name 'valid_flag' instead of ", f.Lookup("valid_flag").Name)
732 }
733
734 f.SetNormalizeFunc(wordSepNormalizeFunc)
735 if f.Lookup("valid_flag").Name != "valid.flag" {
736 t.Error("The new flag should have the name 'valid.flag' instead of ", f.Lookup("valid_flag").Name)
737 }
738
739
740 f = NewFlagSet("normalized", ContinueOnError)
741 f.SetNormalizeFunc(wordSepNormalizeFunc)
742
743 f.Bool("valid_flag", false, "bool value")
744 if f.Lookup("valid_flag").Name != "valid.flag" {
745 t.Error("The new flag should have the name 'valid.flag' instead of ", f.Lookup("valid_flag").Name)
746 }
747 }
748
749
750 func TestNormalizationSharedFlags(t *testing.T) {
751 f := NewFlagSet("set f", ContinueOnError)
752 g := NewFlagSet("set g", ContinueOnError)
753 nfunc := wordSepNormalizeFunc
754 testName := "valid_flag"
755 normName := nfunc(nil, testName)
756 if testName == string(normName) {
757 t.Error("TestNormalizationSharedFlags meaningless: the original and normalized flag names are identical:", testName)
758 }
759
760 f.Bool(testName, false, "bool value")
761 g.AddFlagSet(f)
762
763 f.SetNormalizeFunc(nfunc)
764 g.SetNormalizeFunc(nfunc)
765
766 if len(f.formal) != 1 {
767 t.Error("Normalizing flags should not result in duplications in the flag set:", f.formal)
768 }
769 if f.orderedFormal[0].Name != string(normName) {
770 t.Error("Flag name not normalized")
771 }
772 for k := range f.formal {
773 if k != "valid.flag" {
774 t.Errorf("The key in the flag map should have been normalized: wanted \"%s\", got \"%s\" instead", normName, k)
775 }
776 }
777
778 if !reflect.DeepEqual(f.formal, g.formal) || !reflect.DeepEqual(f.orderedFormal, g.orderedFormal) {
779 t.Error("Two flag sets sharing the same flags should stay consistent after being normalized. Original set:", f.formal, "Duplicate set:", g.formal)
780 }
781 }
782
783 func TestNormalizationSetFlags(t *testing.T) {
784 f := NewFlagSet("normalized", ContinueOnError)
785 nfunc := wordSepNormalizeFunc
786 testName := "valid_flag"
787 normName := nfunc(nil, testName)
788 if testName == string(normName) {
789 t.Error("TestNormalizationSetFlags meaningless: the original and normalized flag names are identical:", testName)
790 }
791
792 f.Bool(testName, false, "bool value")
793 f.Set(testName, "true")
794 f.SetNormalizeFunc(nfunc)
795
796 if len(f.formal) != 1 {
797 t.Error("Normalizing flags should not result in duplications in the flag set:", f.formal)
798 }
799 if f.orderedFormal[0].Name != string(normName) {
800 t.Error("Flag name not normalized")
801 }
802 for k := range f.formal {
803 if k != "valid.flag" {
804 t.Errorf("The key in the flag map should have been normalized: wanted \"%s\", got \"%s\" instead", normName, k)
805 }
806 }
807
808 if !reflect.DeepEqual(f.formal, f.actual) {
809 t.Error("The map of set flags should get normalized. Formal:", f.formal, "Actual:", f.actual)
810 }
811 }
812
813
814 type flagVar []string
815
816 func (f *flagVar) String() string {
817 return fmt.Sprint([]string(*f))
818 }
819
820 func (f *flagVar) Set(value string) error {
821 *f = append(*f, value)
822 return nil
823 }
824
825 func (f *flagVar) Type() string {
826 return "flagVar"
827 }
828
829 func TestUserDefined(t *testing.T) {
830 var flags FlagSet
831 flags.Init("test", ContinueOnError)
832 var v flagVar
833 flags.VarP(&v, "v", "v", "usage")
834 if err := flags.Parse([]string{"--v=1", "-v2", "-v", "3"}); err != nil {
835 t.Error(err)
836 }
837 if len(v) != 3 {
838 t.Fatal("expected 3 args; got ", len(v))
839 }
840 expect := "[1 2 3]"
841 if v.String() != expect {
842 t.Errorf("expected value %q got %q", expect, v.String())
843 }
844 }
845
846 func TestSetOutput(t *testing.T) {
847 var flags FlagSet
848 var buf bytes.Buffer
849 flags.SetOutput(&buf)
850 flags.Init("test", ContinueOnError)
851 flags.Parse([]string{"--unknown"})
852 if out := buf.String(); !strings.Contains(out, "--unknown") {
853 t.Logf("expected output mentioning unknown; got %q", out)
854 }
855 }
856
857
858
859 func TestChangingArgs(t *testing.T) {
860 ResetForTesting(func() { t.Fatal("bad parse") })
861 oldArgs := os.Args
862 defer func() { os.Args = oldArgs }()
863 os.Args = []string{"cmd", "--before", "subcmd"}
864 before := Bool("before", false, "")
865 if err := GetCommandLine().Parse(os.Args[1:]); err != nil {
866 t.Fatal(err)
867 }
868 cmd := Arg(0)
869 os.Args = []string{"subcmd", "--after", "args"}
870 after := Bool("after", false, "")
871 Parse()
872 args := Args()
873
874 if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
875 t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
876 }
877 }
878
879
880 func TestHelp(t *testing.T) {
881 var helpCalled = false
882 fs := NewFlagSet("help test", ContinueOnError)
883 fs.Usage = func() { helpCalled = true }
884 var flag bool
885 fs.BoolVar(&flag, "flag", false, "regular flag")
886
887 err := fs.Parse([]string{"--flag=true"})
888 if err != nil {
889 t.Fatal("expected no error; got ", err)
890 }
891 if !flag {
892 t.Error("flag was not set by --flag")
893 }
894 if helpCalled {
895 t.Error("help called for regular flag")
896 helpCalled = false
897 }
898
899 err = fs.Parse([]string{"--help"})
900 if err == nil {
901 t.Fatal("error expected")
902 }
903 if err != ErrHelp {
904 t.Fatal("expected ErrHelp; got ", err)
905 }
906 if !helpCalled {
907 t.Fatal("help was not called")
908 }
909
910 var help bool
911 fs.BoolVar(&help, "help", false, "help flag")
912 helpCalled = false
913 err = fs.Parse([]string{"--help"})
914 if err != nil {
915 t.Fatal("expected no error for defined --help; got ", err)
916 }
917 if helpCalled {
918 t.Fatal("help was called; should not have been for defined help flag")
919 }
920 }
921
922 func TestNoInterspersed(t *testing.T) {
923 f := NewFlagSet("test", ContinueOnError)
924 f.SetInterspersed(false)
925 f.Bool("true", true, "always true")
926 f.Bool("false", false, "always false")
927 err := f.Parse([]string{"--true", "break", "--false"})
928 if err != nil {
929 t.Fatal("expected no error; got ", err)
930 }
931 args := f.Args()
932 if len(args) != 2 || args[0] != "break" || args[1] != "--false" {
933 t.Fatal("expected interspersed options/non-options to fail")
934 }
935 }
936
937 func TestTermination(t *testing.T) {
938 f := NewFlagSet("termination", ContinueOnError)
939 boolFlag := f.BoolP("bool", "l", false, "bool value")
940 if f.Parsed() {
941 t.Error("f.Parse() = true before Parse")
942 }
943 arg1 := "ls"
944 arg2 := "-l"
945 args := []string{
946 "--",
947 arg1,
948 arg2,
949 }
950 f.SetOutput(ioutil.Discard)
951 if err := f.Parse(args); err != nil {
952 t.Fatal("expected no error; got ", err)
953 }
954 if !f.Parsed() {
955 t.Error("f.Parse() = false after Parse")
956 }
957 if *boolFlag {
958 t.Error("expected boolFlag=false, got true")
959 }
960 if len(f.Args()) != 2 {
961 t.Errorf("expected 2 arguments, got %d: %v", len(f.Args()), f.Args())
962 }
963 if f.Args()[0] != arg1 {
964 t.Errorf("expected argument %q got %q", arg1, f.Args()[0])
965 }
966 if f.Args()[1] != arg2 {
967 t.Errorf("expected argument %q got %q", arg2, f.Args()[1])
968 }
969 if f.ArgsLenAtDash() != 0 {
970 t.Errorf("expected argsLenAtDash %d got %d", 0, f.ArgsLenAtDash())
971 }
972 }
973
974 func getDeprecatedFlagSet() *FlagSet {
975 f := NewFlagSet("bob", ContinueOnError)
976 f.Bool("badflag", true, "always true")
977 f.MarkDeprecated("badflag", "use --good-flag instead")
978 return f
979 }
980 func TestDeprecatedFlagInDocs(t *testing.T) {
981 f := getDeprecatedFlagSet()
982
983 out := new(bytes.Buffer)
984 f.SetOutput(out)
985 f.PrintDefaults()
986
987 if strings.Contains(out.String(), "badflag") {
988 t.Errorf("found deprecated flag in usage!")
989 }
990 }
991
992 func TestUnHiddenDeprecatedFlagInDocs(t *testing.T) {
993 f := getDeprecatedFlagSet()
994 flg := f.Lookup("badflag")
995 if flg == nil {
996 t.Fatalf("Unable to lookup 'bob' in TestUnHiddenDeprecatedFlagInDocs")
997 }
998 flg.Hidden = false
999
1000 out := new(bytes.Buffer)
1001 f.SetOutput(out)
1002 f.PrintDefaults()
1003
1004 defaults := out.String()
1005 if !strings.Contains(defaults, "badflag") {
1006 t.Errorf("Did not find deprecated flag in usage!")
1007 }
1008 if !strings.Contains(defaults, "use --good-flag instead") {
1009 t.Errorf("Did not find 'use --good-flag instead' in defaults")
1010 }
1011 }
1012
1013 func TestDeprecatedFlagShorthandInDocs(t *testing.T) {
1014 f := NewFlagSet("bob", ContinueOnError)
1015 name := "noshorthandflag"
1016 f.BoolP(name, "n", true, "always true")
1017 f.MarkShorthandDeprecated("noshorthandflag", fmt.Sprintf("use --%s instead", name))
1018
1019 out := new(bytes.Buffer)
1020 f.SetOutput(out)
1021 f.PrintDefaults()
1022
1023 if strings.Contains(out.String(), "-n,") {
1024 t.Errorf("found deprecated flag shorthand in usage!")
1025 }
1026 }
1027
1028 func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) {
1029 oldStderr := os.Stderr
1030 r, w, _ := os.Pipe()
1031 os.Stderr = w
1032
1033 err := f.Parse(args)
1034
1035 outC := make(chan string)
1036
1037 go func() {
1038 var buf bytes.Buffer
1039 io.Copy(&buf, r)
1040 outC <- buf.String()
1041 }()
1042
1043 w.Close()
1044 os.Stderr = oldStderr
1045 out := <-outC
1046
1047 return out, err
1048 }
1049
1050 func TestDeprecatedFlagUsage(t *testing.T) {
1051 f := NewFlagSet("bob", ContinueOnError)
1052 f.Bool("badflag", true, "always true")
1053 usageMsg := "use --good-flag instead"
1054 f.MarkDeprecated("badflag", usageMsg)
1055
1056 args := []string{"--badflag"}
1057 out, err := parseReturnStderr(t, f, args)
1058 if err != nil {
1059 t.Fatal("expected no error; got ", err)
1060 }
1061
1062 if !strings.Contains(out, usageMsg) {
1063 t.Errorf("usageMsg not printed when using a deprecated flag!")
1064 }
1065 }
1066
1067 func TestDeprecatedFlagShorthandUsage(t *testing.T) {
1068 f := NewFlagSet("bob", ContinueOnError)
1069 name := "noshorthandflag"
1070 f.BoolP(name, "n", true, "always true")
1071 usageMsg := fmt.Sprintf("use --%s instead", name)
1072 f.MarkShorthandDeprecated(name, usageMsg)
1073
1074 args := []string{"-n"}
1075 out, err := parseReturnStderr(t, f, args)
1076 if err != nil {
1077 t.Fatal("expected no error; got ", err)
1078 }
1079
1080 if !strings.Contains(out, usageMsg) {
1081 t.Errorf("usageMsg not printed when using a deprecated flag!")
1082 }
1083 }
1084
1085 func TestDeprecatedFlagUsageNormalized(t *testing.T) {
1086 f := NewFlagSet("bob", ContinueOnError)
1087 f.Bool("bad-double_flag", true, "always true")
1088 f.SetNormalizeFunc(wordSepNormalizeFunc)
1089 usageMsg := "use --good-flag instead"
1090 f.MarkDeprecated("bad_double-flag", usageMsg)
1091
1092 args := []string{"--bad_double_flag"}
1093 out, err := parseReturnStderr(t, f, args)
1094 if err != nil {
1095 t.Fatal("expected no error; got ", err)
1096 }
1097
1098 if !strings.Contains(out, usageMsg) {
1099 t.Errorf("usageMsg not printed when using a deprecated flag!")
1100 }
1101 }
1102
1103
1104 func TestMultipleNormalizeFlagNameInvocations(t *testing.T) {
1105 normalizeFlagNameInvocations = 0
1106
1107 f := NewFlagSet("normalized", ContinueOnError)
1108 f.SetNormalizeFunc(wordSepNormalizeFunc)
1109 f.Bool("with_under_flag", false, "bool value")
1110
1111 if normalizeFlagNameInvocations != 1 {
1112 t.Fatal("Expected normalizeFlagNameInvocations to be 1; got ", normalizeFlagNameInvocations)
1113 }
1114 }
1115
1116
1117 func TestHiddenFlagInUsage(t *testing.T) {
1118 f := NewFlagSet("bob", ContinueOnError)
1119 f.Bool("secretFlag", true, "shhh")
1120 f.MarkHidden("secretFlag")
1121
1122 out := new(bytes.Buffer)
1123 f.SetOutput(out)
1124 f.PrintDefaults()
1125
1126 if strings.Contains(out.String(), "secretFlag") {
1127 t.Errorf("found hidden flag in usage!")
1128 }
1129 }
1130
1131
1132 func TestHiddenFlagUsage(t *testing.T) {
1133 f := NewFlagSet("bob", ContinueOnError)
1134 f.Bool("secretFlag", true, "shhh")
1135 f.MarkHidden("secretFlag")
1136
1137 args := []string{"--secretFlag"}
1138 out, err := parseReturnStderr(t, f, args)
1139 if err != nil {
1140 t.Fatal("expected no error; got ", err)
1141 }
1142
1143 if strings.Contains(out, "shhh") {
1144 t.Errorf("usage message printed when using a hidden flag!")
1145 }
1146 }
1147
1148 const defaultOutput = ` --A for bootstrapping, allow 'any' type
1149 --Alongflagname disable bounds checking
1150 -C, --CCC a boolean defaulting to true (default true)
1151 --D path set relative path for local imports
1152 -E, --EEE num[=1234] a num with NoOptDefVal (default 4321)
1153 --F number a non-zero number (default 2.7)
1154 --G float a float that defaults to zero
1155 --IP ip IP address with no default
1156 --IPMask ipMask Netmask address with no default
1157 --IPNet ipNet IP network with no default
1158 --Ints ints int slice with zero default
1159 --N int a non-zero int (default 27)
1160 --ND1 string[="bar"] a string with NoOptDefVal (default "foo")
1161 --ND2 num[=4321] a num with NoOptDefVal (default 1234)
1162 --StringArray stringArray string array with zero default
1163 --StringSlice strings string slice with zero default
1164 --Z int an int that defaults to zero
1165 --custom custom custom Value implementation
1166 --customP custom a VarP with default (default 10)
1167 --maxT timeout set timeout for dial
1168 -v, --verbose count verbosity
1169 `
1170
1171
1172 type customValue int
1173
1174 func (cv *customValue) String() string { return fmt.Sprintf("%v", *cv) }
1175
1176 func (cv *customValue) Set(s string) error {
1177 v, err := strconv.ParseInt(s, 0, 64)
1178 *cv = customValue(v)
1179 return err
1180 }
1181
1182 func (cv *customValue) Type() string { return "custom" }
1183
1184 func TestPrintDefaults(t *testing.T) {
1185 fs := NewFlagSet("print defaults test", ContinueOnError)
1186 var buf bytes.Buffer
1187 fs.SetOutput(&buf)
1188 fs.Bool("A", false, "for bootstrapping, allow 'any' type")
1189 fs.Bool("Alongflagname", false, "disable bounds checking")
1190 fs.BoolP("CCC", "C", true, "a boolean defaulting to true")
1191 fs.String("D", "", "set relative `path` for local imports")
1192 fs.Float64("F", 2.7, "a non-zero `number`")
1193 fs.Float64("G", 0, "a float that defaults to zero")
1194 fs.Int("N", 27, "a non-zero int")
1195 fs.IntSlice("Ints", []int{}, "int slice with zero default")
1196 fs.IP("IP", nil, "IP address with no default")
1197 fs.IPMask("IPMask", nil, "Netmask address with no default")
1198 fs.IPNet("IPNet", net.IPNet{}, "IP network with no default")
1199 fs.Int("Z", 0, "an int that defaults to zero")
1200 fs.Duration("maxT", 0, "set `timeout` for dial")
1201 fs.String("ND1", "foo", "a string with NoOptDefVal")
1202 fs.Lookup("ND1").NoOptDefVal = "bar"
1203 fs.Int("ND2", 1234, "a `num` with NoOptDefVal")
1204 fs.Lookup("ND2").NoOptDefVal = "4321"
1205 fs.IntP("EEE", "E", 4321, "a `num` with NoOptDefVal")
1206 fs.ShorthandLookup("E").NoOptDefVal = "1234"
1207 fs.StringSlice("StringSlice", []string{}, "string slice with zero default")
1208 fs.StringArray("StringArray", []string{}, "string array with zero default")
1209 fs.CountP("verbose", "v", "verbosity")
1210
1211 var cv customValue
1212 fs.Var(&cv, "custom", "custom Value implementation")
1213
1214 cv2 := customValue(10)
1215 fs.VarP(&cv2, "customP", "", "a VarP with default")
1216
1217 fs.PrintDefaults()
1218 got := buf.String()
1219 if got != defaultOutput {
1220 fmt.Println("\n" + got)
1221 fmt.Println("\n" + defaultOutput)
1222 t.Errorf("got %q want %q\n", got, defaultOutput)
1223 }
1224 }
1225
1226 func TestVisitAllFlagOrder(t *testing.T) {
1227 fs := NewFlagSet("TestVisitAllFlagOrder", ContinueOnError)
1228 fs.SortFlags = false
1229
1230 fs.SetNormalizeFunc(func(f *FlagSet, name string) NormalizedName {
1231 return NormalizedName(name)
1232 })
1233
1234 names := []string{"C", "B", "A", "D"}
1235 for _, name := range names {
1236 fs.Bool(name, false, "")
1237 }
1238
1239 i := 0
1240 fs.VisitAll(func(f *Flag) {
1241 if names[i] != f.Name {
1242 t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name)
1243 }
1244 i++
1245 })
1246 }
1247
1248 func TestVisitFlagOrder(t *testing.T) {
1249 fs := NewFlagSet("TestVisitFlagOrder", ContinueOnError)
1250 fs.SortFlags = false
1251 names := []string{"C", "B", "A", "D"}
1252 for _, name := range names {
1253 fs.Bool(name, false, "")
1254 fs.Set(name, "true")
1255 }
1256
1257 i := 0
1258 fs.Visit(func(f *Flag) {
1259 if names[i] != f.Name {
1260 t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name)
1261 }
1262 i++
1263 })
1264 }
1265
View as plain text