github.com/fcwu/docker@v1.4.2-0.20150115145920-2a69ca89f0df/pkg/mflag/flag_test.go (about)

     1  // Copyright 2014 The Docker & 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 mflag_test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	. "github.com/docker/docker/pkg/mflag"
    11  	"os"
    12  	"sort"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  )
    17  
    18  // ResetForTesting clears all flag state and sets the usage function as directed.
    19  // After calling ResetForTesting, parse errors in flag handling will not
    20  // exit the program.
    21  func ResetForTesting(usage func()) {
    22  	CommandLine = NewFlagSet(os.Args[0], ContinueOnError)
    23  	Usage = usage
    24  }
    25  func boolString(s string) string {
    26  	if s == "0" {
    27  		return "false"
    28  	}
    29  	return "true"
    30  }
    31  
    32  func TestEverything(t *testing.T) {
    33  	ResetForTesting(nil)
    34  	Bool([]string{"test_bool"}, false, "bool value")
    35  	Int([]string{"test_int"}, 0, "int value")
    36  	Int64([]string{"test_int64"}, 0, "int64 value")
    37  	Uint([]string{"test_uint"}, 0, "uint value")
    38  	Uint64([]string{"test_uint64"}, 0, "uint64 value")
    39  	String([]string{"test_string"}, "0", "string value")
    40  	Float64([]string{"test_float64"}, 0, "float64 value")
    41  	Duration([]string{"test_duration"}, 0, "time.Duration value")
    42  
    43  	m := make(map[string]*Flag)
    44  	desired := "0"
    45  	visitor := func(f *Flag) {
    46  		for _, name := range f.Names {
    47  			if len(name) > 5 && name[0:5] == "test_" {
    48  				m[name] = f
    49  				ok := false
    50  				switch {
    51  				case f.Value.String() == desired:
    52  					ok = true
    53  				case name == "test_bool" && f.Value.String() == boolString(desired):
    54  					ok = true
    55  				case name == "test_duration" && f.Value.String() == desired+"s":
    56  					ok = true
    57  				}
    58  				if !ok {
    59  					t.Error("Visit: bad value", f.Value.String(), "for", name)
    60  				}
    61  			}
    62  		}
    63  	}
    64  	VisitAll(visitor)
    65  	if len(m) != 8 {
    66  		t.Error("VisitAll misses some flags")
    67  		for k, v := range m {
    68  			t.Log(k, *v)
    69  		}
    70  	}
    71  	m = make(map[string]*Flag)
    72  	Visit(visitor)
    73  	if len(m) != 0 {
    74  		t.Errorf("Visit sees unset flags")
    75  		for k, v := range m {
    76  			t.Log(k, *v)
    77  		}
    78  	}
    79  	// Now set all flags
    80  	Set("test_bool", "true")
    81  	Set("test_int", "1")
    82  	Set("test_int64", "1")
    83  	Set("test_uint", "1")
    84  	Set("test_uint64", "1")
    85  	Set("test_string", "1")
    86  	Set("test_float64", "1")
    87  	Set("test_duration", "1s")
    88  	desired = "1"
    89  	Visit(visitor)
    90  	if len(m) != 8 {
    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) {
    99  		for _, name := range f.Names {
   100  			flagNames = append(flagNames, name)
   101  		}
   102  	})
   103  	if !sort.StringsAreSorted(flagNames) {
   104  		t.Errorf("flag names not sorted: %v", flagNames)
   105  	}
   106  }
   107  
   108  func TestGet(t *testing.T) {
   109  	ResetForTesting(nil)
   110  	Bool([]string{"test_bool"}, true, "bool value")
   111  	Int([]string{"test_int"}, 1, "int value")
   112  	Int64([]string{"test_int64"}, 2, "int64 value")
   113  	Uint([]string{"test_uint"}, 3, "uint value")
   114  	Uint64([]string{"test_uint64"}, 4, "uint64 value")
   115  	String([]string{"test_string"}, "5", "string value")
   116  	Float64([]string{"test_float64"}, 6, "float64 value")
   117  	Duration([]string{"test_duration"}, 7, "time.Duration value")
   118  
   119  	visitor := func(f *Flag) {
   120  		for _, name := range f.Names {
   121  			if len(name) > 5 && 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 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(), name)
   147  				}
   148  			}
   149  		}
   150  	}
   151  	VisitAll(visitor)
   152  }
   153  
   154  func testParse(f *FlagSet, t *testing.T) {
   155  	if f.Parsed() {
   156  		t.Error("f.Parse() = true before Parse")
   157  	}
   158  	boolFlag := f.Bool([]string{"bool"}, false, "bool value")
   159  	bool2Flag := f.Bool([]string{"bool2"}, false, "bool2 value")
   160  	f.Bool([]string{"bool3"}, false, "bool3 value")
   161  	bool4Flag := f.Bool([]string{"bool4"}, false, "bool4 value")
   162  	intFlag := f.Int([]string{"-int"}, 0, "int value")
   163  	int64Flag := f.Int64([]string{"-int64"}, 0, "int64 value")
   164  	uintFlag := f.Uint([]string{"uint"}, 0, "uint value")
   165  	uint64Flag := f.Uint64([]string{"-uint64"}, 0, "uint64 value")
   166  	stringFlag := f.String([]string{"string"}, "0", "string value")
   167  	f.String([]string{"string2"}, "0", "string2 value")
   168  	singleQuoteFlag := f.String([]string{"squote"}, "", "single quoted value")
   169  	doubleQuoteFlag := f.String([]string{"dquote"}, "", "double quoted value")
   170  	mixedQuoteFlag := f.String([]string{"mquote"}, "", "mixed quoted value")
   171  	mixed2QuoteFlag := f.String([]string{"mquote2"}, "", "mixed2 quoted value")
   172  	nestedQuoteFlag := f.String([]string{"nquote"}, "", "nested quoted value")
   173  	nested2QuoteFlag := f.String([]string{"nquote2"}, "", "nested2 quoted value")
   174  	float64Flag := f.Float64([]string{"float64"}, 0, "float64 value")
   175  	durationFlag := f.Duration([]string{"duration"}, 5*time.Second, "time.Duration value")
   176  	extra := "one-extra-argument"
   177  	args := []string{
   178  		"-bool",
   179  		"-bool2=true",
   180  		"-bool4=false",
   181  		"--int", "22",
   182  		"--int64", "0x23",
   183  		"-uint", "24",
   184  		"--uint64", "25",
   185  		"-string", "hello",
   186  		"-squote='single'",
   187  		`-dquote="double"`,
   188  		`-mquote='mixed"`,
   189  		`-mquote2="mixed2'`,
   190  		`-nquote="'single nested'"`,
   191  		`-nquote2='"double nested"'`,
   192  		"-float64", "2718e28",
   193  		"-duration", "2m",
   194  		extra,
   195  	}
   196  	if err := f.Parse(args); err != nil {
   197  		t.Fatal(err)
   198  	}
   199  	if !f.Parsed() {
   200  		t.Error("f.Parse() = false after Parse")
   201  	}
   202  	if *boolFlag != true {
   203  		t.Error("bool flag should be true, is ", *boolFlag)
   204  	}
   205  	if *bool2Flag != true {
   206  		t.Error("bool2 flag should be true, is ", *bool2Flag)
   207  	}
   208  	if !f.IsSet("bool2") {
   209  		t.Error("bool2 should be marked as set")
   210  	}
   211  	if f.IsSet("bool3") {
   212  		t.Error("bool3 should not be marked as set")
   213  	}
   214  	if !f.IsSet("bool4") {
   215  		t.Error("bool4 should be marked as set")
   216  	}
   217  	if *bool4Flag != false {
   218  		t.Error("bool4 flag should be false, is ", *bool4Flag)
   219  	}
   220  	if *intFlag != 22 {
   221  		t.Error("int flag should be 22, is ", *intFlag)
   222  	}
   223  	if *int64Flag != 0x23 {
   224  		t.Error("int64 flag should be 0x23, is ", *int64Flag)
   225  	}
   226  	if *uintFlag != 24 {
   227  		t.Error("uint flag should be 24, is ", *uintFlag)
   228  	}
   229  	if *uint64Flag != 25 {
   230  		t.Error("uint64 flag should be 25, is ", *uint64Flag)
   231  	}
   232  	if *stringFlag != "hello" {
   233  		t.Error("string flag should be `hello`, is ", *stringFlag)
   234  	}
   235  	if !f.IsSet("string") {
   236  		t.Error("string flag should be marked as set")
   237  	}
   238  	if f.IsSet("string2") {
   239  		t.Error("string2 flag should not be marked as set")
   240  	}
   241  	if *singleQuoteFlag != "single" {
   242  		t.Error("single quote string flag should be `single`, is ", *singleQuoteFlag)
   243  	}
   244  	if *doubleQuoteFlag != "double" {
   245  		t.Error("double quote string flag should be `double`, is ", *doubleQuoteFlag)
   246  	}
   247  	if *mixedQuoteFlag != `'mixed"` {
   248  		t.Error("mixed quote string flag should be `'mixed\"`, is ", *mixedQuoteFlag)
   249  	}
   250  	if *mixed2QuoteFlag != `"mixed2'` {
   251  		t.Error("mixed2 quote string flag should be `\"mixed2'`, is ", *mixed2QuoteFlag)
   252  	}
   253  	if *nestedQuoteFlag != "'single nested'" {
   254  		t.Error("nested quote string flag should be `'single nested'`, is ", *nestedQuoteFlag)
   255  	}
   256  	if *nested2QuoteFlag != `"double nested"` {
   257  		t.Error("double quote string flag should be `\"double nested\"`, is ", *nested2QuoteFlag)
   258  	}
   259  	if *float64Flag != 2718e28 {
   260  		t.Error("float64 flag should be 2718e28, is ", *float64Flag)
   261  	}
   262  	if *durationFlag != 2*time.Minute {
   263  		t.Error("duration flag should be 2m, is ", *durationFlag)
   264  	}
   265  	if len(f.Args()) != 1 {
   266  		t.Error("expected one argument, got", len(f.Args()))
   267  	} else if f.Args()[0] != extra {
   268  		t.Errorf("expected argument %q got %q", extra, f.Args()[0])
   269  	}
   270  }
   271  
   272  func testPanic(f *FlagSet, t *testing.T) {
   273  	f.Int([]string{"-int"}, 0, "int value")
   274  	if f.Parsed() {
   275  		t.Error("f.Parse() = true before Parse")
   276  	}
   277  	args := []string{
   278  		"-int", "21",
   279  	}
   280  	f.Parse(args)
   281  }
   282  
   283  func TestParsePanic(t *testing.T) {
   284  	ResetForTesting(func() {})
   285  	testPanic(CommandLine, t)
   286  }
   287  
   288  func TestParse(t *testing.T) {
   289  	ResetForTesting(func() { t.Error("bad parse") })
   290  	testParse(CommandLine, t)
   291  }
   292  
   293  func TestFlagSetParse(t *testing.T) {
   294  	testParse(NewFlagSet("test", ContinueOnError), t)
   295  }
   296  
   297  // Declare a user-defined flag type.
   298  type flagVar []string
   299  
   300  func (f *flagVar) String() string {
   301  	return fmt.Sprint([]string(*f))
   302  }
   303  
   304  func (f *flagVar) Set(value string) error {
   305  	*f = append(*f, value)
   306  	return nil
   307  }
   308  
   309  func TestUserDefined(t *testing.T) {
   310  	var flags FlagSet
   311  	flags.Init("test", ContinueOnError)
   312  	var v flagVar
   313  	flags.Var(&v, []string{"v"}, "usage")
   314  	if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
   315  		t.Error(err)
   316  	}
   317  	if len(v) != 3 {
   318  		t.Fatal("expected 3 args; got ", len(v))
   319  	}
   320  	expect := "[1 2 3]"
   321  	if v.String() != expect {
   322  		t.Errorf("expected value %q got %q", expect, v.String())
   323  	}
   324  }
   325  
   326  // Declare a user-defined boolean flag type.
   327  type boolFlagVar struct {
   328  	count int
   329  }
   330  
   331  func (b *boolFlagVar) String() string {
   332  	return fmt.Sprintf("%d", b.count)
   333  }
   334  
   335  func (b *boolFlagVar) Set(value string) error {
   336  	if value == "true" {
   337  		b.count++
   338  	}
   339  	return nil
   340  }
   341  
   342  func (b *boolFlagVar) IsBoolFlag() bool {
   343  	return b.count < 4
   344  }
   345  
   346  func TestUserDefinedBool(t *testing.T) {
   347  	var flags FlagSet
   348  	flags.Init("test", ContinueOnError)
   349  	var b boolFlagVar
   350  	var err error
   351  	flags.Var(&b, []string{"b"}, "usage")
   352  	if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil {
   353  		if b.count < 4 {
   354  			t.Error(err)
   355  		}
   356  	}
   357  
   358  	if b.count != 4 {
   359  		t.Errorf("want: %d; got: %d", 4, b.count)
   360  	}
   361  
   362  	if err == nil {
   363  		t.Error("expected error; got none")
   364  	}
   365  }
   366  
   367  func TestSetOutput(t *testing.T) {
   368  	var flags FlagSet
   369  	var buf bytes.Buffer
   370  	flags.SetOutput(&buf)
   371  	flags.Init("test", ContinueOnError)
   372  	flags.Parse([]string{"-unknown"})
   373  	if out := buf.String(); !strings.Contains(out, "-unknown") {
   374  		t.Logf("expected output mentioning unknown; got %q", out)
   375  	}
   376  }
   377  
   378  // This tests that one can reset the flags. This still works but not well, and is
   379  // superseded by FlagSet.
   380  func TestChangingArgs(t *testing.T) {
   381  	ResetForTesting(func() { t.Fatal("bad parse") })
   382  	oldArgs := os.Args
   383  	defer func() { os.Args = oldArgs }()
   384  	os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
   385  	before := Bool([]string{"before"}, false, "")
   386  	if err := CommandLine.Parse(os.Args[1:]); err != nil {
   387  		t.Fatal(err)
   388  	}
   389  	cmd := Arg(0)
   390  	os.Args = Args()
   391  	after := Bool([]string{"after"}, false, "")
   392  	Parse()
   393  	args := Args()
   394  
   395  	if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
   396  		t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
   397  	}
   398  }
   399  
   400  // Test that -help invokes the usage message and returns ErrHelp.
   401  func TestHelp(t *testing.T) {
   402  	var helpCalled = false
   403  	fs := NewFlagSet("help test", ContinueOnError)
   404  	fs.Usage = func() { helpCalled = true }
   405  	var flag bool
   406  	fs.BoolVar(&flag, []string{"flag"}, false, "regular flag")
   407  	// Regular flag invocation should work
   408  	err := fs.Parse([]string{"-flag=true"})
   409  	if err != nil {
   410  		t.Fatal("expected no error; got ", err)
   411  	}
   412  	if !flag {
   413  		t.Error("flag was not set by -flag")
   414  	}
   415  	if helpCalled {
   416  		t.Error("help called for regular flag")
   417  		helpCalled = false // reset for next test
   418  	}
   419  	// Help flag should work as expected.
   420  	err = fs.Parse([]string{"-help"})
   421  	if err == nil {
   422  		t.Fatal("error expected")
   423  	}
   424  	if err != ErrHelp {
   425  		t.Fatal("expected ErrHelp; got ", err)
   426  	}
   427  	if !helpCalled {
   428  		t.Fatal("help was not called")
   429  	}
   430  	// If we define a help flag, that should override.
   431  	var help bool
   432  	fs.BoolVar(&help, []string{"help"}, false, "help flag")
   433  	helpCalled = false
   434  	err = fs.Parse([]string{"-help"})
   435  	if err != nil {
   436  		t.Fatal("expected no error for defined -help; got ", err)
   437  	}
   438  	if helpCalled {
   439  		t.Fatal("help was called; should not have been for defined help flag")
   440  	}
   441  }
   442  
   443  // Test the flag count functions.
   444  func TestFlagCounts(t *testing.T) {
   445  	fs := NewFlagSet("help test", ContinueOnError)
   446  	var flag bool
   447  	fs.BoolVar(&flag, []string{"flag1"}, false, "regular flag")
   448  	fs.BoolVar(&flag, []string{"#deprecated1"}, false, "regular flag")
   449  	fs.BoolVar(&flag, []string{"f", "flag2"}, false, "regular flag")
   450  	fs.BoolVar(&flag, []string{"#d", "#deprecated2"}, false, "regular flag")
   451  	fs.BoolVar(&flag, []string{"flag3"}, false, "regular flag")
   452  	fs.BoolVar(&flag, []string{"g", "#flag4", "-flag4"}, false, "regular flag")
   453  
   454  	if fs.FlagCount() != 6 {
   455  		t.Fatal("FlagCount wrong. ", fs.FlagCount())
   456  	}
   457  	if fs.FlagCountUndeprecated() != 4 {
   458  		t.Fatal("FlagCountUndeprecated wrong. ", fs.FlagCountUndeprecated())
   459  	}
   460  	if fs.NFlag() != 0 {
   461  		t.Fatal("NFlag wrong. ", fs.NFlag())
   462  	}
   463  	err := fs.Parse([]string{"-fd", "-g", "-flag4"})
   464  	if err != nil {
   465  		t.Fatal("expected no error for defined -help; got ", err)
   466  	}
   467  	if fs.NFlag() != 4 {
   468  		t.Fatal("NFlag wrong. ", fs.NFlag())
   469  	}
   470  }
   471  
   472  // Show up bug in sortFlags
   473  func TestSortFlags(t *testing.T) {
   474  	fs := NewFlagSet("help TestSortFlags", ContinueOnError)
   475  
   476  	var err error
   477  
   478  	var b bool
   479  	fs.BoolVar(&b, []string{"b", "-banana"}, false, "usage")
   480  
   481  	err = fs.Parse([]string{"--banana=true"})
   482  	if err != nil {
   483  		t.Fatal("expected no error; got ", err)
   484  	}
   485  
   486  	count := 0
   487  
   488  	fs.VisitAll(func(flag *Flag) {
   489  		count++
   490  		if flag == nil {
   491  			t.Fatal("VisitAll should not return a nil flag")
   492  		}
   493  	})
   494  	flagcount := fs.FlagCount()
   495  	if flagcount != count {
   496  		t.Fatalf("FlagCount (%d) != number (%d) of elements visited", flagcount, count)
   497  	}
   498  	// Make sure its idempotent
   499  	if flagcount != fs.FlagCount() {
   500  		t.Fatalf("FlagCount (%d) != fs.FlagCount() (%d) of elements visited", flagcount, fs.FlagCount())
   501  	}
   502  
   503  	count = 0
   504  	fs.Visit(func(flag *Flag) {
   505  		count++
   506  		if flag == nil {
   507  			t.Fatal("Visit should not return a nil flag")
   508  		}
   509  	})
   510  	nflag := fs.NFlag()
   511  	if nflag != count {
   512  		t.Fatalf("NFlag (%d) != number (%d) of elements visited", nflag, count)
   513  	}
   514  	if nflag != fs.NFlag() {
   515  		t.Fatalf("NFlag (%d) != fs.NFlag() (%d) of elements visited", nflag, fs.NFlag())
   516  	}
   517  }