1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
23 func boolString(s string) string {
30 func TestEverything(t *testing.T) {
32 Bool("test_bool", false, "bool value")
33 Int("test_int", 0, "int value")
34 Int64("test_int64", 0, "int64 value")
35 Uint("test_uint", 0, "uint value")
36 Uint64("test_uint64", 0, "uint64 value")
37 String("test_string", "0", "string value")
38 Float64("test_float64", 0, "float64 value")
39 Duration("test_duration", 0, "time.Duration value")
40 Func("test_func", "func value", func(string) error { return nil })
41 BoolFunc("test_boolfunc", "func", func(string) error { return nil })
43 m := make(map[string]*Flag)
45 visitor := func(f *Flag) {
46 if len(f.Name) > 5 && f.Name[0:5] == "test_" {
50 case f.Value.String() == desired:
52 case f.Name == "test_bool" && f.Value.String() == boolString(desired):
54 case f.Name == "test_duration" && f.Value.String() == desired+"s":
56 case f.Name == "test_func" && f.Value.String() == "":
58 case f.Name == "test_boolfunc" && f.Value.String() == "":
62 t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
68 t.Error("VisitAll misses some flags")
73 m = make(map[string]*Flag)
76 t.Errorf("Visit sees unset flags")
82 Set("test_bool", "true")
84 Set("test_int64", "1")
86 Set("test_uint64", "1")
87 Set("test_string", "1")
88 Set("test_float64", "1")
89 Set("test_duration", "1s")
91 Set("test_boolfunc", "")
95 t.Error("Visit fails after set")
100 // Now test they're visited in sort order.
101 var flagNames []string
102 Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) })
103 if !sort.StringsAreSorted(flagNames) {
104 t.Errorf("flag names not sorted: %v", flagNames)
108 func TestGet(t *testing.T) {
110 Bool("test_bool", true, "bool value")
111 Int("test_int", 1, "int value")
112 Int64("test_int64", 2, "int64 value")
113 Uint("test_uint", 3, "uint value")
114 Uint64("test_uint64", 4, "uint64 value")
115 String("test_string", "5", "string value")
116 Float64("test_float64", 6, "float64 value")
117 Duration("test_duration", 7, "time.Duration value")
119 visitor := func(f *Flag) {
120 if len(f.Name) > 5 && f.Name[0:5] == "test_" {
121 g, ok := f.Value.(Getter)
123 t.Errorf("Visit: value does not satisfy Getter: %T", f.Value)
130 ok = g.Get() == int(1)
132 ok = g.Get() == int64(2)
134 ok = g.Get() == uint(3)
136 ok = g.Get() == uint64(4)
140 ok = g.Get() == float64(6)
141 case "test_duration":
142 ok = g.Get() == time.Duration(7)
145 t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), f.Name)
152 func TestUsage(t *testing.T) {
154 ResetForTesting(func() { called = true })
155 if CommandLine.Parse([]string{"-x"}) == nil {
156 t.Error("parse did not fail for unknown flag")
159 t.Error("did not call Usage for unknown flag")
163 func testParse(f *FlagSet, t *testing.T) {
165 t.Error("f.Parse() = true before Parse")
167 boolFlag := f.Bool("bool", false, "bool value")
168 bool2Flag := f.Bool("bool2", false, "bool2 value")
169 intFlag := f.Int("int", 0, "int value")
170 int64Flag := f.Int64("int64", 0, "int64 value")
171 uintFlag := f.Uint("uint", 0, "uint value")
172 uint64Flag := f.Uint64("uint64", 0, "uint64 value")
173 stringFlag := f.String("string", "0", "string value")
174 float64Flag := f.Float64("float64", 0, "float64 value")
175 durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
176 extra := "one-extra-argument"
185 "-float64", "2718e28",
189 if err := f.Parse(args); err != nil {
193 t.Error("f.Parse() = false after Parse")
195 if *boolFlag != true {
196 t.Error("bool flag should be true, is ", *boolFlag)
198 if *bool2Flag != true {
199 t.Error("bool2 flag should be true, is ", *bool2Flag)
202 t.Error("int flag should be 22, is ", *intFlag)
204 if *int64Flag != 0x23 {
205 t.Error("int64 flag should be 0x23, is ", *int64Flag)
208 t.Error("uint flag should be 24, is ", *uintFlag)
210 if *uint64Flag != 25 {
211 t.Error("uint64 flag should be 25, is ", *uint64Flag)
213 if *stringFlag != "hello" {
214 t.Error("string flag should be `hello`, is ", *stringFlag)
216 if *float64Flag != 2718e28 {
217 t.Error("float64 flag should be 2718e28, is ", *float64Flag)
219 if *durationFlag != 2*time.Minute {
220 t.Error("duration flag should be 2m, is ", *durationFlag)
222 if len(f.Args()) != 1 {
223 t.Error("expected one argument, got", len(f.Args()))
224 } else if f.Args()[0] != extra {
225 t.Errorf("expected argument %q got %q", extra, f.Args()[0])
229 func TestParse(t *testing.T) {
230 ResetForTesting(func() { t.Error("bad parse") })
231 testParse(CommandLine, t)
234 func TestFlagSetParse(t *testing.T) {
235 testParse(NewFlagSet("test", ContinueOnError), t)
238 // Declare a user-defined flag type.
239 type flagVar []string
241 func (f *flagVar) String() string {
242 return fmt.Sprint([]string(*f))
245 func (f *flagVar) Set(value string) error {
246 *f = append(*f, value)
250 func TestUserDefined(t *testing.T) {
252 flags.Init("test", ContinueOnError)
253 flags.SetOutput(io.Discard)
255 flags.Var(&v, "v", "usage")
256 if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
260 t.Fatal("expected 3 args; got ", len(v))
263 if v.String() != expect {
264 t.Errorf("expected value %q got %q", expect, v.String())
268 func TestUserDefinedFunc(t *testing.T) {
269 flags := NewFlagSet("test", ContinueOnError)
270 flags.SetOutput(io.Discard)
272 flags.Func("v", "usage", func(s string) error {
276 if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
280 t.Fatal("expected 3 args; got ", len(ss))
283 if got := fmt.Sprint(ss); got != expect {
284 t.Errorf("expected value %q got %q", expect, got)
287 var buf strings.Builder
288 flags.SetOutput(&buf)
289 flags.Parse([]string{"-h"})
290 if usage := buf.String(); !strings.Contains(usage, "usage") {
291 t.Errorf("usage string not included: %q", usage)
294 flags = NewFlagSet("test", ContinueOnError)
295 flags.SetOutput(io.Discard)
296 flags.Func("v", "usage", func(s string) error {
297 return fmt.Errorf("test error")
299 // flag not set, so no error
300 if err := flags.Parse(nil); err != nil {
303 // flag set, expect error
304 if err := flags.Parse([]string{"-v", "1"}); err == nil {
305 t.Error("expected error; got none")
306 } else if errMsg := err.Error(); !strings.Contains(errMsg, "test error") {
307 t.Errorf(`error should contain "test error"; got %q`, errMsg)
311 func TestUserDefinedForCommandLine(t *testing.T) {
314 ResetForTesting(func() { result = help })
317 t.Fatalf("got %q; expected %q", result, help)
321 // Declare a user-defined boolean flag type.
322 type boolFlagVar struct {
326 func (b *boolFlagVar) String() string {
327 return fmt.Sprintf("%d", b.count)
330 func (b *boolFlagVar) Set(value string) error {
337 func (b *boolFlagVar) IsBoolFlag() bool {
341 func TestUserDefinedBool(t *testing.T) {
343 flags.Init("test", ContinueOnError)
344 flags.SetOutput(io.Discard)
347 flags.Var(&b, "b", "usage")
348 if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil {
355 t.Errorf("want: %d; got: %d", 4, b.count)
359 t.Error("expected error; got none")
363 func TestUserDefinedBoolUsage(t *testing.T) {
365 flags.Init("test", ContinueOnError)
367 flags.SetOutput(&buf)
369 flags.Var(&b, "b", "X")
371 // b.IsBoolFlag() will return true and usage will look boolean.
372 flags.PrintDefaults()
376 t.Errorf("false: want %q; got %q", want, got)
379 // b.IsBoolFlag() will return false and usage will look non-boolean.
380 flags.PrintDefaults()
382 want = " -b\tX\n -b value\n \tX\n"
384 t.Errorf("false: want %q; got %q", want, got)
388 func TestSetOutput(t *testing.T) {
390 var buf strings.Builder
391 flags.SetOutput(&buf)
392 flags.Init("test", ContinueOnError)
393 flags.Parse([]string{"-unknown"})
394 if out := buf.String(); !strings.Contains(out, "-unknown") {
395 t.Logf("expected output mentioning unknown; got %q", out)
399 // This tests that one can reset the flags. This still works but not well, and is
400 // superseded by FlagSet.
401 func TestChangingArgs(t *testing.T) {
402 ResetForTesting(func() { t.Fatal("bad parse") })
404 defer func() { os.Args = oldArgs }()
405 os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
406 before := Bool("before", false, "")
407 if err := CommandLine.Parse(os.Args[1:]); err != nil {
412 after := Bool("after", false, "")
416 if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
417 t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
421 // Test that -help invokes the usage message and returns ErrHelp.
422 func TestHelp(t *testing.T) {
423 var helpCalled = false
424 fs := NewFlagSet("help test", ContinueOnError)
425 fs.Usage = func() { helpCalled = true }
427 fs.BoolVar(&flag, "flag", false, "regular flag")
428 // Regular flag invocation should work
429 err := fs.Parse([]string{"-flag=true"})
431 t.Fatal("expected no error; got ", err)
434 t.Error("flag was not set by -flag")
437 t.Error("help called for regular flag")
438 helpCalled = false // reset for next test
440 // Help flag should work as expected.
441 err = fs.Parse([]string{"-help"})
443 t.Fatal("error expected")
446 t.Fatal("expected ErrHelp; got ", err)
449 t.Fatal("help was not called")
451 // If we define a help flag, that should override.
453 fs.BoolVar(&help, "help", false, "help flag")
455 err = fs.Parse([]string{"-help"})
457 t.Fatal("expected no error for defined -help; got ", err)
460 t.Fatal("help was called; should not have been for defined help flag")
464 // zeroPanicker is a flag.Value whose String method panics if its dontPanic
466 type zeroPanicker struct {
471 func (f *zeroPanicker) Set(s string) error {
476 func (f *zeroPanicker) String() string {
483 const defaultOutput = ` -A for bootstrapping, allow 'any' type
485 disable bounds checking
486 -C a boolean defaulting to true (default true)
488 set relative path for local imports
490 issue 23543 (default "0")
492 a non-zero number (default 2.7)
494 a float that defaults to zero
500 a non-zero int (default 27)
502 multiline help string (default true)
504 a list of strings (default [a b])
506 an int that defaults to zero
508 a flag whose String method panics when it is zero
510 a flag whose String method panics when it is zero
514 panic calling String method on zero flag_test.zeroPanicker for flag ZP0: panic!
515 panic calling String method on zero flag_test.zeroPanicker for flag ZP1: panic!
518 func TestPrintDefaults(t *testing.T) {
519 fs := NewFlagSet("print defaults test", ContinueOnError)
520 var buf strings.Builder
522 fs.Bool("A", false, "for bootstrapping, allow 'any' type")
523 fs.Bool("Alongflagname", false, "disable bounds checking")
524 fs.Bool("C", true, "a boolean defaulting to true")
525 fs.String("D", "", "set relative `path` for local imports")
526 fs.String("E", "0", "issue 23543")
527 fs.Float64("F", 2.7, "a non-zero `number`")
528 fs.Float64("G", 0, "a float that defaults to zero")
529 fs.String("M", "", "a multiline\nhelp\nstring")
530 fs.Int("N", 27, "a non-zero int")
531 fs.Bool("O", true, "a flag\nmultiline help string")
532 fs.Var(&flagVar{"a", "b"}, "V", "a `list` of strings")
533 fs.Int("Z", 0, "an int that defaults to zero")
534 fs.Var(&zeroPanicker{true, ""}, "ZP0", "a flag whose String method panics when it is zero")
535 fs.Var(&zeroPanicker{true, "something"}, "ZP1", "a flag whose String method panics when it is zero")
536 fs.Duration("maxT", 0, "set `timeout` for dial")
539 if got != defaultOutput {
540 t.Errorf("got:\n%q\nwant:\n%q", got, defaultOutput)
544 // Issue 19230: validate range of Int and Uint flag values.
545 func TestIntFlagOverflow(t *testing.T) {
546 if strconv.IntSize != 32 {
552 if err := Set("i", "2147483648"); err == nil {
553 t.Error("unexpected success setting Int")
555 if err := Set("u", "4294967296"); err == nil {
556 t.Error("unexpected success setting Uint")
560 // Issue 20998: Usage should respect CommandLine.output.
561 func TestUsageOutput(t *testing.T) {
562 ResetForTesting(DefaultUsage)
563 var buf strings.Builder
564 CommandLine.SetOutput(&buf)
565 defer func(old []string) { os.Args = old }(os.Args)
566 os.Args = []string{"app", "-i=1", "-unknown"}
568 const want = "flag provided but not defined: -i\nUsage of app:\n"
569 if got := buf.String(); got != want {
570 t.Errorf("output = %q; want %q", got, want)
574 func TestGetters(t *testing.T) {
575 expectedName := "flag set"
576 expectedErrorHandling := ContinueOnError
577 expectedOutput := io.Writer(os.Stderr)
578 fs := NewFlagSet(expectedName, expectedErrorHandling)
580 if fs.Name() != expectedName {
581 t.Errorf("unexpected name: got %s, expected %s", fs.Name(), expectedName)
583 if fs.ErrorHandling() != expectedErrorHandling {
584 t.Errorf("unexpected ErrorHandling: got %d, expected %d", fs.ErrorHandling(), expectedErrorHandling)
586 if fs.Output() != expectedOutput {
587 t.Errorf("unexpected output: got %#v, expected %#v", fs.Output(), expectedOutput)
590 expectedName = "gopher"
591 expectedErrorHandling = ExitOnError
592 expectedOutput = os.Stdout
593 fs.Init(expectedName, expectedErrorHandling)
594 fs.SetOutput(expectedOutput)
596 if fs.Name() != expectedName {
597 t.Errorf("unexpected name: got %s, expected %s", fs.Name(), expectedName)
599 if fs.ErrorHandling() != expectedErrorHandling {
600 t.Errorf("unexpected ErrorHandling: got %d, expected %d", fs.ErrorHandling(), expectedErrorHandling)
602 if fs.Output() != expectedOutput {
603 t.Errorf("unexpected output: got %v, expected %v", fs.Output(), expectedOutput)
607 func TestParseError(t *testing.T) {
608 for _, typ := range []string{"bool", "int", "int64", "uint", "uint64", "float64", "duration"} {
609 fs := NewFlagSet("parse error test", ContinueOnError)
610 fs.SetOutput(io.Discard)
611 _ = fs.Bool("bool", false, "")
612 _ = fs.Int("int", 0, "")
613 _ = fs.Int64("int64", 0, "")
614 _ = fs.Uint("uint", 0, "")
615 _ = fs.Uint64("uint64", 0, "")
616 _ = fs.Float64("float64", 0, "")
617 _ = fs.Duration("duration", 0, "")
618 // Strings cannot give errors.
619 args := []string{"-" + typ + "=x"}
620 err := fs.Parse(args) // x is not a valid setting for any flag.
622 t.Errorf("Parse(%q)=%v; expected parse error", args, err)
625 if !strings.Contains(err.Error(), "invalid") || !strings.Contains(err.Error(), "parse error") {
626 t.Errorf("Parse(%q)=%v; expected parse error", args, err)
631 func TestRangeError(t *testing.T) {
633 "-int=123456789012345678901",
634 "-int64=123456789012345678901",
635 "-uint=123456789012345678901",
636 "-uint64=123456789012345678901",
639 for _, arg := range bad {
640 fs := NewFlagSet("parse error test", ContinueOnError)
641 fs.SetOutput(io.Discard)
642 _ = fs.Int("int", 0, "")
643 _ = fs.Int64("int64", 0, "")
644 _ = fs.Uint("uint", 0, "")
645 _ = fs.Uint64("uint64", 0, "")
646 _ = fs.Float64("float64", 0, "")
647 // Strings cannot give errors, and bools and durations do not return strconv.NumError.
648 err := fs.Parse([]string{arg})
650 t.Errorf("Parse(%q)=%v; expected range error", arg, err)
653 if !strings.Contains(err.Error(), "invalid") || !strings.Contains(err.Error(), "value out of range") {
654 t.Errorf("Parse(%q)=%v; expected range error", arg, err)
659 func TestExitCode(t *testing.T) {
660 testenv.MustHaveExec(t)
663 if os.Getenv("GO_CHILD_FLAG") != "" {
664 fs := NewFlagSet("test", ExitOnError)
665 if os.Getenv("GO_CHILD_FLAG_HANDLE") != "" {
667 fs.BoolVar(&b, os.Getenv("GO_CHILD_FLAG_HANDLE"), false, "")
669 fs.Parse([]string{os.Getenv("GO_CHILD_FLAG")})
702 for _, test := range tests {
703 cmd := exec.Command(os.Args[0], "-test.run=TestExitCode")
706 "GO_CHILD_FLAG="+test.flag,
707 "GO_CHILD_FLAG_HANDLE="+test.flagHandle,
710 got := cmd.ProcessState.ExitCode()
711 // ExitCode is either 0 or 1 on Plan 9.
712 if runtime.GOOS == "plan9" && test.expectExit != 0 {
715 if got != test.expectExit {
716 t.Errorf("unexpected exit code for test case %+v \n: got %d, expect %d",
717 test, got, test.expectExit)
722 func mustPanic(t *testing.T, testName string, expected string, f func()) {
725 switch msg := recover().(type) {
727 t.Errorf("%s\n: expected panic(%q), but did not panic", testName, expected)
730 t.Errorf("%s\n: expected panic(%q), but got panic(%q)", testName, expected, msg)
733 t.Errorf("%s\n: expected panic(%q), but got panic(%T%v)", testName, expected, msg, msg)
739 func TestInvalidFlags(t *testing.T) {
746 errorMsg: "flag \"-foo\" begins with -",
750 errorMsg: "flag \"foo=bar\" contains =",
754 for _, test := range tests {
755 testName := fmt.Sprintf("FlagSet.Var(&v, %q, \"\")", test.flag)
757 fs := NewFlagSet("", ContinueOnError)
758 buf := &strings.Builder{}
761 mustPanic(t, testName, test.errorMsg, func() {
763 fs.Var(&v, test.flag, "")
765 if msg := test.errorMsg + "\n"; msg != buf.String() {
766 t.Errorf("%s\n: unexpected output: expected %q, bug got %q", testName, msg, buf)
771 func TestRedefinedFlags(t *testing.T) {
778 errorMsg: "flag redefined: foo",
782 errorMsg: "fs flag redefined: foo",
786 for _, test := range tests {
787 testName := fmt.Sprintf("flag redefined in FlagSet(%q)", test.flagSetName)
789 fs := NewFlagSet(test.flagSetName, ContinueOnError)
790 buf := &strings.Builder{}
794 fs.Var(&v, "foo", "")
796 mustPanic(t, testName, test.errorMsg, func() {
797 fs.Var(&v, "foo", "")
799 if msg := test.errorMsg + "\n"; msg != buf.String() {
800 t.Errorf("%s\n: unexpected output: expected %q, bug got %q", testName, msg, buf)
805 func TestUserDefinedBoolFunc(t *testing.T) {
806 flags := NewFlagSet("test", ContinueOnError)
807 flags.SetOutput(io.Discard)
809 flags.BoolFunc("v", "usage", func(s string) error {
813 if err := flags.Parse([]string{"-v", "", "-v", "1", "-v=2"}); err != nil {
817 t.Fatalf("got %d args; want 1 arg", len(ss))
820 if got := fmt.Sprint(ss); got != want {
821 t.Errorf("got %q; want %q", got, want)
824 var buf strings.Builder
825 flags.SetOutput(&buf)
826 flags.Parse([]string{"-h"})
827 if usage := buf.String(); !strings.Contains(usage, "usage") {
828 t.Errorf("usage string not included: %q", usage)
830 // test BoolFunc error
831 flags = NewFlagSet("test", ContinueOnError)
832 flags.SetOutput(io.Discard)
833 flags.BoolFunc("v", "usage", func(s string) error {
834 return fmt.Errorf("test error")
836 // flag not set, so no error
837 if err := flags.Parse(nil); err != nil {
840 // flag set, expect error
841 if err := flags.Parse([]string{"-v", ""}); err == nil {
842 t.Error("got err == nil; want err != nil")
843 } else if errMsg := err.Error(); !strings.Contains(errMsg, "test error") {
844 t.Errorf(`got %q; error should contain "test error"`, errMsg)