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 5package pflag 6 7import ( 8 "bytes" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "net" 13 "os" 14 "reflect" 15 "sort" 16 "strconv" 17 "strings" 18 "testing" 19 "time" 20) 21 22var ( 23 testBool = Bool("test_bool", false, "bool value") 24 testInt = Int("test_int", 0, "int value") 25 testInt64 = Int64("test_int64", 0, "int64 value") 26 testUint = Uint("test_uint", 0, "uint value") 27 testUint64 = Uint64("test_uint64", 0, "uint64 value") 28 testString = String("test_string", "0", "string value") 29 testFloat = Float64("test_float64", 0, "float64 value") 30 testDuration = Duration("test_duration", 0, "time.Duration value") 31 testOptionalInt = Int("test_optional_int", 0, "optional int value") 32 normalizeFlagNameInvocations = 0 33) 34 35func boolString(s string) string { 36 if s == "0" { 37 return "false" 38 } 39 return "true" 40} 41 42func TestEverything(t *testing.T) { 43 m := make(map[string]*Flag) 44 desired := "0" 45 visitor := func(f *Flag) { 46 if len(f.Name) > 5 && f.Name[0:5] == "test_" { 47 m[f.Name] = f 48 ok := false 49 switch { 50 case f.Value.String() == desired: 51 ok = true 52 case f.Name == "test_bool" && f.Value.String() == boolString(desired): 53 ok = true 54 case f.Name == "test_duration" && f.Value.String() == desired+"s": 55 ok = true 56 } 57 if !ok { 58 t.Error("Visit: bad value", f.Value.String(), "for", f.Name) 59 } 60 } 61 } 62 VisitAll(visitor) 63 if len(m) != 9 { 64 t.Error("VisitAll misses some flags") 65 for k, v := range m { 66 t.Log(k, *v) 67 } 68 } 69 m = make(map[string]*Flag) 70 Visit(visitor) 71 if len(m) != 0 { 72 t.Errorf("Visit sees unset flags") 73 for k, v := range m { 74 t.Log(k, *v) 75 } 76 } 77 // Now set all flags 78 Set("test_bool", "true") 79 Set("test_int", "1") 80 Set("test_int64", "1") 81 Set("test_uint", "1") 82 Set("test_uint64", "1") 83 Set("test_string", "1") 84 Set("test_float64", "1") 85 Set("test_duration", "1s") 86 Set("test_optional_int", "1") 87 desired = "1" 88 Visit(visitor) 89 if len(m) != 9 { 90 t.Error("Visit fails after set") 91 for k, v := range m { 92 t.Log(k, *v) 93 } 94 } 95 // Now test they're visited in sort order. 96 var flagNames []string 97 Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) }) 98 if !sort.StringsAreSorted(flagNames) { 99 t.Errorf("flag names not sorted: %v", flagNames) 100 } 101} 102 103func TestUsage(t *testing.T) { 104 called := false 105 ResetForTesting(func() { called = true }) 106 if GetCommandLine().Parse([]string{"--x"}) == nil { 107 t.Error("parse did not fail for unknown flag") 108 } 109 if called { 110 t.Error("did call Usage while using ContinueOnError") 111 } 112} 113 114func TestAddFlagSet(t *testing.T) { 115 oldSet := NewFlagSet("old", ContinueOnError) 116 newSet := NewFlagSet("new", ContinueOnError) 117 118 oldSet.String("flag1", "flag1", "flag1") 119 oldSet.String("flag2", "flag2", "flag2") 120 121 newSet.String("flag2", "flag2", "flag2") 122 newSet.String("flag3", "flag3", "flag3") 123 124 oldSet.AddFlagSet(newSet) 125 126 if len(oldSet.formal) != 3 { 127 t.Errorf("Unexpected result adding a FlagSet to a FlagSet %v", oldSet) 128 } 129} 130 131func TestAnnotation(t *testing.T) { 132 f := NewFlagSet("shorthand", ContinueOnError) 133 134 if err := f.SetAnnotation("missing-flag", "key", nil); err == nil { 135 t.Errorf("Expected error setting annotation on non-existent flag") 136 } 137 138 f.StringP("stringa", "a", "", "string value") 139 if err := f.SetAnnotation("stringa", "key", nil); err != nil { 140 t.Errorf("Unexpected error setting new nil annotation: %v", err) 141 } 142 if annotation := f.Lookup("stringa").Annotations["key"]; annotation != nil { 143 t.Errorf("Unexpected annotation: %v", annotation) 144 } 145 146 f.StringP("stringb", "b", "", "string2 value") 147 if err := f.SetAnnotation("stringb", "key", []string{"value1"}); err != nil { 148 t.Errorf("Unexpected error setting new annotation: %v", err) 149 } 150 if annotation := f.Lookup("stringb").Annotations["key"]; !reflect.DeepEqual(annotation, []string{"value1"}) { 151 t.Errorf("Unexpected annotation: %v", annotation) 152 } 153 154 if err := f.SetAnnotation("stringb", "key", []string{"value2"}); err != nil { 155 t.Errorf("Unexpected error updating annotation: %v", err) 156 } 157 if annotation := f.Lookup("stringb").Annotations["key"]; !reflect.DeepEqual(annotation, []string{"value2"}) { 158 t.Errorf("Unexpected annotation: %v", annotation) 159 } 160} 161 162func testParse(f *FlagSet, t *testing.T) { 163 if f.Parsed() { 164 t.Error("f.Parse() = true before Parse") 165 } 166 boolFlag := f.Bool("bool", false, "bool value") 167 bool2Flag := f.Bool("bool2", false, "bool2 value") 168 bool3Flag := f.Bool("bool3", false, "bool3 value") 169 intFlag := f.Int("int", 0, "int value") 170 int8Flag := f.Int8("int8", 0, "int value") 171 int16Flag := f.Int16("int16", 0, "int value") 172 int32Flag := f.Int32("int32", 0, "int value") 173 int64Flag := f.Int64("int64", 0, "int64 value") 174 uintFlag := f.Uint("uint", 0, "uint value") 175 uint8Flag := f.Uint8("uint8", 0, "uint value") 176 uint16Flag := f.Uint16("uint16", 0, "uint value") 177 uint32Flag := f.Uint32("uint32", 0, "uint value") 178 uint64Flag := f.Uint64("uint64", 0, "uint64 value") 179 stringFlag := f.String("string", "0", "string value") 180 float32Flag := f.Float32("float32", 0, "float32 value") 181 float64Flag := f.Float64("float64", 0, "float64 value") 182 ipFlag := f.IP("ip", net.ParseIP("127.0.0.1"), "ip value") 183 maskFlag := f.IPMask("mask", ParseIPv4Mask("0.0.0.0"), "mask value") 184 durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value") 185 optionalIntNoValueFlag := f.Int("optional-int-no-value", 0, "int value") 186 f.Lookup("optional-int-no-value").NoOptDefVal = "9" 187 optionalIntWithValueFlag := f.Int("optional-int-with-value", 0, "int value") 188 f.Lookup("optional-int-no-value").NoOptDefVal = "9" 189 extra := "one-extra-argument" 190 args := []string{ 191 "--bool", 192 "--bool2=true", 193 "--bool3=false", 194 "--int=22", 195 "--int8=-8", 196 "--int16=-16", 197 "--int32=-32", 198 "--int64=0x23", 199 "--uint", "24", 200 "--uint8=8", 201 "--uint16=16", 202 "--uint32=32", 203 "--uint64=25", 204 "--string=hello", 205 "--float32=-172e12", 206 "--float64=2718e28", 207 "--ip=10.11.12.13", 208 "--mask=255.255.255.0", 209 "--duration=2m", 210 "--optional-int-no-value", 211 "--optional-int-with-value=42", 212 extra, 213 } 214 if err := f.Parse(args); err != nil { 215 t.Fatal(err) 216 } 217 if !f.Parsed() { 218 t.Error("f.Parse() = false after Parse") 219 } 220 if *boolFlag != true { 221 t.Error("bool flag should be true, is ", *boolFlag) 222 } 223 if v, err := f.GetBool("bool"); err != nil || v != *boolFlag { 224 t.Error("GetBool does not work.") 225 } 226 if *bool2Flag != true { 227 t.Error("bool2 flag should be true, is ", *bool2Flag) 228 } 229 if *bool3Flag != false { 230 t.Error("bool3 flag should be false, is ", *bool2Flag) 231 } 232 if *intFlag != 22 { 233 t.Error("int flag should be 22, is ", *intFlag) 234 } 235 if v, err := f.GetInt("int"); err != nil || v != *intFlag { 236 t.Error("GetInt does not work.") 237 } 238 if *int8Flag != -8 { 239 t.Error("int8 flag should be 0x23, is ", *int8Flag) 240 } 241 if *int16Flag != -16 { 242 t.Error("int16 flag should be -16, is ", *int16Flag) 243 } 244 if v, err := f.GetInt8("int8"); err != nil || v != *int8Flag { 245 t.Error("GetInt8 does not work.") 246 } 247 if v, err := f.GetInt16("int16"); err != nil || v != *int16Flag { 248 t.Error("GetInt16 does not work.") 249 } 250 if *int32Flag != -32 { 251 t.Error("int32 flag should be 0x23, is ", *int32Flag) 252 } 253 if v, err := f.GetInt32("int32"); err != nil || v != *int32Flag { 254 t.Error("GetInt32 does not work.") 255 } 256 if *int64Flag != 0x23 { 257 t.Error("int64 flag should be 0x23, is ", *int64Flag) 258 } 259 if v, err := f.GetInt64("int64"); err != nil || v != *int64Flag { 260 t.Error("GetInt64 does not work.") 261 } 262 if *uintFlag != 24 { 263 t.Error("uint flag should be 24, is ", *uintFlag) 264 } 265 if v, err := f.GetUint("uint"); err != nil || v != *uintFlag { 266 t.Error("GetUint does not work.") 267 } 268 if *uint8Flag != 8 { 269 t.Error("uint8 flag should be 8, is ", *uint8Flag) 270 } 271 if v, err := f.GetUint8("uint8"); err != nil || v != *uint8Flag { 272 t.Error("GetUint8 does not work.") 273 } 274 if *uint16Flag != 16 { 275 t.Error("uint16 flag should be 16, is ", *uint16Flag) 276 } 277 if v, err := f.GetUint16("uint16"); err != nil || v != *uint16Flag { 278 t.Error("GetUint16 does not work.") 279 } 280 if *uint32Flag != 32 { 281 t.Error("uint32 flag should be 32, is ", *uint32Flag) 282 } 283 if v, err := f.GetUint32("uint32"); err != nil || v != *uint32Flag { 284 t.Error("GetUint32 does not work.") 285 } 286 if *uint64Flag != 25 { 287 t.Error("uint64 flag should be 25, is ", *uint64Flag) 288 } 289 if v, err := f.GetUint64("uint64"); err != nil || v != *uint64Flag { 290 t.Error("GetUint64 does not work.") 291 } 292 if *stringFlag != "hello" { 293 t.Error("string flag should be `hello`, is ", *stringFlag) 294 } 295 if v, err := f.GetString("string"); err != nil || v != *stringFlag { 296 t.Error("GetString does not work.") 297 } 298 if *float32Flag != -172e12 { 299 t.Error("float32 flag should be -172e12, is ", *float32Flag) 300 } 301 if v, err := f.GetFloat32("float32"); err != nil || v != *float32Flag { 302 t.Errorf("GetFloat32 returned %v but float32Flag was %v", v, *float32Flag) 303 } 304 if *float64Flag != 2718e28 { 305 t.Error("float64 flag should be 2718e28, is ", *float64Flag) 306 } 307 if v, err := f.GetFloat64("float64"); err != nil || v != *float64Flag { 308 t.Errorf("GetFloat64 returned %v but float64Flag was %v", v, *float64Flag) 309 } 310 if !(*ipFlag).Equal(net.ParseIP("10.11.12.13")) { 311 t.Error("ip flag should be 10.11.12.13, is ", *ipFlag) 312 } 313 if v, err := f.GetIP("ip"); err != nil || !v.Equal(*ipFlag) { 314 t.Errorf("GetIP returned %v but ipFlag was %v", v, *ipFlag) 315 } 316 if (*maskFlag).String() != ParseIPv4Mask("255.255.255.0").String() { 317 t.Error("mask flag should be 255.255.255.0, is ", (*maskFlag).String()) 318 } 319 if v, err := f.GetIPv4Mask("mask"); err != nil || v.String() != (*maskFlag).String() { 320 t.Errorf("GetIP returned %v maskFlag was %v error was %v", v, *maskFlag, err) 321 } 322 if *durationFlag != 2*time.Minute { 323 t.Error("duration flag should be 2m, is ", *durationFlag) 324 } 325 if v, err := f.GetDuration("duration"); err != nil || v != *durationFlag { 326 t.Error("GetDuration does not work.") 327 } 328 if _, err := f.GetInt("duration"); err == nil { 329 t.Error("GetInt parsed a time.Duration?!?!") 330 } 331 if *optionalIntNoValueFlag != 9 { 332 t.Error("optional int flag should be the default value, is ", *optionalIntNoValueFlag) 333 } 334 if *optionalIntWithValueFlag != 42 { 335 t.Error("optional int flag should be 42, is ", *optionalIntWithValueFlag) 336 } 337 if len(f.Args()) != 1 { 338 t.Error("expected one argument, got", len(f.Args())) 339 } else if f.Args()[0] != extra { 340 t.Errorf("expected argument %q got %q", extra, f.Args()[0]) 341 } 342} 343 344func testParseAll(f *FlagSet, t *testing.T) { 345 if f.Parsed() { 346 t.Error("f.Parse() = true before Parse") 347 } 348 f.BoolP("boola", "a", false, "bool value") 349 f.BoolP("boolb", "b", false, "bool2 value") 350 f.BoolP("boolc", "c", false, "bool3 value") 351 f.BoolP("boold", "d", false, "bool4 value") 352 f.StringP("stringa", "s", "0", "string value") 353 f.StringP("stringz", "z", "0", "string value") 354 f.StringP("stringx", "x", "0", "string value") 355 f.StringP("stringy", "y", "0", "string value") 356 f.Lookup("stringx").NoOptDefVal = "1" 357 args := []string{ 358 "-ab", 359 "-cs=xx", 360 "--stringz=something", 361 "-d=true", 362 "-x", 363 "-y", 364 "ee", 365 } 366 want := []string{ 367 "boola", "true", 368 "boolb", "true", 369 "boolc", "true", 370 "stringa", "xx", 371 "stringz", "something", 372 "boold", "true", 373 "stringx", "1", 374 "stringy", "ee", 375 } 376 got := []string{} 377 store := func(flag *Flag, value string) error { 378 got = append(got, flag.Name) 379 if len(value) > 0 { 380 got = append(got, value) 381 } 382 return nil 383 } 384 if err := f.ParseAll(args, store); err != nil { 385 t.Errorf("expected no error, got %s", err) 386 } 387 if !f.Parsed() { 388 t.Errorf("f.Parse() = false after Parse") 389 } 390 if !reflect.DeepEqual(got, want) { 391 t.Errorf("f.ParseAll() fail to restore the args") 392 t.Errorf("Got: %v", got) 393 t.Errorf("Want: %v", want) 394 } 395} 396 397func testParseWithUnknownFlags(f *FlagSet, t *testing.T) { 398 if f.Parsed() { 399 t.Error("f.Parse() = true before Parse") 400 } 401 f.ParseErrorsWhitelist.UnknownFlags = true 402 403 f.BoolP("boola", "a", false, "bool value") 404 f.BoolP("boolb", "b", false, "bool2 value") 405 f.BoolP("boolc", "c", false, "bool3 value") 406 f.BoolP("boold", "d", false, "bool4 value") 407 f.BoolP("boole", "e", false, "bool4 value") 408 f.StringP("stringa", "s", "0", "string value") 409 f.StringP("stringz", "z", "0", "string value") 410 f.StringP("stringx", "x", "0", "string value") 411 f.StringP("stringy", "y", "0", "string value") 412 f.StringP("stringo", "o", "0", "string value") 413 f.Lookup("stringx").NoOptDefVal = "1" 414 args := []string{ 415 "-ab", 416 "-cs=xx", 417 "--stringz=something", 418 "--unknown1", 419 "unknown1Value", 420 "-d=true", 421 "-x", 422 "--unknown2=unknown2Value", 423 "-u=unknown3Value", 424 "-p", 425 "unknown4Value", 426 "-q", //another unknown with bool value 427 "-y", 428 "ee", 429 "--unknown7=unknown7value", 430 "--stringo=ovalue", 431 "--unknown8=unknown8value", 432 "--boole", 433 "--unknown6", 434 "", 435 "-uuuuu", 436 "", 437 "--unknown10", 438 "--unknown11", 439 } 440 want := []string{ 441 "boola", "true", 442 "boolb", "true", 443 "boolc", "true", 444 "stringa", "xx", 445 "stringz", "something", 446 "boold", "true", 447 "stringx", "1", 448 "stringy", "ee", 449 "stringo", "ovalue", 450 "boole", "true", 451 } 452 got := []string{} 453 store := func(flag *Flag, value string) error { 454 got = append(got, flag.Name) 455 if len(value) > 0 { 456 got = append(got, value) 457 } 458 return nil 459 } 460 if err := f.ParseAll(args, store); err != nil { 461 t.Errorf("expected no error, got %s", err) 462 } 463 if !f.Parsed() { 464 t.Errorf("f.Parse() = false after Parse") 465 } 466 if !reflect.DeepEqual(got, want) { 467 t.Errorf("f.ParseAll() fail to restore the args") 468 t.Errorf("Got: %v", got) 469 t.Errorf("Want: %v", want) 470 } 471} 472 473func TestShorthand(t *testing.T) { 474 f := NewFlagSet("shorthand", ContinueOnError) 475 if f.Parsed() { 476 t.Error("f.Parse() = true before Parse") 477 } 478 boolaFlag := f.BoolP("boola", "a", false, "bool value") 479 boolbFlag := f.BoolP("boolb", "b", false, "bool2 value") 480 boolcFlag := f.BoolP("boolc", "c", false, "bool3 value") 481 booldFlag := f.BoolP("boold", "d", false, "bool4 value") 482 stringaFlag := f.StringP("stringa", "s", "0", "string value") 483 stringzFlag := f.StringP("stringz", "z", "0", "string value") 484 extra := "interspersed-argument" 485 notaflag := "--i-look-like-a-flag" 486 args := []string{ 487 "-ab", 488 extra, 489 "-cs", 490 "hello", 491 "-z=something", 492 "-d=true", 493 "--", 494 notaflag, 495 } 496 f.SetOutput(ioutil.Discard) 497 if err := f.Parse(args); err != nil { 498 t.Error("expected no error, got ", err) 499 } 500 if !f.Parsed() { 501 t.Error("f.Parse() = false after Parse") 502 } 503 if *boolaFlag != true { 504 t.Error("boola flag should be true, is ", *boolaFlag) 505 } 506 if *boolbFlag != true { 507 t.Error("boolb flag should be true, is ", *boolbFlag) 508 } 509 if *boolcFlag != true { 510 t.Error("boolc flag should be true, is ", *boolcFlag) 511 } 512 if *booldFlag != true { 513 t.Error("boold flag should be true, is ", *booldFlag) 514 } 515 if *stringaFlag != "hello" { 516 t.Error("stringa flag should be `hello`, is ", *stringaFlag) 517 } 518 if *stringzFlag != "something" { 519 t.Error("stringz flag should be `something`, is ", *stringzFlag) 520 } 521 if len(f.Args()) != 2 { 522 t.Error("expected one argument, got", len(f.Args())) 523 } else if f.Args()[0] != extra { 524 t.Errorf("expected argument %q got %q", extra, f.Args()[0]) 525 } else if f.Args()[1] != notaflag { 526 t.Errorf("expected argument %q got %q", notaflag, f.Args()[1]) 527 } 528 if f.ArgsLenAtDash() != 1 { 529 t.Errorf("expected argsLenAtDash %d got %d", f.ArgsLenAtDash(), 1) 530 } 531} 532 533func TestShorthandLookup(t *testing.T) { 534 f := NewFlagSet("shorthand", ContinueOnError) 535 if f.Parsed() { 536 t.Error("f.Parse() = true before Parse") 537 } 538 f.BoolP("boola", "a", false, "bool value") 539 f.BoolP("boolb", "b", false, "bool2 value") 540 args := []string{ 541 "-ab", 542 } 543 f.SetOutput(ioutil.Discard) 544 if err := f.Parse(args); err != nil { 545 t.Error("expected no error, got ", err) 546 } 547 if !f.Parsed() { 548 t.Error("f.Parse() = false after Parse") 549 } 550 flag := f.ShorthandLookup("a") 551 if flag == nil { 552 t.Errorf("f.ShorthandLookup(\"a\") returned nil") 553 } 554 if flag.Name != "boola" { 555 t.Errorf("f.ShorthandLookup(\"a\") found %q instead of \"boola\"", flag.Name) 556 } 557 flag = f.ShorthandLookup("") 558 if flag != nil { 559 t.Errorf("f.ShorthandLookup(\"\") did not return nil") 560 } 561 defer func() { 562 recover() 563 }() 564 flag = f.ShorthandLookup("ab") 565 // should NEVER get here. lookup should panic. defer'd func should recover it. 566 t.Errorf("f.ShorthandLookup(\"ab\") did not panic") 567} 568 569func TestParse(t *testing.T) { 570 ResetForTesting(func() { t.Error("bad parse") }) 571 testParse(GetCommandLine(), t) 572} 573 574func TestParseAll(t *testing.T) { 575 ResetForTesting(func() { t.Error("bad parse") }) 576 testParseAll(GetCommandLine(), t) 577} 578 579func TestIgnoreUnknownFlags(t *testing.T) { 580 ResetForTesting(func() { t.Error("bad parse") }) 581 testParseWithUnknownFlags(GetCommandLine(), t) 582} 583 584func TestFlagSetParse(t *testing.T) { 585 testParse(NewFlagSet("test", ContinueOnError), t) 586} 587 588func TestChangedHelper(t *testing.T) { 589 f := NewFlagSet("changedtest", ContinueOnError) 590 f.Bool("changed", false, "changed bool") 591 f.Bool("settrue", true, "true to true") 592 f.Bool("setfalse", false, "false to false") 593 f.Bool("unchanged", false, "unchanged bool") 594 595 args := []string{"--changed", "--settrue", "--setfalse=false"} 596 if err := f.Parse(args); err != nil { 597 t.Error("f.Parse() = false after Parse") 598 } 599 if !f.Changed("changed") { 600 t.Errorf("--changed wasn't changed!") 601 } 602 if !f.Changed("settrue") { 603 t.Errorf("--settrue wasn't changed!") 604 } 605 if !f.Changed("setfalse") { 606 t.Errorf("--setfalse wasn't changed!") 607 } 608 if f.Changed("unchanged") { 609 t.Errorf("--unchanged was changed!") 610 } 611 if f.Changed("invalid") { 612 t.Errorf("--invalid was changed!") 613 } 614 if f.ArgsLenAtDash() != -1 { 615 t.Errorf("Expected argsLenAtDash: %d but got %d", -1, f.ArgsLenAtDash()) 616 } 617} 618 619func replaceSeparators(name string, from []string, to string) string { 620 result := name 621 for _, sep := range from { 622 result = strings.Replace(result, sep, to, -1) 623 } 624 // Type convert to indicate normalization has been done. 625 return result 626} 627 628func wordSepNormalizeFunc(f *FlagSet, name string) NormalizedName { 629 seps := []string{"-", "_"} 630 name = replaceSeparators(name, seps, ".") 631 normalizeFlagNameInvocations++ 632 633 return NormalizedName(name) 634} 635 636func testWordSepNormalizedNames(args []string, t *testing.T) { 637 f := NewFlagSet("normalized", ContinueOnError) 638 if f.Parsed() { 639 t.Error("f.Parse() = true before Parse") 640 } 641 withDashFlag := f.Bool("with-dash-flag", false, "bool value") 642 // Set this after some flags have been added and before others. 643 f.SetNormalizeFunc(wordSepNormalizeFunc) 644 withUnderFlag := f.Bool("with_under_flag", false, "bool value") 645 withBothFlag := f.Bool("with-both_flag", false, "bool value") 646 if err := f.Parse(args); err != nil { 647 t.Fatal(err) 648 } 649 if !f.Parsed() { 650 t.Error("f.Parse() = false after Parse") 651 } 652 if *withDashFlag != true { 653 t.Error("withDashFlag flag should be true, is ", *withDashFlag) 654 } 655 if *withUnderFlag != true { 656 t.Error("withUnderFlag flag should be true, is ", *withUnderFlag) 657 } 658 if *withBothFlag != true { 659 t.Error("withBothFlag flag should be true, is ", *withBothFlag) 660 } 661} 662 663func TestWordSepNormalizedNames(t *testing.T) { 664 args := []string{ 665 "--with-dash-flag", 666 "--with-under-flag", 667 "--with-both-flag", 668 } 669 testWordSepNormalizedNames(args, t) 670 671 args = []string{ 672 "--with_dash_flag", 673 "--with_under_flag", 674 "--with_both_flag", 675 } 676 testWordSepNormalizedNames(args, t) 677 678 args = []string{ 679 "--with-dash_flag", 680 "--with-under_flag", 681 "--with-both_flag", 682 } 683 testWordSepNormalizedNames(args, t) 684} 685 686func aliasAndWordSepFlagNames(f *FlagSet, name string) NormalizedName { 687 seps := []string{"-", "_"} 688 689 oldName := replaceSeparators("old-valid_flag", seps, ".") 690 newName := replaceSeparators("valid-flag", seps, ".") 691 692 name = replaceSeparators(name, seps, ".") 693 switch name { 694 case oldName: 695 name = newName 696 } 697 698 return NormalizedName(name) 699} 700 701func TestCustomNormalizedNames(t *testing.T) { 702 f := NewFlagSet("normalized", ContinueOnError) 703 if f.Parsed() { 704 t.Error("f.Parse() = true before Parse") 705 } 706 707 validFlag := f.Bool("valid-flag", false, "bool value") 708 f.SetNormalizeFunc(aliasAndWordSepFlagNames) 709 someOtherFlag := f.Bool("some-other-flag", false, "bool value") 710 711 args := []string{"--old_valid_flag", "--some-other_flag"} 712 if err := f.Parse(args); err != nil { 713 t.Fatal(err) 714 } 715 716 if *validFlag != true { 717 t.Errorf("validFlag is %v even though we set the alias --old_valid_falg", *validFlag) 718 } 719 if *someOtherFlag != true { 720 t.Error("someOtherFlag should be true, is ", *someOtherFlag) 721 } 722} 723 724// Every flag we add, the name (displayed also in usage) should normalized 725func TestNormalizationFuncShouldChangeFlagName(t *testing.T) { 726 // Test normalization after addition 727 f := NewFlagSet("normalized", ContinueOnError) 728 729 f.Bool("valid_flag", false, "bool value") 730 if f.Lookup("valid_flag").Name != "valid_flag" { 731 t.Error("The new flag should have the name 'valid_flag' instead of ", f.Lookup("valid_flag").Name) 732 } 733 734 f.SetNormalizeFunc(wordSepNormalizeFunc) 735 if f.Lookup("valid_flag").Name != "valid.flag" { 736 t.Error("The new flag should have the name 'valid.flag' instead of ", f.Lookup("valid_flag").Name) 737 } 738 739 // Test normalization before addition 740 f = NewFlagSet("normalized", ContinueOnError) 741 f.SetNormalizeFunc(wordSepNormalizeFunc) 742 743 f.Bool("valid_flag", false, "bool value") 744 if f.Lookup("valid_flag").Name != "valid.flag" { 745 t.Error("The new flag should have the name 'valid.flag' instead of ", f.Lookup("valid_flag").Name) 746 } 747} 748 749// Related to https://github.com/spf13/cobra/issues/521. 750func TestNormalizationSharedFlags(t *testing.T) { 751 f := NewFlagSet("set f", ContinueOnError) 752 g := NewFlagSet("set g", ContinueOnError) 753 nfunc := wordSepNormalizeFunc 754 testName := "valid_flag" 755 normName := nfunc(nil, testName) 756 if testName == string(normName) { 757 t.Error("TestNormalizationSharedFlags meaningless: the original and normalized flag names are identical:", testName) 758 } 759 760 f.Bool(testName, false, "bool value") 761 g.AddFlagSet(f) 762 763 f.SetNormalizeFunc(nfunc) 764 g.SetNormalizeFunc(nfunc) 765 766 if len(f.formal) != 1 { 767 t.Error("Normalizing flags should not result in duplications in the flag set:", f.formal) 768 } 769 if f.orderedFormal[0].Name != string(normName) { 770 t.Error("Flag name not normalized") 771 } 772 for k := range f.formal { 773 if k != "valid.flag" { 774 t.Errorf("The key in the flag map should have been normalized: wanted \"%s\", got \"%s\" instead", normName, k) 775 } 776 } 777 778 if !reflect.DeepEqual(f.formal, g.formal) || !reflect.DeepEqual(f.orderedFormal, g.orderedFormal) { 779 t.Error("Two flag sets sharing the same flags should stay consistent after being normalized. Original set:", f.formal, "Duplicate set:", g.formal) 780 } 781} 782 783func TestNormalizationSetFlags(t *testing.T) { 784 f := NewFlagSet("normalized", ContinueOnError) 785 nfunc := wordSepNormalizeFunc 786 testName := "valid_flag" 787 normName := nfunc(nil, testName) 788 if testName == string(normName) { 789 t.Error("TestNormalizationSetFlags meaningless: the original and normalized flag names are identical:", testName) 790 } 791 792 f.Bool(testName, false, "bool value") 793 f.Set(testName, "true") 794 f.SetNormalizeFunc(nfunc) 795 796 if len(f.formal) != 1 { 797 t.Error("Normalizing flags should not result in duplications in the flag set:", f.formal) 798 } 799 if f.orderedFormal[0].Name != string(normName) { 800 t.Error("Flag name not normalized") 801 } 802 for k := range f.formal { 803 if k != "valid.flag" { 804 t.Errorf("The key in the flag map should have been normalized: wanted \"%s\", got \"%s\" instead", normName, k) 805 } 806 } 807 808 if !reflect.DeepEqual(f.formal, f.actual) { 809 t.Error("The map of set flags should get normalized. Formal:", f.formal, "Actual:", f.actual) 810 } 811} 812 813// Declare a user-defined flag type. 814type flagVar []string 815 816func (f *flagVar) String() string { 817 return fmt.Sprint([]string(*f)) 818} 819 820func (f *flagVar) Set(value string) error { 821 *f = append(*f, value) 822 return nil 823} 824 825func (f *flagVar) Type() string { 826 return "flagVar" 827} 828 829func TestUserDefined(t *testing.T) { 830 var flags FlagSet 831 flags.Init("test", ContinueOnError) 832 var v flagVar 833 flags.VarP(&v, "v", "v", "usage") 834 if err := flags.Parse([]string{"--v=1", "-v2", "-v", "3"}); err != nil { 835 t.Error(err) 836 } 837 if len(v) != 3 { 838 t.Fatal("expected 3 args; got ", len(v)) 839 } 840 expect := "[1 2 3]" 841 if v.String() != expect { 842 t.Errorf("expected value %q got %q", expect, v.String()) 843 } 844} 845 846func TestSetOutput(t *testing.T) { 847 var flags FlagSet 848 var buf bytes.Buffer 849 flags.SetOutput(&buf) 850 flags.Init("test", ContinueOnError) 851 flags.Parse([]string{"--unknown"}) 852 if out := buf.String(); !strings.Contains(out, "--unknown") { 853 t.Logf("expected output mentioning unknown; got %q", out) 854 } 855} 856 857// This tests that one can reset the flags. This still works but not well, and is 858// superseded by FlagSet. 859func TestChangingArgs(t *testing.T) { 860 ResetForTesting(func() { t.Fatal("bad parse") }) 861 oldArgs := os.Args 862 defer func() { os.Args = oldArgs }() 863 os.Args = []string{"cmd", "--before", "subcmd"} 864 before := Bool("before", false, "") 865 if err := GetCommandLine().Parse(os.Args[1:]); err != nil { 866 t.Fatal(err) 867 } 868 cmd := Arg(0) 869 os.Args = []string{"subcmd", "--after", "args"} 870 after := Bool("after", false, "") 871 Parse() 872 args := Args() 873 874 if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { 875 t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) 876 } 877} 878 879// Test that -help invokes the usage message and returns ErrHelp. 880func TestHelp(t *testing.T) { 881 var helpCalled = false 882 fs := NewFlagSet("help test", ContinueOnError) 883 fs.Usage = func() { helpCalled = true } 884 var flag bool 885 fs.BoolVar(&flag, "flag", false, "regular flag") 886 // Regular flag invocation should work 887 err := fs.Parse([]string{"--flag=true"}) 888 if err != nil { 889 t.Fatal("expected no error; got ", err) 890 } 891 if !flag { 892 t.Error("flag was not set by --flag") 893 } 894 if helpCalled { 895 t.Error("help called for regular flag") 896 helpCalled = false // reset for next test 897 } 898 // Help flag should work as expected. 899 err = fs.Parse([]string{"--help"}) 900 if err == nil { 901 t.Fatal("error expected") 902 } 903 if err != ErrHelp { 904 t.Fatal("expected ErrHelp; got ", err) 905 } 906 if !helpCalled { 907 t.Fatal("help was not called") 908 } 909 // If we define a help flag, that should override. 910 var help bool 911 fs.BoolVar(&help, "help", false, "help flag") 912 helpCalled = false 913 err = fs.Parse([]string{"--help"}) 914 if err != nil { 915 t.Fatal("expected no error for defined --help; got ", err) 916 } 917 if helpCalled { 918 t.Fatal("help was called; should not have been for defined help flag") 919 } 920} 921 922func TestNoInterspersed(t *testing.T) { 923 f := NewFlagSet("test", ContinueOnError) 924 f.SetInterspersed(false) 925 f.Bool("true", true, "always true") 926 f.Bool("false", false, "always false") 927 err := f.Parse([]string{"--true", "break", "--false"}) 928 if err != nil { 929 t.Fatal("expected no error; got ", err) 930 } 931 args := f.Args() 932 if len(args) != 2 || args[0] != "break" || args[1] != "--false" { 933 t.Fatal("expected interspersed options/non-options to fail") 934 } 935} 936 937func TestTermination(t *testing.T) { 938 f := NewFlagSet("termination", ContinueOnError) 939 boolFlag := f.BoolP("bool", "l", false, "bool value") 940 if f.Parsed() { 941 t.Error("f.Parse() = true before Parse") 942 } 943 arg1 := "ls" 944 arg2 := "-l" 945 args := []string{ 946 "--", 947 arg1, 948 arg2, 949 } 950 f.SetOutput(ioutil.Discard) 951 if err := f.Parse(args); err != nil { 952 t.Fatal("expected no error; got ", err) 953 } 954 if !f.Parsed() { 955 t.Error("f.Parse() = false after Parse") 956 } 957 if *boolFlag { 958 t.Error("expected boolFlag=false, got true") 959 } 960 if len(f.Args()) != 2 { 961 t.Errorf("expected 2 arguments, got %d: %v", len(f.Args()), f.Args()) 962 } 963 if f.Args()[0] != arg1 { 964 t.Errorf("expected argument %q got %q", arg1, f.Args()[0]) 965 } 966 if f.Args()[1] != arg2 { 967 t.Errorf("expected argument %q got %q", arg2, f.Args()[1]) 968 } 969 if f.ArgsLenAtDash() != 0 { 970 t.Errorf("expected argsLenAtDash %d got %d", 0, f.ArgsLenAtDash()) 971 } 972} 973 974func getDeprecatedFlagSet() *FlagSet { 975 f := NewFlagSet("bob", ContinueOnError) 976 f.Bool("badflag", true, "always true") 977 f.MarkDeprecated("badflag", "use --good-flag instead") 978 return f 979} 980func TestDeprecatedFlagInDocs(t *testing.T) { 981 f := getDeprecatedFlagSet() 982 983 out := new(bytes.Buffer) 984 f.SetOutput(out) 985 f.PrintDefaults() 986 987 if strings.Contains(out.String(), "badflag") { 988 t.Errorf("found deprecated flag in usage!") 989 } 990} 991 992func TestUnHiddenDeprecatedFlagInDocs(t *testing.T) { 993 f := getDeprecatedFlagSet() 994 flg := f.Lookup("badflag") 995 if flg == nil { 996 t.Fatalf("Unable to lookup 'bob' in TestUnHiddenDeprecatedFlagInDocs") 997 } 998 flg.Hidden = false 999 1000 out := new(bytes.Buffer) 1001 f.SetOutput(out) 1002 f.PrintDefaults() 1003 1004 defaults := out.String() 1005 if !strings.Contains(defaults, "badflag") { 1006 t.Errorf("Did not find deprecated flag in usage!") 1007 } 1008 if !strings.Contains(defaults, "use --good-flag instead") { 1009 t.Errorf("Did not find 'use --good-flag instead' in defaults") 1010 } 1011} 1012 1013func TestDeprecatedFlagShorthandInDocs(t *testing.T) { 1014 f := NewFlagSet("bob", ContinueOnError) 1015 name := "noshorthandflag" 1016 f.BoolP(name, "n", true, "always true") 1017 f.MarkShorthandDeprecated("noshorthandflag", fmt.Sprintf("use --%s instead", name)) 1018 1019 out := new(bytes.Buffer) 1020 f.SetOutput(out) 1021 f.PrintDefaults() 1022 1023 if strings.Contains(out.String(), "-n,") { 1024 t.Errorf("found deprecated flag shorthand in usage!") 1025 } 1026} 1027 1028func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) { 1029 oldStderr := os.Stderr 1030 r, w, _ := os.Pipe() 1031 os.Stderr = w 1032 1033 err := f.Parse(args) 1034 1035 outC := make(chan string) 1036 // copy the output in a separate goroutine so printing can't block indefinitely 1037 go func() { 1038 var buf bytes.Buffer 1039 io.Copy(&buf, r) 1040 outC <- buf.String() 1041 }() 1042 1043 w.Close() 1044 os.Stderr = oldStderr 1045 out := <-outC 1046 1047 return out, err 1048} 1049 1050func TestDeprecatedFlagUsage(t *testing.T) { 1051 f := NewFlagSet("bob", ContinueOnError) 1052 f.Bool("badflag", true, "always true") 1053 usageMsg := "use --good-flag instead" 1054 f.MarkDeprecated("badflag", usageMsg) 1055 1056 args := []string{"--badflag"} 1057 out, err := parseReturnStderr(t, f, args) 1058 if err != nil { 1059 t.Fatal("expected no error; got ", err) 1060 } 1061 1062 if !strings.Contains(out, usageMsg) { 1063 t.Errorf("usageMsg not printed when using a deprecated flag!") 1064 } 1065} 1066 1067func TestDeprecatedFlagShorthandUsage(t *testing.T) { 1068 f := NewFlagSet("bob", ContinueOnError) 1069 name := "noshorthandflag" 1070 f.BoolP(name, "n", true, "always true") 1071 usageMsg := fmt.Sprintf("use --%s instead", name) 1072 f.MarkShorthandDeprecated(name, usageMsg) 1073 1074 args := []string{"-n"} 1075 out, err := parseReturnStderr(t, f, args) 1076 if err != nil { 1077 t.Fatal("expected no error; got ", err) 1078 } 1079 1080 if !strings.Contains(out, usageMsg) { 1081 t.Errorf("usageMsg not printed when using a deprecated flag!") 1082 } 1083} 1084 1085func TestDeprecatedFlagUsageNormalized(t *testing.T) { 1086 f := NewFlagSet("bob", ContinueOnError) 1087 f.Bool("bad-double_flag", true, "always true") 1088 f.SetNormalizeFunc(wordSepNormalizeFunc) 1089 usageMsg := "use --good-flag instead" 1090 f.MarkDeprecated("bad_double-flag", usageMsg) 1091 1092 args := []string{"--bad_double_flag"} 1093 out, err := parseReturnStderr(t, f, args) 1094 if err != nil { 1095 t.Fatal("expected no error; got ", err) 1096 } 1097 1098 if !strings.Contains(out, usageMsg) { 1099 t.Errorf("usageMsg not printed when using a deprecated flag!") 1100 } 1101} 1102 1103// Name normalization function should be called only once on flag addition 1104func TestMultipleNormalizeFlagNameInvocations(t *testing.T) { 1105 normalizeFlagNameInvocations = 0 1106 1107 f := NewFlagSet("normalized", ContinueOnError) 1108 f.SetNormalizeFunc(wordSepNormalizeFunc) 1109 f.Bool("with_under_flag", false, "bool value") 1110 1111 if normalizeFlagNameInvocations != 1 { 1112 t.Fatal("Expected normalizeFlagNameInvocations to be 1; got ", normalizeFlagNameInvocations) 1113 } 1114} 1115 1116// 1117func TestHiddenFlagInUsage(t *testing.T) { 1118 f := NewFlagSet("bob", ContinueOnError) 1119 f.Bool("secretFlag", true, "shhh") 1120 f.MarkHidden("secretFlag") 1121 1122 out := new(bytes.Buffer) 1123 f.SetOutput(out) 1124 f.PrintDefaults() 1125 1126 if strings.Contains(out.String(), "secretFlag") { 1127 t.Errorf("found hidden flag in usage!") 1128 } 1129} 1130 1131// 1132func TestHiddenFlagUsage(t *testing.T) { 1133 f := NewFlagSet("bob", ContinueOnError) 1134 f.Bool("secretFlag", true, "shhh") 1135 f.MarkHidden("secretFlag") 1136 1137 args := []string{"--secretFlag"} 1138 out, err := parseReturnStderr(t, f, args) 1139 if err != nil { 1140 t.Fatal("expected no error; got ", err) 1141 } 1142 1143 if strings.Contains(out, "shhh") { 1144 t.Errorf("usage message printed when using a hidden flag!") 1145 } 1146} 1147 1148const defaultOutput = ` --A for bootstrapping, allow 'any' type 1149 --Alongflagname disable bounds checking 1150 -C, --CCC a boolean defaulting to true (default true) 1151 --D path set relative path for local imports 1152 -E, --EEE num[=1234] a num with NoOptDefVal (default 4321) 1153 --F number a non-zero number (default 2.7) 1154 --G float a float that defaults to zero 1155 --IP ip IP address with no default 1156 --IPMask ipMask Netmask address with no default 1157 --IPNet ipNet IP network with no default 1158 --Ints ints int slice with zero default 1159 --N int a non-zero int (default 27) 1160 --ND1 string[="bar"] a string with NoOptDefVal (default "foo") 1161 --ND2 num[=4321] a num with NoOptDefVal (default 1234) 1162 --StringArray stringArray string array with zero default 1163 --StringSlice strings string slice with zero default 1164 --Z int an int that defaults to zero 1165 --custom custom custom Value implementation 1166 --customP custom a VarP with default (default 10) 1167 --maxT timeout set timeout for dial 1168 -v, --verbose count verbosity 1169` 1170 1171// Custom value that satisfies the Value interface. 1172type customValue int 1173 1174func (cv *customValue) String() string { return fmt.Sprintf("%v", *cv) } 1175 1176func (cv *customValue) Set(s string) error { 1177 v, err := strconv.ParseInt(s, 0, 64) 1178 *cv = customValue(v) 1179 return err 1180} 1181 1182func (cv *customValue) Type() string { return "custom" } 1183 1184func TestPrintDefaults(t *testing.T) { 1185 fs := NewFlagSet("print defaults test", ContinueOnError) 1186 var buf bytes.Buffer 1187 fs.SetOutput(&buf) 1188 fs.Bool("A", false, "for bootstrapping, allow 'any' type") 1189 fs.Bool("Alongflagname", false, "disable bounds checking") 1190 fs.BoolP("CCC", "C", true, "a boolean defaulting to true") 1191 fs.String("D", "", "set relative `path` for local imports") 1192 fs.Float64("F", 2.7, "a non-zero `number`") 1193 fs.Float64("G", 0, "a float that defaults to zero") 1194 fs.Int("N", 27, "a non-zero int") 1195 fs.IntSlice("Ints", []int{}, "int slice with zero default") 1196 fs.IP("IP", nil, "IP address with no default") 1197 fs.IPMask("IPMask", nil, "Netmask address with no default") 1198 fs.IPNet("IPNet", net.IPNet{}, "IP network with no default") 1199 fs.Int("Z", 0, "an int that defaults to zero") 1200 fs.Duration("maxT", 0, "set `timeout` for dial") 1201 fs.String("ND1", "foo", "a string with NoOptDefVal") 1202 fs.Lookup("ND1").NoOptDefVal = "bar" 1203 fs.Int("ND2", 1234, "a `num` with NoOptDefVal") 1204 fs.Lookup("ND2").NoOptDefVal = "4321" 1205 fs.IntP("EEE", "E", 4321, "a `num` with NoOptDefVal") 1206 fs.ShorthandLookup("E").NoOptDefVal = "1234" 1207 fs.StringSlice("StringSlice", []string{}, "string slice with zero default") 1208 fs.StringArray("StringArray", []string{}, "string array with zero default") 1209 fs.CountP("verbose", "v", "verbosity") 1210 1211 var cv customValue 1212 fs.Var(&cv, "custom", "custom Value implementation") 1213 1214 cv2 := customValue(10) 1215 fs.VarP(&cv2, "customP", "", "a VarP with default") 1216 1217 fs.PrintDefaults() 1218 got := buf.String() 1219 if got != defaultOutput { 1220 fmt.Println("\n" + got) 1221 fmt.Println("\n" + defaultOutput) 1222 t.Errorf("got %q want %q\n", got, defaultOutput) 1223 } 1224} 1225 1226func TestVisitAllFlagOrder(t *testing.T) { 1227 fs := NewFlagSet("TestVisitAllFlagOrder", ContinueOnError) 1228 fs.SortFlags = false 1229 // https://github.com/spf13/pflag/issues/120 1230 fs.SetNormalizeFunc(func(f *FlagSet, name string) NormalizedName { 1231 return NormalizedName(name) 1232 }) 1233 1234 names := []string{"C", "B", "A", "D"} 1235 for _, name := range names { 1236 fs.Bool(name, false, "") 1237 } 1238 1239 i := 0 1240 fs.VisitAll(func(f *Flag) { 1241 if names[i] != f.Name { 1242 t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name) 1243 } 1244 i++ 1245 }) 1246} 1247 1248func TestVisitFlagOrder(t *testing.T) { 1249 fs := NewFlagSet("TestVisitFlagOrder", ContinueOnError) 1250 fs.SortFlags = false 1251 names := []string{"C", "B", "A", "D"} 1252 for _, name := range names { 1253 fs.Bool(name, false, "") 1254 fs.Set(name, "true") 1255 } 1256 1257 i := 0 1258 fs.Visit(func(f *Flag) { 1259 if names[i] != f.Name { 1260 t.Errorf("Incorrect order. Expected %v, got %v", names[i], f.Name) 1261 } 1262 i++ 1263 }) 1264} 1265