1package cli 2 3import ( 4 "fmt" 5 "os" 6 "reflect" 7 "regexp" 8 "runtime" 9 "strings" 10 "testing" 11 "time" 12) 13 14var boolFlagTests = []struct { 15 name string 16 expected string 17}{ 18 {"help", "--help\t"}, 19 {"h", "-h\t"}, 20} 21 22func TestBoolFlagHelpOutput(t *testing.T) { 23 for _, test := range boolFlagTests { 24 flag := BoolFlag{Name: test.name} 25 output := flag.String() 26 27 if output != test.expected { 28 t.Errorf("%q does not match %q", output, test.expected) 29 } 30 } 31} 32 33func TestFlagsFromEnv(t *testing.T) { 34 var flagTests = []struct { 35 input string 36 output interface{} 37 flag Flag 38 errRegexp string 39 }{ 40 {"", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, 41 {"1", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, 42 {"false", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, 43 {"foobar", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Sprintf(`could not parse foobar as bool value for flag debug: .*`)}, 44 45 {"", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, 46 {"1", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, 47 {"false", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, 48 {"foobar", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Sprintf(`could not parse foobar as bool value for flag debug: .*`)}, 49 50 {"1s", 1 * time.Second, DurationFlag{Name: "time", EnvVar: "TIME"}, ""}, 51 {"foobar", false, DurationFlag{Name: "time", EnvVar: "TIME"}, fmt.Sprintf(`could not parse foobar as duration for flag time: .*`)}, 52 53 {"1.2", 1.2, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, 54 {"1", 1.0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, 55 {"foobar", 0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as float64 value for flag seconds: .*`)}, 56 57 {"1", int64(1), Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, 58 {"1.2", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as int value for flag seconds: .*`)}, 59 {"foobar", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int value for flag seconds: .*`)}, 60 61 {"1", 1, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, 62 {"1.2", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as int value for flag seconds: .*`)}, 63 {"foobar", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int value for flag seconds: .*`)}, 64 65 {"1,2", IntSlice{1, 2}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, 66 {"1.2,2", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2,2 as int slice value for flag seconds: .*`)}, 67 {"foobar", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int slice value for flag seconds: .*`)}, 68 69 {"1,2", Int64Slice{1, 2}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, 70 {"1.2,2", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2,2 as int64 slice value for flag seconds: .*`)}, 71 {"foobar", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int64 slice value for flag seconds: .*`)}, 72 73 {"foo", "foo", StringFlag{Name: "name", EnvVar: "NAME"}, ""}, 74 75 {"foo,bar", StringSlice{"foo", "bar"}, StringSliceFlag{Name: "names", EnvVar: "NAMES"}, ""}, 76 77 {"1", uint(1), UintFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, 78 {"1.2", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as uint value for flag seconds: .*`)}, 79 {"foobar", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as uint value for flag seconds: .*`)}, 80 81 {"1", uint64(1), Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, 82 {"1.2", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as uint64 value for flag seconds: .*`)}, 83 {"foobar", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as uint64 value for flag seconds: .*`)}, 84 85 {"foo,bar", &Parser{"foo", "bar"}, GenericFlag{Name: "names", Value: &Parser{}, EnvVar: "NAMES"}, ""}, 86 } 87 88 for _, test := range flagTests { 89 os.Clearenv() 90 os.Setenv(reflect.ValueOf(test.flag).FieldByName("EnvVar").String(), test.input) 91 a := App{ 92 Flags: []Flag{test.flag}, 93 Action: func(ctx *Context) error { 94 if !reflect.DeepEqual(ctx.value(test.flag.GetName()), test.output) { 95 t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.value(test.flag.GetName())) 96 } 97 return nil 98 }, 99 } 100 101 err := a.Run([]string{"run"}) 102 103 if test.errRegexp != "" { 104 if err == nil { 105 t.Errorf("expected error to match %s, got none", test.errRegexp) 106 } else { 107 if matched, _ := regexp.MatchString(test.errRegexp, err.Error()); !matched { 108 t.Errorf("expected error to match %s, got error %s", test.errRegexp, err) 109 } 110 } 111 } else { 112 if err != nil && test.errRegexp == "" { 113 t.Errorf("expected no error got %s", err) 114 } 115 } 116 } 117} 118 119var stringFlagTests = []struct { 120 name string 121 usage string 122 value string 123 expected string 124}{ 125 {"foo", "", "", "--foo value\t"}, 126 {"f", "", "", "-f value\t"}, 127 {"f", "The total `foo` desired", "all", "-f foo\tThe total foo desired (default: \"all\")"}, 128 {"test", "", "Something", "--test value\t(default: \"Something\")"}, 129 {"config,c", "Load configuration from `FILE`", "", "--config FILE, -c FILE\tLoad configuration from FILE"}, 130 {"config,c", "Load configuration from `CONFIG`", "config.json", "--config CONFIG, -c CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"}, 131} 132 133func TestStringFlagHelpOutput(t *testing.T) { 134 for _, test := range stringFlagTests { 135 flag := StringFlag{Name: test.name, Usage: test.usage, Value: test.value} 136 output := flag.String() 137 138 if output != test.expected { 139 t.Errorf("%q does not match %q", output, test.expected) 140 } 141 } 142} 143 144func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { 145 os.Clearenv() 146 os.Setenv("APP_FOO", "derp") 147 for _, test := range stringFlagTests { 148 flag := StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"} 149 output := flag.String() 150 151 expectedSuffix := " [$APP_FOO]" 152 if runtime.GOOS == "windows" { 153 expectedSuffix = " [%APP_FOO%]" 154 } 155 if !strings.HasSuffix(output, expectedSuffix) { 156 t.Errorf("%s does not end with"+expectedSuffix, output) 157 } 158 } 159} 160 161var stringSliceFlagTests = []struct { 162 name string 163 value *StringSlice 164 expected string 165}{ 166 {"foo", func() *StringSlice { 167 s := &StringSlice{} 168 s.Set("") 169 return s 170 }(), "--foo value\t"}, 171 {"f", func() *StringSlice { 172 s := &StringSlice{} 173 s.Set("") 174 return s 175 }(), "-f value\t"}, 176 {"f", func() *StringSlice { 177 s := &StringSlice{} 178 s.Set("Lipstick") 179 return s 180 }(), "-f value\t(default: \"Lipstick\")"}, 181 {"test", func() *StringSlice { 182 s := &StringSlice{} 183 s.Set("Something") 184 return s 185 }(), "--test value\t(default: \"Something\")"}, 186} 187 188func TestStringSliceFlagHelpOutput(t *testing.T) { 189 for _, test := range stringSliceFlagTests { 190 flag := StringSliceFlag{Name: test.name, Value: test.value} 191 output := flag.String() 192 193 if output != test.expected { 194 t.Errorf("%q does not match %q", output, test.expected) 195 } 196 } 197} 198 199func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { 200 os.Clearenv() 201 os.Setenv("APP_QWWX", "11,4") 202 for _, test := range stringSliceFlagTests { 203 flag := StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"} 204 output := flag.String() 205 206 expectedSuffix := " [$APP_QWWX]" 207 if runtime.GOOS == "windows" { 208 expectedSuffix = " [%APP_QWWX%]" 209 } 210 if !strings.HasSuffix(output, expectedSuffix) { 211 t.Errorf("%q does not end with"+expectedSuffix, output) 212 } 213 } 214} 215 216var intFlagTests = []struct { 217 name string 218 expected string 219}{ 220 {"hats", "--hats value\t(default: 9)"}, 221 {"H", "-H value\t(default: 9)"}, 222} 223 224func TestIntFlagHelpOutput(t *testing.T) { 225 for _, test := range intFlagTests { 226 flag := IntFlag{Name: test.name, Value: 9} 227 output := flag.String() 228 229 if output != test.expected { 230 t.Errorf("%s does not match %s", output, test.expected) 231 } 232 } 233} 234 235func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { 236 os.Clearenv() 237 os.Setenv("APP_BAR", "2") 238 for _, test := range intFlagTests { 239 flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} 240 output := flag.String() 241 242 expectedSuffix := " [$APP_BAR]" 243 if runtime.GOOS == "windows" { 244 expectedSuffix = " [%APP_BAR%]" 245 } 246 if !strings.HasSuffix(output, expectedSuffix) { 247 t.Errorf("%s does not end with"+expectedSuffix, output) 248 } 249 } 250} 251 252var int64FlagTests = []struct { 253 name string 254 expected string 255}{ 256 {"hats", "--hats value\t(default: 8589934592)"}, 257 {"H", "-H value\t(default: 8589934592)"}, 258} 259 260func TestInt64FlagHelpOutput(t *testing.T) { 261 for _, test := range int64FlagTests { 262 flag := Int64Flag{Name: test.name, Value: 8589934592} 263 output := flag.String() 264 265 if output != test.expected { 266 t.Errorf("%s does not match %s", output, test.expected) 267 } 268 } 269} 270 271func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) { 272 os.Clearenv() 273 os.Setenv("APP_BAR", "2") 274 for _, test := range int64FlagTests { 275 flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} 276 output := flag.String() 277 278 expectedSuffix := " [$APP_BAR]" 279 if runtime.GOOS == "windows" { 280 expectedSuffix = " [%APP_BAR%]" 281 } 282 if !strings.HasSuffix(output, expectedSuffix) { 283 t.Errorf("%s does not end with"+expectedSuffix, output) 284 } 285 } 286} 287 288var uintFlagTests = []struct { 289 name string 290 expected string 291}{ 292 {"nerfs", "--nerfs value\t(default: 41)"}, 293 {"N", "-N value\t(default: 41)"}, 294} 295 296func TestUintFlagHelpOutput(t *testing.T) { 297 for _, test := range uintFlagTests { 298 flag := UintFlag{Name: test.name, Value: 41} 299 output := flag.String() 300 301 if output != test.expected { 302 t.Errorf("%s does not match %s", output, test.expected) 303 } 304 } 305} 306 307func TestUintFlagWithEnvVarHelpOutput(t *testing.T) { 308 os.Clearenv() 309 os.Setenv("APP_BAR", "2") 310 for _, test := range uintFlagTests { 311 flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} 312 output := flag.String() 313 314 expectedSuffix := " [$APP_BAR]" 315 if runtime.GOOS == "windows" { 316 expectedSuffix = " [%APP_BAR%]" 317 } 318 if !strings.HasSuffix(output, expectedSuffix) { 319 t.Errorf("%s does not end with"+expectedSuffix, output) 320 } 321 } 322} 323 324var uint64FlagTests = []struct { 325 name string 326 expected string 327}{ 328 {"gerfs", "--gerfs value\t(default: 8589934582)"}, 329 {"G", "-G value\t(default: 8589934582)"}, 330} 331 332func TestUint64FlagHelpOutput(t *testing.T) { 333 for _, test := range uint64FlagTests { 334 flag := Uint64Flag{Name: test.name, Value: 8589934582} 335 output := flag.String() 336 337 if output != test.expected { 338 t.Errorf("%s does not match %s", output, test.expected) 339 } 340 } 341} 342 343func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) { 344 os.Clearenv() 345 os.Setenv("APP_BAR", "2") 346 for _, test := range uint64FlagTests { 347 flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} 348 output := flag.String() 349 350 expectedSuffix := " [$APP_BAR]" 351 if runtime.GOOS == "windows" { 352 expectedSuffix = " [%APP_BAR%]" 353 } 354 if !strings.HasSuffix(output, expectedSuffix) { 355 t.Errorf("%s does not end with"+expectedSuffix, output) 356 } 357 } 358} 359 360var durationFlagTests = []struct { 361 name string 362 expected string 363}{ 364 {"hooting", "--hooting value\t(default: 1s)"}, 365 {"H", "-H value\t(default: 1s)"}, 366} 367 368func TestDurationFlagHelpOutput(t *testing.T) { 369 for _, test := range durationFlagTests { 370 flag := DurationFlag{Name: test.name, Value: 1 * time.Second} 371 output := flag.String() 372 373 if output != test.expected { 374 t.Errorf("%q does not match %q", output, test.expected) 375 } 376 } 377} 378 379func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { 380 os.Clearenv() 381 os.Setenv("APP_BAR", "2h3m6s") 382 for _, test := range durationFlagTests { 383 flag := DurationFlag{Name: test.name, EnvVar: "APP_BAR"} 384 output := flag.String() 385 386 expectedSuffix := " [$APP_BAR]" 387 if runtime.GOOS == "windows" { 388 expectedSuffix = " [%APP_BAR%]" 389 } 390 if !strings.HasSuffix(output, expectedSuffix) { 391 t.Errorf("%s does not end with"+expectedSuffix, output) 392 } 393 } 394} 395 396var intSliceFlagTests = []struct { 397 name string 398 value *IntSlice 399 expected string 400}{ 401 {"heads", &IntSlice{}, "--heads value\t"}, 402 {"H", &IntSlice{}, "-H value\t"}, 403 {"H, heads", func() *IntSlice { 404 i := &IntSlice{} 405 i.Set("9") 406 i.Set("3") 407 return i 408 }(), "-H value, --heads value\t(default: 9, 3)"}, 409} 410 411func TestIntSliceFlagHelpOutput(t *testing.T) { 412 for _, test := range intSliceFlagTests { 413 flag := IntSliceFlag{Name: test.name, Value: test.value} 414 output := flag.String() 415 416 if output != test.expected { 417 t.Errorf("%q does not match %q", output, test.expected) 418 } 419 } 420} 421 422func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { 423 os.Clearenv() 424 os.Setenv("APP_SMURF", "42,3") 425 for _, test := range intSliceFlagTests { 426 flag := IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} 427 output := flag.String() 428 429 expectedSuffix := " [$APP_SMURF]" 430 if runtime.GOOS == "windows" { 431 expectedSuffix = " [%APP_SMURF%]" 432 } 433 if !strings.HasSuffix(output, expectedSuffix) { 434 t.Errorf("%q does not end with"+expectedSuffix, output) 435 } 436 } 437} 438 439var int64SliceFlagTests = []struct { 440 name string 441 value *Int64Slice 442 expected string 443}{ 444 {"heads", &Int64Slice{}, "--heads value\t"}, 445 {"H", &Int64Slice{}, "-H value\t"}, 446 {"H, heads", func() *Int64Slice { 447 i := &Int64Slice{} 448 i.Set("2") 449 i.Set("17179869184") 450 return i 451 }(), "-H value, --heads value\t(default: 2, 17179869184)"}, 452} 453 454func TestInt64SliceFlagHelpOutput(t *testing.T) { 455 for _, test := range int64SliceFlagTests { 456 flag := Int64SliceFlag{Name: test.name, Value: test.value} 457 output := flag.String() 458 459 if output != test.expected { 460 t.Errorf("%q does not match %q", output, test.expected) 461 } 462 } 463} 464 465func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) { 466 os.Clearenv() 467 os.Setenv("APP_SMURF", "42,17179869184") 468 for _, test := range int64SliceFlagTests { 469 flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} 470 output := flag.String() 471 472 expectedSuffix := " [$APP_SMURF]" 473 if runtime.GOOS == "windows" { 474 expectedSuffix = " [%APP_SMURF%]" 475 } 476 if !strings.HasSuffix(output, expectedSuffix) { 477 t.Errorf("%q does not end with"+expectedSuffix, output) 478 } 479 } 480} 481 482var float64FlagTests = []struct { 483 name string 484 expected string 485}{ 486 {"hooting", "--hooting value\t(default: 0.1)"}, 487 {"H", "-H value\t(default: 0.1)"}, 488} 489 490func TestFloat64FlagHelpOutput(t *testing.T) { 491 for _, test := range float64FlagTests { 492 flag := Float64Flag{Name: test.name, Value: float64(0.1)} 493 output := flag.String() 494 495 if output != test.expected { 496 t.Errorf("%q does not match %q", output, test.expected) 497 } 498 } 499} 500 501func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { 502 os.Clearenv() 503 os.Setenv("APP_BAZ", "99.4") 504 for _, test := range float64FlagTests { 505 flag := Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} 506 output := flag.String() 507 508 expectedSuffix := " [$APP_BAZ]" 509 if runtime.GOOS == "windows" { 510 expectedSuffix = " [%APP_BAZ%]" 511 } 512 if !strings.HasSuffix(output, expectedSuffix) { 513 t.Errorf("%s does not end with"+expectedSuffix, output) 514 } 515 } 516} 517 518var genericFlagTests = []struct { 519 name string 520 value Generic 521 expected string 522}{ 523 {"toads", &Parser{"abc", "def"}, "--toads value\ttest flag (default: abc,def)"}, 524 {"t", &Parser{"abc", "def"}, "-t value\ttest flag (default: abc,def)"}, 525} 526 527func TestGenericFlagHelpOutput(t *testing.T) { 528 for _, test := range genericFlagTests { 529 flag := GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"} 530 output := flag.String() 531 532 if output != test.expected { 533 t.Errorf("%q does not match %q", output, test.expected) 534 } 535 } 536} 537 538func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { 539 os.Clearenv() 540 os.Setenv("APP_ZAP", "3") 541 for _, test := range genericFlagTests { 542 flag := GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} 543 output := flag.String() 544 545 expectedSuffix := " [$APP_ZAP]" 546 if runtime.GOOS == "windows" { 547 expectedSuffix = " [%APP_ZAP%]" 548 } 549 if !strings.HasSuffix(output, expectedSuffix) { 550 t.Errorf("%s does not end with"+expectedSuffix, output) 551 } 552 } 553} 554 555func TestParseMultiString(t *testing.T) { 556 (&App{ 557 Flags: []Flag{ 558 StringFlag{Name: "serve, s"}, 559 }, 560 Action: func(ctx *Context) error { 561 if ctx.String("serve") != "10" { 562 t.Errorf("main name not set") 563 } 564 if ctx.String("s") != "10" { 565 t.Errorf("short name not set") 566 } 567 return nil 568 }, 569 }).Run([]string{"run", "-s", "10"}) 570} 571 572func TestParseDestinationString(t *testing.T) { 573 var dest string 574 a := App{ 575 Flags: []Flag{ 576 StringFlag{ 577 Name: "dest", 578 Destination: &dest, 579 }, 580 }, 581 Action: func(ctx *Context) error { 582 if dest != "10" { 583 t.Errorf("expected destination String 10") 584 } 585 return nil 586 }, 587 } 588 a.Run([]string{"run", "--dest", "10"}) 589} 590 591func TestParseMultiStringFromEnv(t *testing.T) { 592 os.Clearenv() 593 os.Setenv("APP_COUNT", "20") 594 (&App{ 595 Flags: []Flag{ 596 StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, 597 }, 598 Action: func(ctx *Context) error { 599 if ctx.String("count") != "20" { 600 t.Errorf("main name not set") 601 } 602 if ctx.String("c") != "20" { 603 t.Errorf("short name not set") 604 } 605 return nil 606 }, 607 }).Run([]string{"run"}) 608} 609 610func TestParseMultiStringFromEnvCascade(t *testing.T) { 611 os.Clearenv() 612 os.Setenv("APP_COUNT", "20") 613 (&App{ 614 Flags: []Flag{ 615 StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"}, 616 }, 617 Action: func(ctx *Context) error { 618 if ctx.String("count") != "20" { 619 t.Errorf("main name not set") 620 } 621 if ctx.String("c") != "20" { 622 t.Errorf("short name not set") 623 } 624 return nil 625 }, 626 }).Run([]string{"run"}) 627} 628 629func TestParseMultiStringSlice(t *testing.T) { 630 (&App{ 631 Flags: []Flag{ 632 StringSliceFlag{Name: "serve, s", Value: &StringSlice{}}, 633 }, 634 Action: func(ctx *Context) error { 635 if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) { 636 t.Errorf("main name not set") 637 } 638 if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) { 639 t.Errorf("short name not set") 640 } 641 return nil 642 }, 643 }).Run([]string{"run", "-s", "10", "-s", "20"}) 644} 645 646func TestParseMultiStringSliceFromEnv(t *testing.T) { 647 os.Clearenv() 648 os.Setenv("APP_INTERVALS", "20,30,40") 649 650 (&App{ 651 Flags: []Flag{ 652 StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "APP_INTERVALS"}, 653 }, 654 Action: func(ctx *Context) error { 655 if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { 656 t.Errorf("main name not set from env") 657 } 658 if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { 659 t.Errorf("short name not set from env") 660 } 661 return nil 662 }, 663 }).Run([]string{"run"}) 664} 665 666func TestParseMultiStringSliceFromEnvCascade(t *testing.T) { 667 os.Clearenv() 668 os.Setenv("APP_INTERVALS", "20,30,40") 669 670 (&App{ 671 Flags: []Flag{ 672 StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, 673 }, 674 Action: func(ctx *Context) error { 675 if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { 676 t.Errorf("main name not set from env") 677 } 678 if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { 679 t.Errorf("short name not set from env") 680 } 681 return nil 682 }, 683 }).Run([]string{"run"}) 684} 685 686func TestParseMultiInt(t *testing.T) { 687 a := App{ 688 Flags: []Flag{ 689 IntFlag{Name: "serve, s"}, 690 }, 691 Action: func(ctx *Context) error { 692 if ctx.Int("serve") != 10 { 693 t.Errorf("main name not set") 694 } 695 if ctx.Int("s") != 10 { 696 t.Errorf("short name not set") 697 } 698 return nil 699 }, 700 } 701 a.Run([]string{"run", "-s", "10"}) 702} 703 704func TestParseDestinationInt(t *testing.T) { 705 var dest int 706 a := App{ 707 Flags: []Flag{ 708 IntFlag{ 709 Name: "dest", 710 Destination: &dest, 711 }, 712 }, 713 Action: func(ctx *Context) error { 714 if dest != 10 { 715 t.Errorf("expected destination Int 10") 716 } 717 return nil 718 }, 719 } 720 a.Run([]string{"run", "--dest", "10"}) 721} 722 723func TestParseMultiIntFromEnv(t *testing.T) { 724 os.Clearenv() 725 os.Setenv("APP_TIMEOUT_SECONDS", "10") 726 a := App{ 727 Flags: []Flag{ 728 IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, 729 }, 730 Action: func(ctx *Context) error { 731 if ctx.Int("timeout") != 10 { 732 t.Errorf("main name not set") 733 } 734 if ctx.Int("t") != 10 { 735 t.Errorf("short name not set") 736 } 737 return nil 738 }, 739 } 740 a.Run([]string{"run"}) 741} 742 743func TestParseMultiIntFromEnvCascade(t *testing.T) { 744 os.Clearenv() 745 os.Setenv("APP_TIMEOUT_SECONDS", "10") 746 a := App{ 747 Flags: []Flag{ 748 IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, 749 }, 750 Action: func(ctx *Context) error { 751 if ctx.Int("timeout") != 10 { 752 t.Errorf("main name not set") 753 } 754 if ctx.Int("t") != 10 { 755 t.Errorf("short name not set") 756 } 757 return nil 758 }, 759 } 760 a.Run([]string{"run"}) 761} 762 763func TestParseMultiIntSlice(t *testing.T) { 764 (&App{ 765 Flags: []Flag{ 766 IntSliceFlag{Name: "serve, s", Value: &IntSlice{}}, 767 }, 768 Action: func(ctx *Context) error { 769 if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { 770 t.Errorf("main name not set") 771 } 772 if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) { 773 t.Errorf("short name not set") 774 } 775 return nil 776 }, 777 }).Run([]string{"run", "-s", "10", "-s", "20"}) 778} 779 780func TestParseMultiIntSliceFromEnv(t *testing.T) { 781 os.Clearenv() 782 os.Setenv("APP_INTERVALS", "20,30,40") 783 784 (&App{ 785 Flags: []Flag{ 786 IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "APP_INTERVALS"}, 787 }, 788 Action: func(ctx *Context) error { 789 if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { 790 t.Errorf("main name not set from env") 791 } 792 if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { 793 t.Errorf("short name not set from env") 794 } 795 return nil 796 }, 797 }).Run([]string{"run"}) 798} 799 800func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { 801 os.Clearenv() 802 os.Setenv("APP_INTERVALS", "20,30,40") 803 804 (&App{ 805 Flags: []Flag{ 806 IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, 807 }, 808 Action: func(ctx *Context) error { 809 if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { 810 t.Errorf("main name not set from env") 811 } 812 if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { 813 t.Errorf("short name not set from env") 814 } 815 return nil 816 }, 817 }).Run([]string{"run"}) 818} 819 820func TestParseMultiInt64Slice(t *testing.T) { 821 (&App{ 822 Flags: []Flag{ 823 Int64SliceFlag{Name: "serve, s", Value: &Int64Slice{}}, 824 }, 825 Action: func(ctx *Context) error { 826 if !reflect.DeepEqual(ctx.Int64Slice("serve"), []int64{10, 17179869184}) { 827 t.Errorf("main name not set") 828 } 829 if !reflect.DeepEqual(ctx.Int64Slice("s"), []int64{10, 17179869184}) { 830 t.Errorf("short name not set") 831 } 832 return nil 833 }, 834 }).Run([]string{"run", "-s", "10", "-s", "17179869184"}) 835} 836 837func TestParseMultiInt64SliceFromEnv(t *testing.T) { 838 os.Clearenv() 839 os.Setenv("APP_INTERVALS", "20,30,17179869184") 840 841 (&App{ 842 Flags: []Flag{ 843 Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "APP_INTERVALS"}, 844 }, 845 Action: func(ctx *Context) error { 846 if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) { 847 t.Errorf("main name not set from env") 848 } 849 if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) { 850 t.Errorf("short name not set from env") 851 } 852 return nil 853 }, 854 }).Run([]string{"run"}) 855} 856 857func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) { 858 os.Clearenv() 859 os.Setenv("APP_INTERVALS", "20,30,17179869184") 860 861 (&App{ 862 Flags: []Flag{ 863 Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, 864 }, 865 Action: func(ctx *Context) error { 866 if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) { 867 t.Errorf("main name not set from env") 868 } 869 if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) { 870 t.Errorf("short name not set from env") 871 } 872 return nil 873 }, 874 }).Run([]string{"run"}) 875} 876 877func TestParseMultiFloat64(t *testing.T) { 878 a := App{ 879 Flags: []Flag{ 880 Float64Flag{Name: "serve, s"}, 881 }, 882 Action: func(ctx *Context) error { 883 if ctx.Float64("serve") != 10.2 { 884 t.Errorf("main name not set") 885 } 886 if ctx.Float64("s") != 10.2 { 887 t.Errorf("short name not set") 888 } 889 return nil 890 }, 891 } 892 a.Run([]string{"run", "-s", "10.2"}) 893} 894 895func TestParseDestinationFloat64(t *testing.T) { 896 var dest float64 897 a := App{ 898 Flags: []Flag{ 899 Float64Flag{ 900 Name: "dest", 901 Destination: &dest, 902 }, 903 }, 904 Action: func(ctx *Context) error { 905 if dest != 10.2 { 906 t.Errorf("expected destination Float64 10.2") 907 } 908 return nil 909 }, 910 } 911 a.Run([]string{"run", "--dest", "10.2"}) 912} 913 914func TestParseMultiFloat64FromEnv(t *testing.T) { 915 os.Clearenv() 916 os.Setenv("APP_TIMEOUT_SECONDS", "15.5") 917 a := App{ 918 Flags: []Flag{ 919 Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, 920 }, 921 Action: func(ctx *Context) error { 922 if ctx.Float64("timeout") != 15.5 { 923 t.Errorf("main name not set") 924 } 925 if ctx.Float64("t") != 15.5 { 926 t.Errorf("short name not set") 927 } 928 return nil 929 }, 930 } 931 a.Run([]string{"run"}) 932} 933 934func TestParseMultiFloat64FromEnvCascade(t *testing.T) { 935 os.Clearenv() 936 os.Setenv("APP_TIMEOUT_SECONDS", "15.5") 937 a := App{ 938 Flags: []Flag{ 939 Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, 940 }, 941 Action: func(ctx *Context) error { 942 if ctx.Float64("timeout") != 15.5 { 943 t.Errorf("main name not set") 944 } 945 if ctx.Float64("t") != 15.5 { 946 t.Errorf("short name not set") 947 } 948 return nil 949 }, 950 } 951 a.Run([]string{"run"}) 952} 953 954func TestParseMultiBool(t *testing.T) { 955 a := App{ 956 Flags: []Flag{ 957 BoolFlag{Name: "serve, s"}, 958 }, 959 Action: func(ctx *Context) error { 960 if ctx.Bool("serve") != true { 961 t.Errorf("main name not set") 962 } 963 if ctx.Bool("s") != true { 964 t.Errorf("short name not set") 965 } 966 return nil 967 }, 968 } 969 a.Run([]string{"run", "--serve"}) 970} 971 972func TestParseDestinationBool(t *testing.T) { 973 var dest bool 974 a := App{ 975 Flags: []Flag{ 976 BoolFlag{ 977 Name: "dest", 978 Destination: &dest, 979 }, 980 }, 981 Action: func(ctx *Context) error { 982 if dest != true { 983 t.Errorf("expected destination Bool true") 984 } 985 return nil 986 }, 987 } 988 a.Run([]string{"run", "--dest"}) 989} 990 991func TestParseMultiBoolFromEnv(t *testing.T) { 992 os.Clearenv() 993 os.Setenv("APP_DEBUG", "1") 994 a := App{ 995 Flags: []Flag{ 996 BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, 997 }, 998 Action: func(ctx *Context) error { 999 if ctx.Bool("debug") != true { 1000 t.Errorf("main name not set from env") 1001 } 1002 if ctx.Bool("d") != true { 1003 t.Errorf("short name not set from env") 1004 } 1005 return nil 1006 }, 1007 } 1008 a.Run([]string{"run"}) 1009} 1010 1011func TestParseMultiBoolFromEnvCascade(t *testing.T) { 1012 os.Clearenv() 1013 os.Setenv("APP_DEBUG", "1") 1014 a := App{ 1015 Flags: []Flag{ 1016 BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, 1017 }, 1018 Action: func(ctx *Context) error { 1019 if ctx.Bool("debug") != true { 1020 t.Errorf("main name not set from env") 1021 } 1022 if ctx.Bool("d") != true { 1023 t.Errorf("short name not set from env") 1024 } 1025 return nil 1026 }, 1027 } 1028 a.Run([]string{"run"}) 1029} 1030 1031func TestParseBoolTFromEnv(t *testing.T) { 1032 var boolTFlagTests = []struct { 1033 input string 1034 output bool 1035 }{ 1036 {"", false}, 1037 {"1", true}, 1038 {"false", false}, 1039 {"true", true}, 1040 } 1041 1042 for _, test := range boolTFlagTests { 1043 os.Clearenv() 1044 os.Setenv("DEBUG", test.input) 1045 a := App{ 1046 Flags: []Flag{ 1047 BoolTFlag{Name: "debug, d", EnvVar: "DEBUG"}, 1048 }, 1049 Action: func(ctx *Context) error { 1050 if ctx.Bool("debug") != test.output { 1051 t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("debug")) 1052 } 1053 if ctx.Bool("d") != test.output { 1054 t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("d")) 1055 } 1056 return nil 1057 }, 1058 } 1059 a.Run([]string{"run"}) 1060 } 1061} 1062 1063func TestParseMultiBoolT(t *testing.T) { 1064 a := App{ 1065 Flags: []Flag{ 1066 BoolTFlag{Name: "serve, s"}, 1067 }, 1068 Action: func(ctx *Context) error { 1069 if ctx.BoolT("serve") != true { 1070 t.Errorf("main name not set") 1071 } 1072 if ctx.BoolT("s") != true { 1073 t.Errorf("short name not set") 1074 } 1075 return nil 1076 }, 1077 } 1078 a.Run([]string{"run", "--serve"}) 1079} 1080 1081func TestParseDestinationBoolT(t *testing.T) { 1082 var dest bool 1083 a := App{ 1084 Flags: []Flag{ 1085 BoolTFlag{ 1086 Name: "dest", 1087 Destination: &dest, 1088 }, 1089 }, 1090 Action: func(ctx *Context) error { 1091 if dest != true { 1092 t.Errorf("expected destination BoolT true") 1093 } 1094 return nil 1095 }, 1096 } 1097 a.Run([]string{"run", "--dest"}) 1098} 1099 1100func TestParseMultiBoolTFromEnv(t *testing.T) { 1101 os.Clearenv() 1102 os.Setenv("APP_DEBUG", "0") 1103 a := App{ 1104 Flags: []Flag{ 1105 BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, 1106 }, 1107 Action: func(ctx *Context) error { 1108 if ctx.BoolT("debug") != false { 1109 t.Errorf("main name not set from env") 1110 } 1111 if ctx.BoolT("d") != false { 1112 t.Errorf("short name not set from env") 1113 } 1114 return nil 1115 }, 1116 } 1117 a.Run([]string{"run"}) 1118} 1119 1120func TestParseMultiBoolTFromEnvCascade(t *testing.T) { 1121 os.Clearenv() 1122 os.Setenv("APP_DEBUG", "0") 1123 a := App{ 1124 Flags: []Flag{ 1125 BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, 1126 }, 1127 Action: func(ctx *Context) error { 1128 if ctx.BoolT("debug") != false { 1129 t.Errorf("main name not set from env") 1130 } 1131 if ctx.BoolT("d") != false { 1132 t.Errorf("short name not set from env") 1133 } 1134 return nil 1135 }, 1136 } 1137 a.Run([]string{"run"}) 1138} 1139 1140type Parser [2]string 1141 1142func (p *Parser) Set(value string) error { 1143 parts := strings.Split(value, ",") 1144 if len(parts) != 2 { 1145 return fmt.Errorf("invalid format") 1146 } 1147 1148 (*p)[0] = parts[0] 1149 (*p)[1] = parts[1] 1150 1151 return nil 1152} 1153 1154func (p *Parser) String() string { 1155 return fmt.Sprintf("%s,%s", p[0], p[1]) 1156} 1157 1158func (p *Parser) Get() interface{} { 1159 return p 1160} 1161 1162func TestParseGeneric(t *testing.T) { 1163 a := App{ 1164 Flags: []Flag{ 1165 GenericFlag{Name: "serve, s", Value: &Parser{}}, 1166 }, 1167 Action: func(ctx *Context) error { 1168 if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { 1169 t.Errorf("main name not set") 1170 } 1171 if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) { 1172 t.Errorf("short name not set") 1173 } 1174 return nil 1175 }, 1176 } 1177 a.Run([]string{"run", "-s", "10,20"}) 1178} 1179 1180func TestParseGenericFromEnv(t *testing.T) { 1181 os.Clearenv() 1182 os.Setenv("APP_SERVE", "20,30") 1183 a := App{ 1184 Flags: []Flag{ 1185 GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, 1186 }, 1187 Action: func(ctx *Context) error { 1188 if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { 1189 t.Errorf("main name not set from env") 1190 } 1191 if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) { 1192 t.Errorf("short name not set from env") 1193 } 1194 return nil 1195 }, 1196 } 1197 a.Run([]string{"run"}) 1198} 1199 1200func TestParseGenericFromEnvCascade(t *testing.T) { 1201 os.Clearenv() 1202 os.Setenv("APP_FOO", "99,2000") 1203 a := App{ 1204 Flags: []Flag{ 1205 GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"}, 1206 }, 1207 Action: func(ctx *Context) error { 1208 if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) { 1209 t.Errorf("value not set from env") 1210 } 1211 return nil 1212 }, 1213 } 1214 a.Run([]string{"run"}) 1215} 1216