...

Source file src/flag/flag_test.go

Documentation: flag

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

View as plain text