github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/fla9/fla9_test.go (about)

     1  package fla9_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"sort"
     9  	"strings"
    10  	"syscall"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/bingoohuang/gg/pkg/fla9"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  func ExampleFlag() {
    19  	var (
    20  		name        string
    21  		addr        []string
    22  		xddr        []string
    23  		age         int
    24  		length      float64
    25  		female      bool
    26  		connections int
    27  		v           int
    28  		p           int
    29  		b1          bool
    30  		b2          bool
    31  		b3          bool
    32  		b4          bool
    33  	)
    34  
    35  	fla9.String("fla9", "testdata/test.conf", "help message")
    36  	fla9.StringVar(&name, "name,n", "", "help message")
    37  	fla9.IntVar(&age, "age,a", 0, "help message")
    38  	fla9.IntVar(&connections, "c", 0, "help message")
    39  	fla9.Float64Var(&length, "length", 0, "help message")
    40  	fla9.BoolVar(&female, "female", false, "help message")
    41  	fla9.CountVar(&v, "v", 0, "-v -vv -vvv")
    42  	fla9.CountVar(&p, "p", 0, "-p -pp -ppp")
    43  	fla9.CountVar(&p, "q", 0, "-q -qq -qqq")
    44  	fla9.BoolVar(&b1, "b1", false, "b1")
    45  	fla9.BoolVar(&b2, "b2", false, "b2")
    46  	fla9.BoolVar(&b3, "b3", false, "b3")
    47  	fla9.BoolVar(&b4, "b4", false, "b4")
    48  	fla9.StringsVar(&addr, "addr", nil, "addr")
    49  	fla9.StringsVar(&xddr, "xddr", nil, "xddr")
    50  
    51  	fla9.CommandLine.Parse([]string{"-v", "-pp", "-b1", "false", "-b2", "true", "-b3", "-b4=true", "-addr=nj", "-addr=bj"})
    52  
    53  	fmt.Println("connections:", connections)
    54  	fmt.Println("length:", length)
    55  	fmt.Println("age:", age)
    56  	fmt.Println("name:", name)
    57  	fmt.Println("female:", female)
    58  	fmt.Println("v:", v)
    59  	fmt.Println("p:", p)
    60  	fmt.Println("q:", 0)
    61  	fmt.Println("b:", b1, b2, b3, b4)
    62  	fmt.Println("addr:", addr)
    63  	fmt.Println("xddr:", xddr)
    64  
    65  	// Output:
    66  	// connections: 12345
    67  	// length: 175.5
    68  	// age: 2
    69  	// name: Gloria
    70  	// female: false
    71  	// v: 1
    72  	// p: 2
    73  	// q: 0
    74  	// b: false true true true
    75  	// addr: [nj bj]
    76  	// xddr: []
    77  }
    78  
    79  /*
    80  func TestConfSampleFileCreated(t *testing.T) {
    81  	var (
    82  		name   string
    83  		age    int
    84  		length float64
    85  		female bool
    86  	)
    87  
    88  	_ = os.Remove("testdata/a.conf.sample")
    89  	fla9.String("conf", "testdata/a.conf", "help message")
    90  	fla9.StringVar(&name, "name", "", "help message")
    91  	fla9.IntVar(&age, "age", 0, "help message")
    92  	fla9.Float64Var(&length, "length", 0, "help message")
    93  	fla9.BoolVar(&female, "female", false, "help message")
    94  
    95  	fla9.Parse()
    96  
    97  	_, err := os.Stat("testdata/a.conf.sample")
    98  	assert.Nil(t, err)
    99  }
   100  */
   101  
   102  // Example 1: A single string flag called "species" with default value "gopher".
   103  var species = fla9.String("species", "gopher", "the species we are studying")
   104  
   105  // Example 2: Two flags sharing a variable, so we can have a shorthand.
   106  // The order of initialization is undefined, so make sure both use the
   107  // same default value. They must be set up with an init function.
   108  var gopherType string
   109  
   110  func init() {
   111  	const (
   112  		defaultGopher = "pocket"
   113  		usage         = "the variety of gopher"
   114  	)
   115  	fla9.StringVar(&gopherType, "gopher_type", defaultGopher, usage)
   116  	fla9.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)")
   117  }
   118  
   119  // Example 3: A user-defined flag type, a slice of durations.
   120  type interval []time.Duration
   121  
   122  // String is the method to format the flag's value, part of the flag.Value interface.
   123  // The String method's output will be used in diagnostics.
   124  func (i *interval) String() string {
   125  	return fmt.Sprint(*i)
   126  }
   127  
   128  // Set is the method to set the flag value, part of the flag.Value interface.
   129  // Set's argument is a string to be parsed to set the flag.
   130  // It's a comma-separated list, so we split it.
   131  func (i *interval) Set(value string) error {
   132  	// If we wanted to allow the flag to be set multiple times,
   133  	// accumulating values, we would delete this if statement.
   134  	// That would permit usages such as
   135  	//	-deltaT 10s -deltaT 15s
   136  	// and other combinations.
   137  	if len(*i) > 0 {
   138  		return errors.New("interval flag already set")
   139  	}
   140  	for _, dt := range strings.Split(value, ",") {
   141  		duration, err := time.ParseDuration(dt)
   142  		if err != nil {
   143  			return err
   144  		}
   145  		*i = append(*i, duration)
   146  	}
   147  	return nil
   148  }
   149  
   150  // Define a flag to accumulate durations. Because it has a special type,
   151  // we need to use the Var function and therefore create the flag during
   152  // init.
   153  
   154  var intervalFlag interval
   155  
   156  func init() {
   157  	// Tie the command-line flag to the intervalFlag variable and
   158  	// set a usage message.
   159  	fla9.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events")
   160  }
   161  
   162  func Example() {
   163  	// All the interesting pieces are with the variables declared above, but
   164  	// to enable the flag package to see the flags defined there, one must
   165  	// execute, typically at the start of main (not init!):
   166  	//	flag.Parse()
   167  	// We don't run it here because this is not a main function and
   168  	// the testing suite has already parsed the flags.
   169  }
   170  
   171  // Additional routines compiled into the package only during testing.
   172  
   173  // ResetForTesting clears all flag state and sets the usage function as directed.
   174  // After calling ResetForTesting, parse errors in flag handling will not
   175  // exit the program.
   176  func ResetForTesting(usage func()) {
   177  	fla9.CommandLine = fla9.NewFlagSet(os.Args[0], fla9.ContinueOnError)
   178  	fla9.Usage = usage
   179  	fla9.CommandLine.Usage = usage
   180  }
   181  
   182  func boolString(s string) string {
   183  	if s == "0" {
   184  		return "false"
   185  	}
   186  	return "true"
   187  }
   188  
   189  func TestEverything(t *testing.T) {
   190  	ResetForTesting(nil)
   191  	fla9.Bool("test_bool", false, "bool value")
   192  	fla9.Int("test_int", 0, "int value")
   193  	fla9.Int64("test_int64", 0, "int64 value")
   194  	fla9.Uint("test_uint", 0, "uint value")
   195  	fla9.Uint64("test_uint64", 0, "uint64 value")
   196  	fla9.String("test_string", "0", "string value")
   197  	fla9.Float64("test_float64", 0, "float64 value")
   198  	fla9.Duration("test_duration", 0, "time.Duration value")
   199  
   200  	m := make(map[string]*fla9.Flag)
   201  	desired := "0"
   202  	visitor := func(f *fla9.Flag) {
   203  		if len(f.Name) > 5 && f.Name[0:5] == "test_" {
   204  			m[f.Name] = f
   205  			ok := false
   206  			switch {
   207  			case f.Value.String() == desired:
   208  				ok = true
   209  			case f.Name == "test_bool" && f.Value.String() == boolString(desired):
   210  				ok = true
   211  			case f.Name == "test_duration" && f.Value.String() == desired+"s":
   212  				ok = true
   213  			}
   214  			if !ok {
   215  				t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
   216  			}
   217  		}
   218  	}
   219  	fla9.VisitAll(visitor)
   220  	if len(m) != 8 {
   221  		t.Error("VisitAll misses some flags")
   222  		for k, v := range m {
   223  			t.Log(k, *v)
   224  		}
   225  	}
   226  	m = make(map[string]*fla9.Flag)
   227  	fla9.Visit(visitor)
   228  	if len(m) != 0 {
   229  		t.Errorf("Visit sees unset flags")
   230  		for k, v := range m {
   231  			t.Log(k, *v)
   232  		}
   233  	}
   234  	// Now set all flags
   235  	fla9.Set("test_bool", "true")
   236  	fla9.Set("test_int", "1")
   237  	fla9.Set("test_int64", "1")
   238  	fla9.Set("test_uint", "1")
   239  	fla9.Set("test_uint64", "1")
   240  	fla9.Set("test_string", "1")
   241  	fla9.Set("test_float64", "1")
   242  	fla9.Set("test_duration", "1s")
   243  	desired = "1"
   244  	fla9.Visit(visitor)
   245  	if len(m) != 8 {
   246  		t.Error("Visit fails after set")
   247  		for k, v := range m {
   248  			t.Log(k, *v)
   249  		}
   250  	}
   251  	// Now test they're visited in sort order.
   252  	var flagNames []string
   253  	fla9.Visit(func(f *fla9.Flag) { flagNames = append(flagNames, f.Name) })
   254  	if !sort.StringsAreSorted(flagNames) {
   255  		t.Errorf("flag names not sorted: %v", flagNames)
   256  	}
   257  }
   258  
   259  func TestGet(t *testing.T) {
   260  	ResetForTesting(nil)
   261  	fla9.Bool("test_bool", true, "bool value")
   262  	fla9.Int("test_int", 1, "int value")
   263  	fla9.Int64("test_int64", 2, "int64 value")
   264  	fla9.Uint("test_uint", 3, "uint value")
   265  	fla9.Uint64("test_uint64", 4, "uint64 value")
   266  	fla9.String("test_string", "5", "string value")
   267  	fla9.Float64("test_float64", 6, "float64 value")
   268  	fla9.Duration("test_duration", 7, "time.Duration value")
   269  
   270  	visitor := func(f *fla9.Flag) {
   271  		if len(f.Name) > 5 && f.Name[0:5] == "test_" {
   272  			g, ok := f.Value.(fla9.Getter)
   273  			if !ok {
   274  				t.Errorf("Visit: value does not satisfy Getter: %T", f.Value)
   275  				return
   276  			}
   277  			switch f.Name {
   278  			case "test_bool":
   279  				ok = g.Get() == true
   280  			case "test_int":
   281  				ok = g.Get() == int(1)
   282  			case "test_int64":
   283  				ok = g.Get() == int64(2)
   284  			case "test_uint":
   285  				ok = g.Get() == uint(3)
   286  			case "test_uint64":
   287  				ok = g.Get() == uint64(4)
   288  			case "test_string":
   289  				ok = g.Get() == "5"
   290  			case "test_float64":
   291  				ok = g.Get() == float64(6)
   292  			case "test_duration":
   293  				ok = g.Get() == time.Duration(7)
   294  			}
   295  			if !ok {
   296  				t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), f.Name)
   297  			}
   298  		}
   299  	}
   300  	fla9.VisitAll(visitor)
   301  }
   302  
   303  func TestUsage(t *testing.T) {
   304  	called := false
   305  	ResetForTesting(func() { called = true })
   306  	if fla9.CommandLine.Parse([]string{"-x"}) == nil {
   307  		t.Error("parse did not fail for unknown flag")
   308  	}
   309  	if !called {
   310  		t.Error("did not call Usage for unknown flag")
   311  	}
   312  }
   313  
   314  func testParse(f *fla9.FlagSet, t *testing.T) {
   315  	if f.Parsed() {
   316  		t.Error("f.Parse() = true before Parse")
   317  	}
   318  	boolFlag := f.Bool("bool", false, "bool value")
   319  	bool2Flag := f.Bool("bool2", false, "bool2 value")
   320  	intFlag := f.Int("int", 0, "int value")
   321  	int64Flag := f.Int64("int64", 0, "int64 value")
   322  	uintFlag := f.Uint("uint", 0, "uint value")
   323  	uint64Flag := f.Uint64("uint64", 0, "uint64 value")
   324  	stringFlag := f.String("string", "0", "string value")
   325  	float64Flag := f.Float64("float64", 0, "float64 value")
   326  	durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
   327  	extra := "one-extra-argument"
   328  	args := []string{
   329  		"-bool",
   330  		"-bool2=true",
   331  		"--int", "22",
   332  		"--int64", "0x23",
   333  		"-uint", "24",
   334  		"--uint64", "25",
   335  		"-string", "hello",
   336  		"-float64", "2718e28",
   337  		"-duration", "2m",
   338  		extra,
   339  	}
   340  	if err := f.Parse(args); err != nil {
   341  		t.Fatal(err)
   342  	}
   343  	if !f.Parsed() {
   344  		t.Error("f.Parse() = false after Parse")
   345  	}
   346  	if *boolFlag != true {
   347  		t.Error("bool flag should be true, is ", *boolFlag)
   348  	}
   349  	if *bool2Flag != true {
   350  		t.Error("bool2 flag should be true, is ", *bool2Flag)
   351  	}
   352  	if *intFlag != 22 {
   353  		t.Error("int flag should be 22, is ", *intFlag)
   354  	}
   355  	if *int64Flag != 0x23 {
   356  		t.Error("int64 flag should be 0x23, is ", *int64Flag)
   357  	}
   358  	if *uintFlag != 24 {
   359  		t.Error("uint flag should be 24, is ", *uintFlag)
   360  	}
   361  	if *uint64Flag != 25 {
   362  		t.Error("uint64 flag should be 25, is ", *uint64Flag)
   363  	}
   364  	if *stringFlag != "hello" {
   365  		t.Error("string flag should be `hello`, is ", *stringFlag)
   366  	}
   367  	if *float64Flag != 2718e28 {
   368  		t.Error("float64 flag should be 2718e28, is ", *float64Flag)
   369  	}
   370  	if *durationFlag != 2*time.Minute {
   371  		t.Error("duration flag should be 2m, is ", *durationFlag)
   372  	}
   373  	if len(f.Args()) != 1 {
   374  		t.Error("expected one argument, got", len(f.Args()))
   375  	} else if f.Args()[0] != extra {
   376  		t.Errorf("expected argument %q got %q", extra, f.Args()[0])
   377  	}
   378  }
   379  
   380  func TestParse(t *testing.T) {
   381  	ResetForTesting(func() { t.Error("bad parse") })
   382  	testParse(fla9.CommandLine, t)
   383  }
   384  
   385  func TestFlagSetParse(t *testing.T) {
   386  	testParse(fla9.NewFlagSet("test", fla9.ContinueOnError), t)
   387  }
   388  
   389  // Declare a user-defined flag type.
   390  type flagVar []string
   391  
   392  func (f *flagVar) String() string {
   393  	return fmt.Sprint([]string(*f))
   394  }
   395  
   396  func (f *flagVar) Set(value string) error {
   397  	*f = append(*f, value)
   398  	return nil
   399  }
   400  
   401  func TestUserDefined(t *testing.T) {
   402  	var flags fla9.FlagSet
   403  	flags.Init("test", fla9.ContinueOnError)
   404  	var v flagVar
   405  	flags.Var(&v, "v", "usage")
   406  	if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
   407  		t.Error(err)
   408  	}
   409  	if len(v) != 3 {
   410  		t.Fatal("expected 3 args; got ", len(v))
   411  	}
   412  	expect := "[1 2 3]"
   413  	if v.String() != expect {
   414  		t.Errorf("expected value %q got %q", expect, v.String())
   415  	}
   416  }
   417  
   418  func TestUserDefinedForCommandLine(t *testing.T) {
   419  	const help = "HELP"
   420  	var result string
   421  	ResetForTesting(func() { result = help })
   422  	fla9.Usage()
   423  	if result != help {
   424  		t.Fatalf("got %q; expected %q", result, help)
   425  	}
   426  }
   427  
   428  // Declare a user-defined boolean flag type.
   429  type boolFlagVar struct {
   430  	count int
   431  }
   432  
   433  func (b *boolFlagVar) String() string {
   434  	return fmt.Sprintf("%d", b.count)
   435  }
   436  
   437  func (b *boolFlagVar) Set(value string) error {
   438  	if value == "true" {
   439  		b.count++
   440  	}
   441  	return nil
   442  }
   443  
   444  func (b *boolFlagVar) IsBoolFlag() bool {
   445  	return b.count < 4
   446  }
   447  
   448  func TestUserDefinedBool(t *testing.T) {
   449  	var flags fla9.FlagSet
   450  	flags.Init("test", fla9.ContinueOnError)
   451  	var b boolFlagVar
   452  	var err error
   453  	flags.Var(&b, "b", "usage")
   454  	if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil {
   455  		if b.count < 4 {
   456  			t.Error(err)
   457  		}
   458  	}
   459  
   460  	if b.count != 4 {
   461  		t.Errorf("want: %d; got: %d", 4, b.count)
   462  	}
   463  
   464  	if err == nil {
   465  		t.Error("expected error; got none")
   466  	}
   467  }
   468  
   469  func TestSetOutput(t *testing.T) {
   470  	var flags fla9.FlagSet
   471  	var buf bytes.Buffer
   472  	flags.SetOutput(&buf)
   473  	flags.Init("test", fla9.ContinueOnError)
   474  	flags.Parse([]string{"-unknown"})
   475  	if out := buf.String(); !strings.Contains(out, "-unknown") {
   476  		t.Logf("expected output mentioning unknown; got %q", out)
   477  	}
   478  }
   479  
   480  // This tests that one can reset the flags. This still works but not well, and is
   481  // superseded by FlagSet.
   482  func TestChangingArgs(t *testing.T) {
   483  	ResetForTesting(func() { t.Fatal("bad parse") })
   484  	oldArgs := os.Args
   485  	defer func() { os.Args = oldArgs }()
   486  	os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
   487  	before := fla9.Bool("before", false, "")
   488  	if err := fla9.CommandLine.Parse(os.Args[1:]); err != nil {
   489  		t.Fatal(err)
   490  	}
   491  	cmd := fla9.Arg(0)
   492  	os.Args = fla9.Args()
   493  	after := fla9.Bool("after", false, "")
   494  	fla9.Parse()
   495  	args := fla9.Args()
   496  
   497  	if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
   498  		t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
   499  	}
   500  }
   501  
   502  // Test that -help invokes the usage message and returns ErrHelp.
   503  func TestHelp(t *testing.T) {
   504  	helpCalled := false
   505  	fs := fla9.NewFlagSet("help test", fla9.ContinueOnError)
   506  	fs.Usage = func() { helpCalled = true }
   507  	var fla bool
   508  	fs.BoolVar(&fla, "fla", false, "regular fla")
   509  	// Regular fla invocation should work
   510  	err := fs.Parse([]string{"-fla=true"})
   511  	if err != nil {
   512  		t.Fatal("expected no error; got ", err)
   513  	}
   514  	if !fla {
   515  		t.Error("fla was not set by -fla")
   516  	}
   517  	if helpCalled {
   518  		t.Error("help called for regular fla")
   519  		helpCalled = false // reset for next test
   520  	}
   521  	// Help fla should work as expected.
   522  	err = fs.Parse([]string{"-help"})
   523  	if err == nil {
   524  		t.Fatal("error expected")
   525  	}
   526  	if err != fla9.ErrHelp {
   527  		t.Fatal("expected ErrHelp; got ", err)
   528  	}
   529  	if !helpCalled {
   530  		t.Fatal("help was not called")
   531  	}
   532  	// If we define a help fla, that should override.
   533  	var help bool
   534  	fs.BoolVar(&help, "help", false, "help fla")
   535  	helpCalled = false
   536  	err = fs.Parse([]string{"-help"})
   537  	if err != nil {
   538  		t.Fatal("expected no error for defined -help; got ", err)
   539  	}
   540  	if helpCalled {
   541  		t.Fatal("help was called; should not have been for defined help fla")
   542  	}
   543  }
   544  
   545  const defaultOutput = `  -A	for bootstrapping, allow 'any' type
   546    -Alongflagname
   547      	disable bounds checking
   548    -C	a boolean defaulting to true (default true)
   549    -D path
   550      	set relative path for local imports
   551    -F number
   552      	a non-zero number (default 2.7)
   553    -G float
   554      	a float that defaults to zero
   555    -N int
   556      	a non-zero int (default 27)
   557    -Z int
   558      	an int that defaults to zero
   559    -maxT timeout
   560      	set timeout for dial
   561  `
   562  
   563  func TestPrintDefaults(t *testing.T) {
   564  	fs := fla9.NewFlagSet("print defaults test", fla9.ContinueOnError)
   565  	var buf bytes.Buffer
   566  	fs.SetOutput(&buf)
   567  	fs.Bool("A", false, "for bootstrapping, allow 'any' type")
   568  	fs.Bool("Alongflagname", false, "disable bounds checking")
   569  	fs.Bool("C", true, "a boolean defaulting to true")
   570  	fs.String("D", "", "set relative `path` for local imports")
   571  	fs.Float64("F", 2.7, "a non-zero `number`")
   572  	fs.Float64("G", 0, "a float that defaults to zero")
   573  	fs.Int("N", 27, "a non-zero int")
   574  	fs.Int("Z", 0, "an int that defaults to zero")
   575  	fs.Duration("maxT", 0, "set `timeout` for dial")
   576  	fs.PrintDefaults()
   577  	got := buf.String()
   578  	assert.Equal(t, defaultOutput, got)
   579  }
   580  
   581  // Test parsing a environment variables
   582  func TestParseEnv(t *testing.T) {
   583  	syscall.Setenv("BOOL", "")
   584  	syscall.Setenv("BOOL2", "true")
   585  	syscall.Setenv("INT", "22")
   586  	syscall.Setenv("INT64", "0x23")
   587  	syscall.Setenv("UINT", "24")
   588  	syscall.Setenv("UINT64", "25")
   589  	syscall.Setenv("STRING", "hello")
   590  	syscall.Setenv("FLOAT64", "2718e28")
   591  	syscall.Setenv("DURATION", "2m")
   592  
   593  	f := fla9.NewFlagSet(os.Args[0], fla9.ContinueOnError)
   594  
   595  	boolFlag := f.Bool("bool", false, "bool value")
   596  	bool2Flag := f.Bool("bool2", false, "bool2 value")
   597  	intFlag := f.Int("int", 0, "int value")
   598  	int64Flag := f.Int64("int64", 0, "int64 value")
   599  	uintFlag := f.Uint("uint", 0, "uint value")
   600  	uint64Flag := f.Uint64("uint64", 0, "uint64 value")
   601  	stringFlag := f.String("string", "0", "string value")
   602  	float64Flag := f.Float64("float64", 0, "float64 value")
   603  	durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
   604  
   605  	err := f.ParseEnv(os.Environ())
   606  	if err != nil {
   607  		t.Fatal("expected no error; got ", err)
   608  	}
   609  	if *boolFlag != true {
   610  		t.Error("bool flag should be true, is ", *boolFlag)
   611  	}
   612  	if *bool2Flag != true {
   613  		t.Error("bool2 flag should be true, is ", *bool2Flag)
   614  	}
   615  	if *intFlag != 22 {
   616  		t.Error("int flag should be 22, is ", *intFlag)
   617  	}
   618  	if *int64Flag != 0x23 {
   619  		t.Error("int64 flag should be 0x23, is ", *int64Flag)
   620  	}
   621  	if *uintFlag != 24 {
   622  		t.Error("uint flag should be 24, is ", *uintFlag)
   623  	}
   624  	if *uint64Flag != 25 {
   625  		t.Error("uint64 flag should be 25, is ", *uint64Flag)
   626  	}
   627  	if *stringFlag != "hello" {
   628  		t.Error("string flag should be `hello`, is ", *stringFlag)
   629  	}
   630  	if *float64Flag != 2718e28 {
   631  		t.Error("float64 flag should be 2718e28, is ", *float64Flag)
   632  	}
   633  	if *durationFlag != 2*time.Minute {
   634  		t.Error("duration flag should be 2m, is ", *durationFlag)
   635  	}
   636  }
   637  
   638  // Test parsing a configuration file
   639  func TestParseFile(t *testing.T) {
   640  	f := fla9.NewFlagSet(os.Args[0], fla9.ContinueOnError)
   641  
   642  	boolFlag := f.Bool("bool", false, "bool value")
   643  	bool2Flag := f.Bool("bool2", false, "bool2 value")
   644  	intFlag := f.Int("int", 0, "int value")
   645  	int64Flag := f.Int64("int64", 0, "int64 value")
   646  	uintFlag := f.Uint("uint", 0, "uint value")
   647  	uint64Flag := f.Uint64("uint64", 0, "uint64 value")
   648  	stringFlag := f.String("string", "0", "string value")
   649  	float64Flag := f.Float64("float64", 0, "float64 value")
   650  	durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
   651  
   652  	err := f.ParseFile("./testdata/test.conf", true)
   653  	if err != nil {
   654  		t.Fatal("expected no error; got ", err)
   655  	}
   656  	if *boolFlag != true {
   657  		t.Error("bool flag should be true, is ", *boolFlag)
   658  	}
   659  	if *bool2Flag != true {
   660  		t.Error("bool2 flag should be true, is ", *bool2Flag)
   661  	}
   662  	if *intFlag != 22 {
   663  		t.Error("int flag should be 22, is ", *intFlag)
   664  	}
   665  	if *int64Flag != 0x23 {
   666  		t.Error("int64 flag should be 0x23, is ", *int64Flag)
   667  	}
   668  	if *uintFlag != 24 {
   669  		t.Error("uint flag should be 24, is ", *uintFlag)
   670  	}
   671  	if *uint64Flag != 25 {
   672  		t.Error("uint64 flag should be 25, is ", *uint64Flag)
   673  	}
   674  	if *stringFlag != "hello" {
   675  		t.Error("string flag should be `hello`, is ", *stringFlag)
   676  	}
   677  	if *float64Flag != 2718e28 {
   678  		t.Error("float64 flag should be 2718e28, is ", *float64Flag)
   679  	}
   680  	if *durationFlag != 2*time.Minute {
   681  		t.Error("duration flag should be 2m, is ", *durationFlag)
   682  	}
   683  }
   684  
   685  func TestParseFileUnknownFlag(t *testing.T) {
   686  	f := fla9.NewFlagSet("test", fla9.ContinueOnError)
   687  	if err := f.ParseFile("testdata/bad_test.conf", false); err == nil {
   688  		t.Error("parse did not fail for unknown flag; ", err)
   689  	}
   690  }
   691  
   692  func TestDefaultConfigFlagname(t *testing.T) {
   693  	f := fla9.NewFlagSet("test", fla9.ContinueOnError)
   694  
   695  	f.Bool("bool", false, "bool value")
   696  	f.Bool("bool2", false, "bool2 value")
   697  	f.Int("int", 0, "int value")
   698  	f.Int64("int64", 0, "int64 value")
   699  	f.Uint("uint", 0, "uint value")
   700  	f.Uint64("uint64", 0, "uint64 value")
   701  	stringFlag := f.String("string", "0", "string value")
   702  	f.Float64("float64", 0, "float64 value")
   703  	f.Duration("duration", 5*time.Second, "time.Duration value")
   704  
   705  	f.String(fla9.DefaultConFlagName, "./testdata/test.conf", "config path")
   706  
   707  	if err := os.Unsetenv("STRING"); err != nil {
   708  		t.Error(err)
   709  	}
   710  
   711  	if err := f.Parse([]string{}); err != nil {
   712  		t.Error("parse failed; ", err)
   713  	}
   714  
   715  	if *stringFlag != "hello" {
   716  		t.Error("string flag should be `hello`, is", *stringFlag)
   717  	}
   718  }
   719  
   720  func TestDefaultConfigFlagnameMissingFile(t *testing.T) {
   721  	f := fla9.NewFlagSet("test", fla9.ContinueOnError)
   722  	f.String(fla9.DefaultConFlagName, "./testdata/missing", "config path")
   723  
   724  	if err := os.Unsetenv("STRING"); err != nil {
   725  		t.Error(err)
   726  	}
   727  	if err := f.Parse([]string{}); err == nil {
   728  		t.Error("expected error of missing config file, got nil")
   729  	}
   730  }
   731  
   732  func TestFlagSetParseErrors(t *testing.T) {
   733  	fs := fla9.NewFlagSet("test", fla9.ContinueOnError)
   734  	fs.Int("int", 0, "int value")
   735  
   736  	args := []string{"-int", "bad"}
   737  	expected := `invalid value "bad" for flag -int: strconv.ParseInt: parsing "bad": invalid syntax`
   738  	if err := fs.Parse(args); err == nil || err.Error() != expected {
   739  		t.Errorf("expected error %q parsing from args, got: %v", expected, err)
   740  	}
   741  
   742  	if err := os.Setenv("INT", "bad"); err != nil {
   743  		t.Fatalf("error setting env: %s", err.Error())
   744  	}
   745  	expected = `invalid value "bad" for environment variable int: strconv.ParseInt: parsing "bad": invalid syntax`
   746  	if err := fs.Parse([]string{}); err == nil || err.Error() != expected {
   747  		t.Errorf("expected error %q parsing from env, got: %v", expected, err)
   748  	}
   749  	if err := os.Unsetenv("INT"); err != nil {
   750  		t.Fatalf("error unsetting env: %s", err.Error())
   751  	}
   752  
   753  	fs.String("conf", "", "config filename")
   754  	args = []string{"-conf", "testdata/bad_test.conf"}
   755  	expected = `invalid value "bad" for configuration variable int: strconv.ParseInt: parsing "bad": invalid syntax`
   756  	if err := fs.Parse(args); err == nil || err.Error() != expected {
   757  		t.Errorf("expected error %q parsing from config, got: %v", expected, err)
   758  	}
   759  }
   760  
   761  func TestTestingPackageFlags(t *testing.T) {
   762  	f := fla9.NewFlagSet("test", fla9.ContinueOnError)
   763  	if err := f.Parse([]string{"-test.v", "-test.count", "1"}); err != nil {
   764  		t.Error(err)
   765  	}
   766  }