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 }