Source file
src/flag/flag_test.go
Documentation: flag
1
2
3
4
5 package flag_test
6
7 import (
8 "bytes"
9 . "flag"
10 "fmt"
11 "internal/testenv"
12 "io"
13 "os"
14 "os/exec"
15 "regexp"
16 "runtime"
17 "slices"
18 "strconv"
19 "strings"
20 "testing"
21 "time"
22 )
23
24 func boolString(s string) string {
25 if s == "0" {
26 return "false"
27 }
28 return "true"
29 }
30
31 func TestEverything(t *testing.T) {
32 ResetForTesting(nil)
33 Bool("test_bool", false, "bool value")
34 Int("test_int", 0, "int value")
35 Int64("test_int64", 0, "int64 value")
36 Uint("test_uint", 0, "uint value")
37 Uint64("test_uint64", 0, "uint64 value")
38 String("test_string", "0", "string value")
39 Float64("test_float64", 0, "float64 value")
40 Duration("test_duration", 0, "time.Duration value")
41 Func("test_func", "func value", func(string) error { return nil })
42 BoolFunc("test_boolfunc", "func", func(string) error { return nil })
43
44 m := make(map[string]*Flag)
45 desired := "0"
46 visitor := func(f *Flag) {
47 if len(f.Name) > 5 && f.Name[0:5] == "test_" {
48 m[f.Name] = f
49 ok := false
50 switch {
51 case f.Value.String() == desired:
52 ok = true
53 case f.Name == "test_bool" && f.Value.String() == boolString(desired):
54 ok = true
55 case f.Name == "test_duration" && f.Value.String() == desired+"s":
56 ok = true
57 case f.Name == "test_func" && f.Value.String() == "":
58 ok = true
59 case f.Name == "test_boolfunc" && f.Value.String() == "":
60 ok = true
61 }
62 if !ok {
63 t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
64 }
65 }
66 }
67 VisitAll(visitor)
68 if len(m) != 10 {
69 t.Error("VisitAll misses some flags")
70 for k, v := range m {
71 t.Log(k, *v)
72 }
73 }
74 m = make(map[string]*Flag)
75 Visit(visitor)
76 if len(m) != 0 {
77 t.Errorf("Visit sees unset flags")
78 for k, v := range m {
79 t.Log(k, *v)
80 }
81 }
82
83 Set("test_bool", "true")
84 Set("test_int", "1")
85 Set("test_int64", "1")
86 Set("test_uint", "1")
87 Set("test_uint64", "1")
88 Set("test_string", "1")
89 Set("test_float64", "1")
90 Set("test_duration", "1s")
91 Set("test_func", "1")
92 Set("test_boolfunc", "")
93 desired = "1"
94 Visit(visitor)
95 if len(m) != 10 {
96 t.Error("Visit fails after set")
97 for k, v := range m {
98 t.Log(k, *v)
99 }
100 }
101
102 var flagNames []string
103 Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) })
104 if !slices.IsSorted(flagNames) {
105 t.Errorf("flag names not sorted: %v", flagNames)
106 }
107 }
108
109 func TestGet(t *testing.T) {
110 ResetForTesting(nil)
111 Bool("test_bool", true, "bool value")
112 Int("test_int", 1, "int value")
113 Int64("test_int64", 2, "int64 value")
114 Uint("test_uint", 3, "uint value")
115 Uint64("test_uint64", 4, "uint64 value")
116 String("test_string", "5", "string value")
117 Float64("test_float64", 6, "float64 value")
118 Duration("test_duration", 7, "time.Duration value")
119
120 visitor := func(f *Flag) {
121 if len(f.Name) > 5 && f.Name[0:5] == "test_" {
122 g, ok := f.Value.(Getter)
123 if !ok {
124 t.Errorf("Visit: value does not satisfy Getter: %T", f.Value)
125 return
126 }
127 switch f.Name {
128 case "test_bool":
129 ok = g.Get() == true
130 case "test_int":
131 ok = g.Get() == int(1)
132 case "test_int64":
133 ok = g.Get() == int64(2)
134 case "test_uint":
135 ok = g.Get() == uint(3)
136 case "test_uint64":
137 ok = g.Get() == uint64(4)
138 case "test_string":
139 ok = g.Get() == "5"
140 case "test_float64":
141 ok = g.Get() == float64(6)
142 case "test_duration":
143 ok = g.Get() == time.Duration(7)
144 }
145 if !ok {
146 t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), f.Name)
147 }
148 }
149 }
150 VisitAll(visitor)
151 }
152
153 func TestUsage(t *testing.T) {
154 called := false
155 ResetForTesting(func() { called = true })
156 if CommandLine.Parse([]string{"-x"}) == nil {
157 t.Error("parse did not fail for unknown flag")
158 }
159 if !called {
160 t.Error("did not call Usage for unknown flag")
161 }
162 }
163
164 func testParse(f *FlagSet, t *testing.T) {
165 if f.Parsed() {
166 t.Error("f.Parse() = true before Parse")
167 }
168 boolFlag := f.Bool("bool", false, "bool value")
169 bool2Flag := f.Bool("bool2", false, "bool2 value")
170 intFlag := f.Int("int", 0, "int value")
171 int64Flag := f.Int64("int64", 0, "int64 value")
172 uintFlag := f.Uint("uint", 0, "uint value")
173 uint64Flag := f.Uint64("uint64", 0, "uint64 value")
174 stringFlag := f.String("string", "0", "string value")
175 float64Flag := f.Float64("float64", 0, "float64 value")
176 durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
177 extra := "one-extra-argument"
178 args := []string{
179 "-bool",
180 "-bool2=true",
181 "--int", "22",
182 "--int64", "0x23",
183 "-uint", "24",
184 "--uint64", "25",
185 "-string", "hello",
186 "-float64", "2718e28",
187 "-duration", "2m",
188 extra,
189 }
190 if err := f.Parse(args); err != nil {
191 t.Fatal(err)
192 }
193 if !f.Parsed() {
194 t.Error("f.Parse() = false after Parse")
195 }
196 if *boolFlag != true {
197 t.Error("bool flag should be true, is ", *boolFlag)
198 }
199 if *bool2Flag != true {
200 t.Error("bool2 flag should be true, is ", *bool2Flag)
201 }
202 if *intFlag != 22 {
203 t.Error("int flag should be 22, is ", *intFlag)
204 }
205 if *int64Flag != 0x23 {
206 t.Error("int64 flag should be 0x23, is ", *int64Flag)
207 }
208 if *uintFlag != 24 {
209 t.Error("uint flag should be 24, is ", *uintFlag)
210 }
211 if *uint64Flag != 25 {
212 t.Error("uint64 flag should be 25, is ", *uint64Flag)
213 }
214 if *stringFlag != "hello" {
215 t.Error("string flag should be `hello`, is ", *stringFlag)
216 }
217 if *float64Flag != 2718e28 {
218 t.Error("float64 flag should be 2718e28, is ", *float64Flag)
219 }
220 if *durationFlag != 2*time.Minute {
221 t.Error("duration flag should be 2m, is ", *durationFlag)
222 }
223 if len(f.Args()) != 1 {
224 t.Error("expected one argument, got", len(f.Args()))
225 } else if f.Args()[0] != extra {
226 t.Errorf("expected argument %q got %q", extra, f.Args()[0])
227 }
228 }
229
230 func TestParse(t *testing.T) {
231 ResetForTesting(func() { t.Error("bad parse") })
232 testParse(CommandLine, t)
233 }
234
235 func TestFlagSetParse(t *testing.T) {
236 testParse(NewFlagSet("test", ContinueOnError), t)
237 }
238
239
240 type flagVar []string
241
242 func (f *flagVar) String() string {
243 return fmt.Sprint([]string(*f))
244 }
245
246 func (f *flagVar) Set(value string) error {
247 *f = append(*f, value)
248 return nil
249 }
250
251 func TestUserDefined(t *testing.T) {
252 var flags FlagSet
253 flags.Init("test", ContinueOnError)
254 flags.SetOutput(io.Discard)
255 var v flagVar
256 flags.Var(&v, "v", "usage")
257 if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
258 t.Error(err)
259 }
260 if len(v) != 3 {
261 t.Fatal("expected 3 args; got ", len(v))
262 }
263 expect := "[1 2 3]"
264 if v.String() != expect {
265 t.Errorf("expected value %q got %q", expect, v.String())
266 }
267 }
268
269 func TestUserDefinedFunc(t *testing.T) {
270 flags := NewFlagSet("test", ContinueOnError)
271 flags.SetOutput(io.Discard)
272 var ss []string
273 flags.Func("v", "usage", func(s string) error {
274 ss = append(ss, s)
275 return nil
276 })
277 if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
278 t.Error(err)
279 }
280 if len(ss) != 3 {
281 t.Fatal("expected 3 args; got ", len(ss))
282 }
283 expect := "[1 2 3]"
284 if got := fmt.Sprint(ss); got != expect {
285 t.Errorf("expected value %q got %q", expect, got)
286 }
287
288 var buf strings.Builder
289 flags.SetOutput(&buf)
290 flags.Parse([]string{"-h"})
291 if usage := buf.String(); !strings.Contains(usage, "usage") {
292 t.Errorf("usage string not included: %q", usage)
293 }
294
295 flags = NewFlagSet("test", ContinueOnError)
296 flags.SetOutput(io.Discard)
297 flags.Func("v", "usage", func(s string) error {
298 return fmt.Errorf("test error")
299 })
300
301 if err := flags.Parse(nil); err != nil {
302 t.Error(err)
303 }
304
305 if err := flags.Parse([]string{"-v", "1"}); err == nil {
306 t.Error("expected error; got none")
307 } else if errMsg := err.Error(); !strings.Contains(errMsg, "test error") {
308 t.Errorf(`error should contain "test error"; got %q`, errMsg)
309 }
310 }
311
312 func TestUserDefinedForCommandLine(t *testing.T) {
313 const help = "HELP"
314 var result string
315 ResetForTesting(func() { result = help })
316 Usage()
317 if result != help {
318 t.Fatalf("got %q; expected %q", result, help)
319 }
320 }
321
322
323 type boolFlagVar struct {
324 count int
325 }
326
327 func (b *boolFlagVar) String() string {
328 return fmt.Sprintf("%d", b.count)
329 }
330
331 func (b *boolFlagVar) Set(value string) error {
332 if value == "true" {
333 b.count++
334 }
335 return nil
336 }
337
338 func (b *boolFlagVar) IsBoolFlag() bool {
339 return b.count < 4
340 }
341
342 func TestUserDefinedBool(t *testing.T) {
343 var flags FlagSet
344 flags.Init("test", ContinueOnError)
345 flags.SetOutput(io.Discard)
346 var b boolFlagVar
347 var err error
348 flags.Var(&b, "b", "usage")
349 if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil {
350 if b.count < 4 {
351 t.Error(err)
352 }
353 }
354
355 if b.count != 4 {
356 t.Errorf("want: %d; got: %d", 4, b.count)
357 }
358
359 if err == nil {
360 t.Error("expected error; got none")
361 }
362 }
363
364 func TestUserDefinedBoolUsage(t *testing.T) {
365 var flags FlagSet
366 flags.Init("test", ContinueOnError)
367 var buf bytes.Buffer
368 flags.SetOutput(&buf)
369 var b boolFlagVar
370 flags.Var(&b, "b", "X")
371 b.count = 0
372
373 flags.PrintDefaults()
374 got := buf.String()
375 want := " -b\tX\n"
376 if got != want {
377 t.Errorf("false: want %q; got %q", want, got)
378 }
379 b.count = 4
380
381 flags.PrintDefaults()
382 got = buf.String()
383 want = " -b\tX\n -b value\n \tX\n"
384 if got != want {
385 t.Errorf("false: want %q; got %q", want, got)
386 }
387 }
388
389 func TestSetOutput(t *testing.T) {
390 var flags FlagSet
391 var buf strings.Builder
392 flags.SetOutput(&buf)
393 flags.Init("test", ContinueOnError)
394 flags.Parse([]string{"-unknown"})
395 if out := buf.String(); !strings.Contains(out, "-unknown") {
396 t.Logf("expected output mentioning unknown; got %q", out)
397 }
398 }
399
400
401
402 func TestChangingArgs(t *testing.T) {
403 ResetForTesting(func() { t.Fatal("bad parse") })
404 oldArgs := os.Args
405 defer func() { os.Args = oldArgs }()
406 os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
407 before := Bool("before", false, "")
408 if err := CommandLine.Parse(os.Args[1:]); err != nil {
409 t.Fatal(err)
410 }
411 cmd := Arg(0)
412 os.Args = Args()
413 after := Bool("after", false, "")
414 Parse()
415 args := Args()
416
417 if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
418 t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
419 }
420 }
421
422
423 func TestHelp(t *testing.T) {
424 var helpCalled = false
425 fs := NewFlagSet("help test", ContinueOnError)
426 fs.Usage = func() { helpCalled = true }
427 var flag bool
428 fs.BoolVar(&flag, "flag", false, "regular flag")
429
430 err := fs.Parse([]string{"-flag=true"})
431 if err != nil {
432 t.Fatal("expected no error; got ", err)
433 }
434 if !flag {
435 t.Error("flag was not set by -flag")
436 }
437 if helpCalled {
438 t.Error("help called for regular flag")
439 helpCalled = false
440 }
441
442 err = fs.Parse([]string{"-help"})
443 if err == nil {
444 t.Fatal("error expected")
445 }
446 if err != ErrHelp {
447 t.Fatal("expected ErrHelp; got ", err)
448 }
449 if !helpCalled {
450 t.Fatal("help was not called")
451 }
452
453 var help bool
454 fs.BoolVar(&help, "help", false, "help flag")
455 helpCalled = false
456 err = fs.Parse([]string{"-help"})
457 if err != nil {
458 t.Fatal("expected no error for defined -help; got ", err)
459 }
460 if helpCalled {
461 t.Fatal("help was called; should not have been for defined help flag")
462 }
463 }
464
465
466
467 type zeroPanicker struct {
468 dontPanic bool
469 v string
470 }
471
472 func (f *zeroPanicker) Set(s string) error {
473 f.v = s
474 return nil
475 }
476
477 func (f *zeroPanicker) String() string {
478 if !f.dontPanic {
479 panic("panic!")
480 }
481 return f.v
482 }
483
484 const defaultOutput = ` -A for bootstrapping, allow 'any' type
485 -Alongflagname
486 disable bounds checking
487 -C a boolean defaulting to true (default true)
488 -D path
489 set relative path for local imports
490 -E string
491 issue 23543 (default "0")
492 -F number
493 a non-zero number (default 2.7)
494 -G float
495 a float that defaults to zero
496 -M string
497 a multiline
498 help
499 string
500 -N int
501 a non-zero int (default 27)
502 -O a flag
503 multiline help string (default true)
504 -V list
505 a list of strings (default [a b])
506 -Z int
507 an int that defaults to zero
508 -ZP0 value
509 a flag whose String method panics when it is zero
510 -ZP1 value
511 a flag whose String method panics when it is zero
512 -maxT timeout
513 set timeout for dial
514
515 panic calling String method on zero flag_test.zeroPanicker for flag ZP0: panic!
516 panic calling String method on zero flag_test.zeroPanicker for flag ZP1: panic!
517 `
518
519 func TestPrintDefaults(t *testing.T) {
520 fs := NewFlagSet("print defaults test", ContinueOnError)
521 var buf strings.Builder
522 fs.SetOutput(&buf)
523 fs.Bool("A", false, "for bootstrapping, allow 'any' type")
524 fs.Bool("Alongflagname", false, "disable bounds checking")
525 fs.Bool("C", true, "a boolean defaulting to true")
526 fs.String("D", "", "set relative `path` for local imports")
527 fs.String("E", "0", "issue 23543")
528 fs.Float64("F", 2.7, "a non-zero `number`")
529 fs.Float64("G", 0, "a float that defaults to zero")
530 fs.String("M", "", "a multiline\nhelp\nstring")
531 fs.Int("N", 27, "a non-zero int")
532 fs.Bool("O", true, "a flag\nmultiline help string")
533 fs.Var(&flagVar{"a", "b"}, "V", "a `list` of strings")
534 fs.Int("Z", 0, "an int that defaults to zero")
535 fs.Var(&zeroPanicker{true, ""}, "ZP0", "a flag whose String method panics when it is zero")
536 fs.Var(&zeroPanicker{true, "something"}, "ZP1", "a flag whose String method panics when it is zero")
537 fs.Duration("maxT", 0, "set `timeout` for dial")
538 fs.PrintDefaults()
539 got := buf.String()
540 if got != defaultOutput {
541 t.Errorf("got:\n%q\nwant:\n%q", got, defaultOutput)
542 }
543 }
544
545
546 func TestIntFlagOverflow(t *testing.T) {
547 if strconv.IntSize != 32 {
548 return
549 }
550 ResetForTesting(nil)
551 Int("i", 0, "")
552 Uint("u", 0, "")
553 if err := Set("i", "2147483648"); err == nil {
554 t.Error("unexpected success setting Int")
555 }
556 if err := Set("u", "4294967296"); err == nil {
557 t.Error("unexpected success setting Uint")
558 }
559 }
560
561
562 func TestUsageOutput(t *testing.T) {
563 ResetForTesting(DefaultUsage)
564 var buf strings.Builder
565 CommandLine.SetOutput(&buf)
566 defer func(old []string) { os.Args = old }(os.Args)
567 os.Args = []string{"app", "-i=1", "-unknown"}
568 Parse()
569 const want = "flag provided but not defined: -i\nUsage of app:\n"
570 if got := buf.String(); got != want {
571 t.Errorf("output = %q; want %q", got, want)
572 }
573 }
574
575 func TestGetters(t *testing.T) {
576 expectedName := "flag set"
577 expectedErrorHandling := ContinueOnError
578 expectedOutput := io.Writer(os.Stderr)
579 fs := NewFlagSet(expectedName, expectedErrorHandling)
580
581 if fs.Name() != expectedName {
582 t.Errorf("unexpected name: got %s, expected %s", fs.Name(), expectedName)
583 }
584 if fs.ErrorHandling() != expectedErrorHandling {
585 t.Errorf("unexpected ErrorHandling: got %d, expected %d", fs.ErrorHandling(), expectedErrorHandling)
586 }
587 if fs.Output() != expectedOutput {
588 t.Errorf("unexpected output: got %#v, expected %#v", fs.Output(), expectedOutput)
589 }
590
591 expectedName = "gopher"
592 expectedErrorHandling = ExitOnError
593 expectedOutput = os.Stdout
594 fs.Init(expectedName, expectedErrorHandling)
595 fs.SetOutput(expectedOutput)
596
597 if fs.Name() != expectedName {
598 t.Errorf("unexpected name: got %s, expected %s", fs.Name(), expectedName)
599 }
600 if fs.ErrorHandling() != expectedErrorHandling {
601 t.Errorf("unexpected ErrorHandling: got %d, expected %d", fs.ErrorHandling(), expectedErrorHandling)
602 }
603 if fs.Output() != expectedOutput {
604 t.Errorf("unexpected output: got %v, expected %v", fs.Output(), expectedOutput)
605 }
606 }
607
608 func TestParseError(t *testing.T) {
609 for _, typ := range []string{"bool", "int", "int64", "uint", "uint64", "float64", "duration"} {
610 fs := NewFlagSet("parse error test", ContinueOnError)
611 fs.SetOutput(io.Discard)
612 _ = fs.Bool("bool", false, "")
613 _ = fs.Int("int", 0, "")
614 _ = fs.Int64("int64", 0, "")
615 _ = fs.Uint("uint", 0, "")
616 _ = fs.Uint64("uint64", 0, "")
617 _ = fs.Float64("float64", 0, "")
618 _ = fs.Duration("duration", 0, "")
619
620 args := []string{"-" + typ + "=x"}
621 err := fs.Parse(args)
622 if err == nil {
623 t.Errorf("Parse(%q)=%v; expected parse error", args, err)
624 continue
625 }
626 if !strings.Contains(err.Error(), "invalid") || !strings.Contains(err.Error(), "parse error") {
627 t.Errorf("Parse(%q)=%v; expected parse error", args, err)
628 }
629 }
630 }
631
632 func TestRangeError(t *testing.T) {
633 bad := []string{
634 "-int=123456789012345678901",
635 "-int64=123456789012345678901",
636 "-uint=123456789012345678901",
637 "-uint64=123456789012345678901",
638 "-float64=1e1000",
639 }
640 for _, arg := range bad {
641 fs := NewFlagSet("parse error test", ContinueOnError)
642 fs.SetOutput(io.Discard)
643 _ = fs.Int("int", 0, "")
644 _ = fs.Int64("int64", 0, "")
645 _ = fs.Uint("uint", 0, "")
646 _ = fs.Uint64("uint64", 0, "")
647 _ = fs.Float64("float64", 0, "")
648
649 err := fs.Parse([]string{arg})
650 if err == nil {
651 t.Errorf("Parse(%q)=%v; expected range error", arg, err)
652 continue
653 }
654 if !strings.Contains(err.Error(), "invalid") || !strings.Contains(err.Error(), "value out of range") {
655 t.Errorf("Parse(%q)=%v; expected range error", arg, err)
656 }
657 }
658 }
659
660 func TestExitCode(t *testing.T) {
661 testenv.MustHaveExec(t)
662
663 magic := 123
664 if os.Getenv("GO_CHILD_FLAG") != "" {
665 fs := NewFlagSet("test", ExitOnError)
666 if os.Getenv("GO_CHILD_FLAG_HANDLE") != "" {
667 var b bool
668 fs.BoolVar(&b, os.Getenv("GO_CHILD_FLAG_HANDLE"), false, "")
669 }
670 fs.Parse([]string{os.Getenv("GO_CHILD_FLAG")})
671 os.Exit(magic)
672 }
673
674 tests := []struct {
675 flag string
676 flagHandle string
677 expectExit int
678 }{
679 {
680 flag: "-h",
681 expectExit: 0,
682 },
683 {
684 flag: "-help",
685 expectExit: 0,
686 },
687 {
688 flag: "-undefined",
689 expectExit: 2,
690 },
691 {
692 flag: "-h",
693 flagHandle: "h",
694 expectExit: magic,
695 },
696 {
697 flag: "-help",
698 flagHandle: "help",
699 expectExit: magic,
700 },
701 }
702
703 for _, test := range tests {
704 cmd := exec.Command(os.Args[0], "-test.run=^TestExitCode$")
705 cmd.Env = append(
706 os.Environ(),
707 "GO_CHILD_FLAG="+test.flag,
708 "GO_CHILD_FLAG_HANDLE="+test.flagHandle,
709 )
710 cmd.Run()
711 got := cmd.ProcessState.ExitCode()
712
713 if runtime.GOOS == "plan9" && test.expectExit != 0 {
714 test.expectExit = 1
715 }
716 if got != test.expectExit {
717 t.Errorf("unexpected exit code for test case %+v \n: got %d, expect %d",
718 test, got, test.expectExit)
719 }
720 }
721 }
722
723 func mustPanic(t *testing.T, testName string, expected string, f func()) {
724 t.Helper()
725 defer func() {
726 switch msg := recover().(type) {
727 case nil:
728 t.Errorf("%s\n: expected panic(%q), but did not panic", testName, expected)
729 case string:
730 if ok, _ := regexp.MatchString(expected, msg); !ok {
731 t.Errorf("%s\n: expected panic(%q), but got panic(%q)", testName, expected, msg)
732 }
733 default:
734 t.Errorf("%s\n: expected panic(%q), but got panic(%T%v)", testName, expected, msg, msg)
735 }
736 }()
737 f()
738 }
739
740 func TestInvalidFlags(t *testing.T) {
741 tests := []struct {
742 flag string
743 errorMsg string
744 }{
745 {
746 flag: "-foo",
747 errorMsg: "flag \"-foo\" begins with -",
748 },
749 {
750 flag: "foo=bar",
751 errorMsg: "flag \"foo=bar\" contains =",
752 },
753 }
754
755 for _, test := range tests {
756 testName := fmt.Sprintf("FlagSet.Var(&v, %q, \"\")", test.flag)
757
758 fs := NewFlagSet("", ContinueOnError)
759 buf := &strings.Builder{}
760 fs.SetOutput(buf)
761
762 mustPanic(t, testName, test.errorMsg, func() {
763 var v flagVar
764 fs.Var(&v, test.flag, "")
765 })
766 if msg := test.errorMsg + "\n"; msg != buf.String() {
767 t.Errorf("%s\n: unexpected output: expected %q, bug got %q", testName, msg, buf)
768 }
769 }
770 }
771
772 func TestRedefinedFlags(t *testing.T) {
773 tests := []struct {
774 flagSetName string
775 errorMsg string
776 }{
777 {
778 flagSetName: "",
779 errorMsg: "flag redefined: foo",
780 },
781 {
782 flagSetName: "fs",
783 errorMsg: "fs flag redefined: foo",
784 },
785 }
786
787 for _, test := range tests {
788 testName := fmt.Sprintf("flag redefined in FlagSet(%q)", test.flagSetName)
789
790 fs := NewFlagSet(test.flagSetName, ContinueOnError)
791 buf := &strings.Builder{}
792 fs.SetOutput(buf)
793
794 var v flagVar
795 fs.Var(&v, "foo", "")
796
797 mustPanic(t, testName, test.errorMsg, func() {
798 fs.Var(&v, "foo", "")
799 })
800 if msg := test.errorMsg + "\n"; msg != buf.String() {
801 t.Errorf("%s\n: unexpected output: expected %q, bug got %q", testName, msg, buf)
802 }
803 }
804 }
805
806 func TestUserDefinedBoolFunc(t *testing.T) {
807 flags := NewFlagSet("test", ContinueOnError)
808 flags.SetOutput(io.Discard)
809 var ss []string
810 flags.BoolFunc("v", "usage", func(s string) error {
811 ss = append(ss, s)
812 return nil
813 })
814 if err := flags.Parse([]string{"-v", "", "-v", "1", "-v=2"}); err != nil {
815 t.Error(err)
816 }
817 if len(ss) != 1 {
818 t.Fatalf("got %d args; want 1 arg", len(ss))
819 }
820 want := "[true]"
821 if got := fmt.Sprint(ss); got != want {
822 t.Errorf("got %q; want %q", got, want)
823 }
824
825 var buf strings.Builder
826 flags.SetOutput(&buf)
827 flags.Parse([]string{"-h"})
828 if usage := buf.String(); !strings.Contains(usage, "usage") {
829 t.Errorf("usage string not included: %q", usage)
830 }
831
832 flags = NewFlagSet("test", ContinueOnError)
833 flags.SetOutput(io.Discard)
834 flags.BoolFunc("v", "usage", func(s string) error {
835 return fmt.Errorf("test error")
836 })
837
838 if err := flags.Parse(nil); err != nil {
839 t.Error(err)
840 }
841
842 if err := flags.Parse([]string{"-v", ""}); err == nil {
843 t.Error("got err == nil; want err != nil")
844 } else if errMsg := err.Error(); !strings.Contains(errMsg, "test error") {
845 t.Errorf(`got %q; error should contain "test error"`, errMsg)
846 }
847 }
848
849 func TestDefineAfterSet(t *testing.T) {
850 flags := NewFlagSet("test", ContinueOnError)
851
852 flags.Set("myFlag", "value")
853
854
855 mustPanic(t, "DefineAfterSet", "flag myFlag set at .*/flag_test.go:.* before being defined", func() {
856 _ = flags.String("myFlag", "default", "usage")
857 })
858 }
859
View as plain text