github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/flag/flag_test.go (about) 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 "os" 12 "sort" 13 "strconv" 14 "strings" 15 "testing" 16 "time" 17 ) 18 19 func boolString(s string) string { 20 if s == "0" { 21 return "false" 22 } 23 return "true" 24 } 25 26 func TestEverything(t *testing.T) { 27 ResetForTesting(nil) 28 Bool("test_bool", false, "bool value") 29 Int("test_int", 0, "int value") 30 Int64("test_int64", 0, "int64 value") 31 Uint("test_uint", 0, "uint value") 32 Uint64("test_uint64", 0, "uint64 value") 33 String("test_string", "0", "string value") 34 Float64("test_float64", 0, "float64 value") 35 Duration("test_duration", 0, "time.Duration value") 36 37 m := make(map[string]*Flag) 38 desired := "0" 39 visitor := func(f *Flag) { 40 if len(f.Name) > 5 && f.Name[0:5] == "test_" { 41 m[f.Name] = f 42 ok := false 43 switch { 44 case f.Value.String() == desired: 45 ok = true 46 case f.Name == "test_bool" && f.Value.String() == boolString(desired): 47 ok = true 48 case f.Name == "test_duration" && f.Value.String() == desired+"s": 49 ok = true 50 } 51 if !ok { 52 t.Error("Visit: bad value", f.Value.String(), "for", f.Name) 53 } 54 } 55 } 56 VisitAll(visitor) 57 if len(m) != 8 { 58 t.Error("VisitAll misses some flags") 59 for k, v := range m { 60 t.Log(k, *v) 61 } 62 } 63 m = make(map[string]*Flag) 64 Visit(visitor) 65 if len(m) != 0 { 66 t.Errorf("Visit sees unset flags") 67 for k, v := range m { 68 t.Log(k, *v) 69 } 70 } 71 // Now set all flags 72 Set("test_bool", "true") 73 Set("test_int", "1") 74 Set("test_int64", "1") 75 Set("test_uint", "1") 76 Set("test_uint64", "1") 77 Set("test_string", "1") 78 Set("test_float64", "1") 79 Set("test_duration", "1s") 80 desired = "1" 81 Visit(visitor) 82 if len(m) != 8 { 83 t.Error("Visit fails after set") 84 for k, v := range m { 85 t.Log(k, *v) 86 } 87 } 88 // Now test they're visited in sort order. 89 var flagNames []string 90 Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) }) 91 if !sort.StringsAreSorted(flagNames) { 92 t.Errorf("flag names not sorted: %v", flagNames) 93 } 94 } 95 96 func TestGet(t *testing.T) { 97 ResetForTesting(nil) 98 Bool("test_bool", true, "bool value") 99 Int("test_int", 1, "int value") 100 Int64("test_int64", 2, "int64 value") 101 Uint("test_uint", 3, "uint value") 102 Uint64("test_uint64", 4, "uint64 value") 103 String("test_string", "5", "string value") 104 Float64("test_float64", 6, "float64 value") 105 Duration("test_duration", 7, "time.Duration value") 106 107 visitor := func(f *Flag) { 108 if len(f.Name) > 5 && f.Name[0:5] == "test_" { 109 g, ok := f.Value.(Getter) 110 if !ok { 111 t.Errorf("Visit: value does not satisfy Getter: %T", f.Value) 112 return 113 } 114 switch f.Name { 115 case "test_bool": 116 ok = g.Get() == true 117 case "test_int": 118 ok = g.Get() == int(1) 119 case "test_int64": 120 ok = g.Get() == int64(2) 121 case "test_uint": 122 ok = g.Get() == uint(3) 123 case "test_uint64": 124 ok = g.Get() == uint64(4) 125 case "test_string": 126 ok = g.Get() == "5" 127 case "test_float64": 128 ok = g.Get() == float64(6) 129 case "test_duration": 130 ok = g.Get() == time.Duration(7) 131 } 132 if !ok { 133 t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), f.Name) 134 } 135 } 136 } 137 VisitAll(visitor) 138 } 139 140 func TestUsage(t *testing.T) { 141 called := false 142 ResetForTesting(func() { called = true }) 143 if CommandLine.Parse([]string{"-x"}) == nil { 144 t.Error("parse did not fail for unknown flag") 145 } 146 if !called { 147 t.Error("did not call Usage for unknown flag") 148 } 149 } 150 151 func testParse(f *FlagSet, t *testing.T) { 152 if f.Parsed() { 153 t.Error("f.Parse() = true before Parse") 154 } 155 boolFlag := f.Bool("bool", false, "bool value") 156 bool2Flag := f.Bool("bool2", false, "bool2 value") 157 intFlag := f.Int("int", 0, "int value") 158 int64Flag := f.Int64("int64", 0, "int64 value") 159 uintFlag := f.Uint("uint", 0, "uint value") 160 uint64Flag := f.Uint64("uint64", 0, "uint64 value") 161 stringFlag := f.String("string", "0", "string value") 162 float64Flag := f.Float64("float64", 0, "float64 value") 163 durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value") 164 extra := "one-extra-argument" 165 args := []string{ 166 "-bool", 167 "-bool2=true", 168 "--int", "22", 169 "--int64", "0x23", 170 "-uint", "24", 171 "--uint64", "25", 172 "-string", "hello", 173 "-float64", "2718e28", 174 "-duration", "2m", 175 extra, 176 } 177 if err := f.Parse(args); err != nil { 178 t.Fatal(err) 179 } 180 if !f.Parsed() { 181 t.Error("f.Parse() = false after Parse") 182 } 183 if *boolFlag != true { 184 t.Error("bool flag should be true, is ", *boolFlag) 185 } 186 if *bool2Flag != true { 187 t.Error("bool2 flag should be true, is ", *bool2Flag) 188 } 189 if *intFlag != 22 { 190 t.Error("int flag should be 22, is ", *intFlag) 191 } 192 if *int64Flag != 0x23 { 193 t.Error("int64 flag should be 0x23, is ", *int64Flag) 194 } 195 if *uintFlag != 24 { 196 t.Error("uint flag should be 24, is ", *uintFlag) 197 } 198 if *uint64Flag != 25 { 199 t.Error("uint64 flag should be 25, is ", *uint64Flag) 200 } 201 if *stringFlag != "hello" { 202 t.Error("string flag should be `hello`, is ", *stringFlag) 203 } 204 if *float64Flag != 2718e28 { 205 t.Error("float64 flag should be 2718e28, is ", *float64Flag) 206 } 207 if *durationFlag != 2*time.Minute { 208 t.Error("duration flag should be 2m, is ", *durationFlag) 209 } 210 if len(f.Args()) != 1 { 211 t.Error("expected one argument, got", len(f.Args())) 212 } else if f.Args()[0] != extra { 213 t.Errorf("expected argument %q got %q", extra, f.Args()[0]) 214 } 215 } 216 217 func TestParse(t *testing.T) { 218 ResetForTesting(func() { t.Error("bad parse") }) 219 testParse(CommandLine, t) 220 } 221 222 func TestFlagSetParse(t *testing.T) { 223 testParse(NewFlagSet("test", ContinueOnError), t) 224 } 225 226 // Declare a user-defined flag type. 227 type flagVar []string 228 229 func (f *flagVar) String() string { 230 return fmt.Sprint([]string(*f)) 231 } 232 233 func (f *flagVar) Set(value string) error { 234 *f = append(*f, value) 235 return nil 236 } 237 238 func TestUserDefined(t *testing.T) { 239 var flags FlagSet 240 flags.Init("test", ContinueOnError) 241 var v flagVar 242 flags.Var(&v, "v", "usage") 243 if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil { 244 t.Error(err) 245 } 246 if len(v) != 3 { 247 t.Fatal("expected 3 args; got ", len(v)) 248 } 249 expect := "[1 2 3]" 250 if v.String() != expect { 251 t.Errorf("expected value %q got %q", expect, v.String()) 252 } 253 } 254 255 func TestUserDefinedForCommandLine(t *testing.T) { 256 const help = "HELP" 257 var result string 258 ResetForTesting(func() { result = help }) 259 Usage() 260 if result != help { 261 t.Fatalf("got %q; expected %q", result, help) 262 } 263 } 264 265 // Declare a user-defined boolean flag type. 266 type boolFlagVar struct { 267 count int 268 } 269 270 func (b *boolFlagVar) String() string { 271 return fmt.Sprintf("%d", b.count) 272 } 273 274 func (b *boolFlagVar) Set(value string) error { 275 if value == "true" { 276 b.count++ 277 } 278 return nil 279 } 280 281 func (b *boolFlagVar) IsBoolFlag() bool { 282 return b.count < 4 283 } 284 285 func TestUserDefinedBool(t *testing.T) { 286 var flags FlagSet 287 flags.Init("test", ContinueOnError) 288 var b boolFlagVar 289 var err error 290 flags.Var(&b, "b", "usage") 291 if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil { 292 if b.count < 4 { 293 t.Error(err) 294 } 295 } 296 297 if b.count != 4 { 298 t.Errorf("want: %d; got: %d", 4, b.count) 299 } 300 301 if err == nil { 302 t.Error("expected error; got none") 303 } 304 } 305 306 func TestSetOutput(t *testing.T) { 307 var flags FlagSet 308 var buf bytes.Buffer 309 flags.SetOutput(&buf) 310 flags.Init("test", ContinueOnError) 311 flags.Parse([]string{"-unknown"}) 312 if out := buf.String(); !strings.Contains(out, "-unknown") { 313 t.Logf("expected output mentioning unknown; got %q", out) 314 } 315 } 316 317 // This tests that one can reset the flags. This still works but not well, and is 318 // superseded by FlagSet. 319 func TestChangingArgs(t *testing.T) { 320 ResetForTesting(func() { t.Fatal("bad parse") }) 321 oldArgs := os.Args 322 defer func() { os.Args = oldArgs }() 323 os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} 324 before := Bool("before", false, "") 325 if err := CommandLine.Parse(os.Args[1:]); err != nil { 326 t.Fatal(err) 327 } 328 cmd := Arg(0) 329 os.Args = Args() 330 after := Bool("after", false, "") 331 Parse() 332 args := Args() 333 334 if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { 335 t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) 336 } 337 } 338 339 // Test that -help invokes the usage message and returns ErrHelp. 340 func TestHelp(t *testing.T) { 341 var helpCalled = false 342 fs := NewFlagSet("help test", ContinueOnError) 343 fs.Usage = func() { helpCalled = true } 344 var flag bool 345 fs.BoolVar(&flag, "flag", false, "regular flag") 346 // Regular flag invocation should work 347 err := fs.Parse([]string{"-flag=true"}) 348 if err != nil { 349 t.Fatal("expected no error; got ", err) 350 } 351 if !flag { 352 t.Error("flag was not set by -flag") 353 } 354 if helpCalled { 355 t.Error("help called for regular flag") 356 helpCalled = false // reset for next test 357 } 358 // Help flag should work as expected. 359 err = fs.Parse([]string{"-help"}) 360 if err == nil { 361 t.Fatal("error expected") 362 } 363 if err != ErrHelp { 364 t.Fatal("expected ErrHelp; got ", err) 365 } 366 if !helpCalled { 367 t.Fatal("help was not called") 368 } 369 // If we define a help flag, that should override. 370 var help bool 371 fs.BoolVar(&help, "help", false, "help flag") 372 helpCalled = false 373 err = fs.Parse([]string{"-help"}) 374 if err != nil { 375 t.Fatal("expected no error for defined -help; got ", err) 376 } 377 if helpCalled { 378 t.Fatal("help was called; should not have been for defined help flag") 379 } 380 } 381 382 const defaultOutput = ` -A for bootstrapping, allow 'any' type 383 -Alongflagname 384 disable bounds checking 385 -C a boolean defaulting to true (default true) 386 -D path 387 set relative path for local imports 388 -F number 389 a non-zero number (default 2.7) 390 -G float 391 a float that defaults to zero 392 -N int 393 a non-zero int (default 27) 394 -Z int 395 an int that defaults to zero 396 -maxT timeout 397 set timeout for dial 398 ` 399 400 func TestPrintDefaults(t *testing.T) { 401 fs := NewFlagSet("print defaults test", ContinueOnError) 402 var buf bytes.Buffer 403 fs.SetOutput(&buf) 404 fs.Bool("A", false, "for bootstrapping, allow 'any' type") 405 fs.Bool("Alongflagname", false, "disable bounds checking") 406 fs.Bool("C", true, "a boolean defaulting to true") 407 fs.String("D", "", "set relative `path` for local imports") 408 fs.Float64("F", 2.7, "a non-zero `number`") 409 fs.Float64("G", 0, "a float that defaults to zero") 410 fs.Int("N", 27, "a non-zero int") 411 fs.Int("Z", 0, "an int that defaults to zero") 412 fs.Duration("maxT", 0, "set `timeout` for dial") 413 fs.PrintDefaults() 414 got := buf.String() 415 if got != defaultOutput { 416 t.Errorf("got %q want %q\n", got, defaultOutput) 417 } 418 } 419 420 // Issue 19230: validate range of Int and Uint flag values. 421 func TestIntFlagOverflow(t *testing.T) { 422 if strconv.IntSize != 32 { 423 return 424 } 425 ResetForTesting(nil) 426 Int("i", 0, "") 427 Uint("u", 0, "") 428 if err := Set("i", "2147483648"); err == nil { 429 t.Error("unexpected success setting Int") 430 } 431 if err := Set("u", "4294967296"); err == nil { 432 t.Error("unexpected success setting Uint") 433 } 434 }