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