1 package cli
2
3 import (
4 "flag"
5 "fmt"
6 "io"
7 "os"
8 "reflect"
9 "regexp"
10 "strings"
11 "testing"
12 "time"
13 )
14
15 var boolFlagTests = []struct {
16 name string
17 expected string
18 }{
19 {"help", "--help\t(default: false)"},
20 {"h", "-h\t(default: false)"},
21 }
22
23 func resetEnv(env []string) {
24 for _, e := range env {
25 fields := strings.SplitN(e, "=", 2)
26 os.Setenv(fields[0], fields[1])
27 }
28 }
29
30 func TestBoolFlagHelpOutput(t *testing.T) {
31 for _, test := range boolFlagTests {
32 fl := &BoolFlag{Name: test.name}
33 output := fl.String()
34
35 if output != test.expected {
36 t.Errorf("%q does not match %q", output, test.expected)
37 }
38 }
39 }
40
41 func TestBoolFlagApply_SetsAllNames(t *testing.T) {
42 v := false
43 fl := BoolFlag{Name: "wat", Aliases: []string{"W", "huh"}, Destination: &v}
44 set := flag.NewFlagSet("test", 0)
45 if err := fl.Apply(set); err != nil {
46 t.Error(err)
47 }
48
49 err := set.Parse([]string{"--wat", "-W", "--huh"})
50 expect(t, err, nil)
51 expect(t, v, true)
52 }
53
54 func TestBoolFlagValueFromContext(t *testing.T) {
55 set := flag.NewFlagSet("test", 0)
56 set.Bool("trueflag", true, "doc")
57 set.Bool("falseflag", false, "doc")
58 ctx := NewContext(nil, set, nil)
59 tf := &BoolFlag{Name: "trueflag"}
60 ff := &BoolFlag{Name: "falseflag"}
61 expect(t, tf.Get(ctx), true)
62 expect(t, ff.Get(ctx), false)
63 }
64
65 func TestBoolFlagApply_SetsCount(t *testing.T) {
66 v := false
67 count := 0
68 app := &App{
69 Name: "foo",
70 Flags: []Flag{
71 &BoolFlag{Name: "wat", Aliases: []string{"W", "huh"}, Destination: &v, Count: &count},
72 },
73 }
74
75 err := app.Run([]string{"foo", "--wat", "-W", "--huh"})
76 if err == nil {
77 t.Error("Expected error")
78 } else if es := err.Error(); !strings.Contains(es, "Cannot use two forms of the same flag") {
79 t.Errorf("Unexpected error %s", es)
80 }
81
82 v = false
83 count = 0
84
85 err = app.Run([]string{"foo", "--wat", "--wat", "--wat"})
86 expect(t, err, nil)
87 expect(t, v, true)
88 expect(t, count, 3)
89 }
90
91 func TestBoolFlagCountFromContext(t *testing.T) {
92
93 boolCountTests := []struct {
94 input []string
95 expectedVal bool
96 expectedCount int
97 }{
98 {
99 input: []string{"foo", "-tf"},
100 expectedVal: true,
101 expectedCount: 1,
102 },
103 {
104 input: []string{"foo", "-tf", "-tf", "-tf"},
105 expectedVal: true,
106 expectedCount: 3,
107 },
108 {
109 input: []string{},
110 expectedVal: false,
111 expectedCount: 0,
112 },
113 }
114
115 for _, bct := range boolCountTests {
116 tf := &BoolFlag{Name: "tf", Aliases: []string{"w", "huh"}}
117 app := &App{
118 Name: "foo",
119 Flags: []Flag{tf},
120 Action: func(ctx *Context) error {
121 expect(t, tf.Get(ctx), bct.expectedVal)
122 expect(t, ctx.Count("tf"), bct.expectedCount)
123 expect(t, ctx.Count("w"), bct.expectedCount)
124 expect(t, ctx.Count("huh"), bct.expectedCount)
125 return nil
126 },
127 }
128
129 err := app.Run(bct.input)
130 expect(t, err, nil)
131
132 }
133 }
134
135 func TestFlagsFromEnv(t *testing.T) {
136 newSetFloat64Slice := func(defaults ...float64) Float64Slice {
137 s := NewFloat64Slice(defaults...)
138 s.hasBeenSet = false
139 return *s
140 }
141
142 newSetIntSlice := func(defaults ...int) IntSlice {
143 s := NewIntSlice(defaults...)
144 s.hasBeenSet = false
145 return *s
146 }
147
148 newSetUintSlice := func(defaults ...uint) UintSlice {
149 s := NewUintSlice(defaults...)
150 s.hasBeenSet = false
151 return *s
152 }
153
154 newSetInt64Slice := func(defaults ...int64) Int64Slice {
155 s := NewInt64Slice(defaults...)
156 s.hasBeenSet = false
157 return *s
158 }
159
160 newSetUint64Slice := func(defaults ...uint64) Uint64Slice {
161 s := NewUint64Slice(defaults...)
162 s.hasBeenSet = false
163 return *s
164 }
165
166 newSetStringSlice := func(defaults ...string) StringSlice {
167 s := NewStringSlice(defaults...)
168 s.hasBeenSet = false
169 return *s
170 }
171 newSetStringSliceKeepSpace := func(defaults ...string) StringSlice {
172 s := newSetStringSlice(defaults...)
173 s.keepSpace = true
174 return s
175 }
176
177 var flagTests = []struct {
178 input string
179 output interface{}
180 flag Flag
181 errRegexp string
182 }{
183 {"", false, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
184 {"1", true, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
185 {"false", false, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
186 {"foobar", true, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, `could not parse "foobar" as bool value from environment variable "DEBUG" for flag debug: .*`},
187
188 {"1s", 1 * time.Second, &DurationFlag{Name: "time", EnvVars: []string{"TIME"}}, ""},
189 {"foobar", false, &DurationFlag{Name: "time", EnvVars: []string{"TIME"}}, `could not parse "foobar" as duration value from environment variable "TIME" for flag time: .*`},
190
191 {"1.2", 1.2, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
192 {"1", 1.0, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
193 {"foobar", 0, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as float64 value from environment variable "SECONDS" for flag seconds: .*`},
194
195 {"1", int64(1), &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
196 {"1.2", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value from environment variable "SECONDS" for flag seconds: .*`},
197 {"foobar", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value from environment variable "SECONDS" for flag seconds: .*`},
198
199 {"1", 1, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
200 {"08", 8, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 10}, ""},
201 {"755", 493, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 8}, ""},
202 {"deadBEEF", 3735928559, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 16}, ""},
203 {"08", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 0}, `could not parse "08" as int value from environment variable "SECONDS" for flag seconds: .*`},
204 {"1.2", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value from environment variable "SECONDS" for flag seconds: .*`},
205 {"foobar", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value from environment variable "SECONDS" for flag seconds: .*`},
206
207 {"1.0,2", newSetFloat64Slice(1, 2), &Float64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
208 {"foobar", newSetFloat64Slice(), &Float64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as float64 slice value from environment variable "SECONDS" for flag seconds: .*`},
209
210 {"1,2", newSetIntSlice(1, 2), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
211 {"1.2,2", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int slice value from environment variable "SECONDS" for flag seconds: .*`},
212 {"foobar", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int slice value from environment variable "SECONDS" for flag seconds: .*`},
213
214 {"1,2", newSetUintSlice(1, 2), &UintSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
215 {"1.2,2", newSetUintSlice(), &UintSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as uint slice value from environment variable "SECONDS" for flag seconds: .*`},
216 {"foobar", newSetUintSlice(), &UintSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint slice value from environment variable "SECONDS" for flag seconds: .*`},
217
218 {"1,2", newSetInt64Slice(1, 2), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
219 {"1.2,2", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int64 slice value from environment variable "SECONDS" for flag seconds: .*`},
220 {"foobar", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int64 slice value from environment variable "SECONDS" for flag seconds: .*`},
221
222 {"1,2", newSetUint64Slice(1, 2), &Uint64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
223 {"1.2,2", newSetUint64Slice(), &Uint64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as uint64 slice value from environment variable "SECONDS" for flag seconds: .*`},
224 {"foobar", newSetUint64Slice(), &Uint64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint64 slice value from environment variable "SECONDS" for flag seconds: .*`},
225
226 {"foo", "foo", &StringFlag{Name: "name", EnvVars: []string{"NAME"}}, ""},
227 {"path", "path", &PathFlag{Name: "path", EnvVars: []string{"PATH"}}, ""},
228
229 {"foo,bar", newSetStringSlice("foo", "bar"), &StringSliceFlag{Name: "names", EnvVars: []string{"NAMES"}}, ""},
230 {" space ", newSetStringSliceKeepSpace(" space "), &StringSliceFlag{Name: "names", KeepSpace: true, EnvVars: []string{"NAMES"}}, ""},
231 {" no space ", newSetStringSlice("no space"), &StringSliceFlag{Name: "names", EnvVars: []string{"NAMES"}}, ""},
232
233 {"1", uint(1), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
234 {"08", uint(8), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 10}, ""},
235 {"755", uint(493), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 8}, ""},
236 {"deadBEEF", uint(3735928559), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 16}, ""},
237 {"08", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 0}, `could not parse "08" as uint value from environment variable "SECONDS" for flag seconds: .*`},
238 {"1.2", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint value from environment variable "SECONDS" for flag seconds: .*`},
239 {"foobar", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint value from environment variable "SECONDS" for flag seconds: .*`},
240
241 {"1", uint64(1), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
242 {"08", uint64(8), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 10}, ""},
243 {"755", uint64(493), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 8}, ""},
244 {"deadBEEF", uint64(3735928559), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 16}, ""},
245 {"08", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 0}, `could not parse "08" as uint64 value from environment variable "SECONDS" for flag seconds: .*`},
246 {"1.2", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint64 value from environment variable "SECONDS" for flag seconds: .*`},
247 {"foobar", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint64 value from environment variable "SECONDS" for flag seconds: .*`},
248
249 {"foo,bar", &Parser{"foo", "bar"}, &GenericFlag{Name: "names", Value: &Parser{}, EnvVars: []string{"NAMES"}}, ""},
250 }
251
252 for i, test := range flagTests {
253 defer resetEnv(os.Environ())
254 os.Clearenv()
255
256 f, ok := test.flag.(DocGenerationFlag)
257 if !ok {
258 t.Errorf("flag %v needs to implement DocGenerationFlag to retrieve env vars", test.flag)
259 }
260 envVarSlice := f.GetEnvVars()
261 _ = os.Setenv(envVarSlice[0], test.input)
262
263 a := App{
264 Flags: []Flag{test.flag},
265 Action: func(ctx *Context) error {
266 if !reflect.DeepEqual(ctx.Value(test.flag.Names()[0]), test.output) {
267 t.Errorf("ex:%01d expected %q to be parsed as %#v, instead was %#v", i, test.input, test.output, ctx.Value(test.flag.Names()[0]))
268 }
269 if !f.IsSet() {
270 t.Errorf("Flag %s not set", f.Names()[0])
271 }
272
273
274 if !reflect.DeepEqual(ctx.FlagNames(), test.flag.Names()) {
275 t.Errorf("Not enough flag names %+v", ctx.FlagNames())
276 }
277 return nil
278 },
279 }
280
281 err := a.Run([]string{"run"})
282
283 if test.errRegexp != "" {
284 if err == nil {
285 t.Errorf("expected error to match %q, got none", test.errRegexp)
286 } else {
287 if matched, _ := regexp.MatchString(test.errRegexp, err.Error()); !matched {
288 t.Errorf("expected error to match %q, got error %s", test.errRegexp, err)
289 }
290 }
291 } else {
292 if err != nil && test.errRegexp == "" {
293 t.Errorf("expected no error got %q", err)
294 }
295 }
296 }
297 }
298
299 type nodocFlag struct {
300 Flag
301
302 Name string
303 }
304
305 func TestFlagStringifying(t *testing.T) {
306 for _, tc := range []struct {
307 name string
308 fl Flag
309 expected string
310 }{
311 {
312 name: "bool-flag",
313 fl: &BoolFlag{Name: "vividly"},
314 expected: "--vividly\t(default: false)",
315 },
316 {
317 name: "bool-flag-with-default-text",
318 fl: &BoolFlag{Name: "wildly", DefaultText: "scrambled"},
319 expected: "--wildly\t(default: scrambled)",
320 },
321 {
322 name: "duration-flag",
323 fl: &DurationFlag{Name: "scream-for"},
324 expected: "--scream-for value\t(default: 0s)",
325 },
326 {
327 name: "duration-flag-with-default-text",
328 fl: &DurationFlag{Name: "feels-about", DefaultText: "whimsically"},
329 expected: "--feels-about value\t(default: whimsically)",
330 },
331 {
332 name: "float64-flag",
333 fl: &Float64Flag{Name: "arduous"},
334 expected: "--arduous value\t(default: 0)",
335 },
336 {
337 name: "float64-flag-with-default-text",
338 fl: &Float64Flag{Name: "filibuster", DefaultText: "42"},
339 expected: "--filibuster value\t(default: 42)",
340 },
341 {
342 name: "float64-slice-flag",
343 fl: &Float64SliceFlag{Name: "pizzas"},
344 expected: "--pizzas value [ --pizzas value ]\t",
345 },
346 {
347 name: "float64-slice-flag-with-default-text",
348 fl: &Float64SliceFlag{Name: "pepperonis", DefaultText: "shaved"},
349 expected: "--pepperonis value [ --pepperonis value ]\t(default: shaved)",
350 },
351 {
352 name: "generic-flag",
353 fl: &GenericFlag{Name: "yogurt"},
354 expected: "--yogurt value\t",
355 },
356 {
357 name: "generic-flag-with-default-text",
358 fl: &GenericFlag{Name: "ricotta", DefaultText: "plops"},
359 expected: "--ricotta value\t(default: plops)",
360 },
361 {
362 name: "int-flag",
363 fl: &IntFlag{Name: "grubs"},
364 expected: "--grubs value\t(default: 0)",
365 },
366 {
367 name: "int-flag-with-default-text",
368 fl: &IntFlag{Name: "poisons", DefaultText: "11ty"},
369 expected: "--poisons value\t(default: 11ty)",
370 },
371 {
372 name: "int-slice-flag",
373 fl: &IntSliceFlag{Name: "pencils"},
374 expected: "--pencils value [ --pencils value ]\t",
375 },
376 {
377 name: "int-slice-flag-with-default-text",
378 fl: &IntFlag{Name: "pens", DefaultText: "-19"},
379 expected: "--pens value\t(default: -19)",
380 },
381 {
382 name: "uint-slice-flag",
383 fl: &UintSliceFlag{Name: "pencils"},
384 expected: "--pencils value [ --pencils value ]\t",
385 },
386 {
387 name: "uint-slice-flag-with-default-text",
388 fl: &UintFlag{Name: "pens", DefaultText: "29"},
389 expected: "--pens value\t(default: 29)",
390 },
391 {
392 name: "int64-flag",
393 fl: &Int64Flag{Name: "flume"},
394 expected: "--flume value\t(default: 0)",
395 },
396 {
397 name: "int64-flag-with-default-text",
398 fl: &Int64Flag{Name: "shattering", DefaultText: "22"},
399 expected: "--shattering value\t(default: 22)",
400 },
401 {
402 name: "int64-slice-flag",
403 fl: &Int64SliceFlag{Name: "drawers"},
404 expected: "--drawers value [ --drawers value ]\t",
405 },
406 {
407 name: "int64-slice-flag-with-default-text",
408 fl: &Int64SliceFlag{Name: "handles", DefaultText: "-2"},
409 expected: "--handles value [ --handles value ]\t(default: -2)",
410 },
411 {
412 name: "uint64-slice-flag",
413 fl: &Uint64SliceFlag{Name: "drawers"},
414 expected: "--drawers value [ --drawers value ]\t",
415 },
416 {
417 name: "uint64-slice-flag-with-default-text",
418 fl: &Uint64SliceFlag{Name: "handles", DefaultText: "-2"},
419 expected: "--handles value [ --handles value ]\t(default: -2)",
420 },
421 {
422 name: "path-flag",
423 fl: &PathFlag{Name: "soup"},
424 expected: "--soup value\t",
425 },
426 {
427 name: "path-flag-with-default-text",
428 fl: &PathFlag{Name: "stew", DefaultText: "charred/beans"},
429 expected: "--stew value\t(default: charred/beans)",
430 },
431 {
432 name: "string-flag",
433 fl: &StringFlag{Name: "arf-sound"},
434 expected: "--arf-sound value\t",
435 },
436 {
437 name: "string-flag-with-default-text",
438 fl: &StringFlag{Name: "woof-sound", DefaultText: "urp"},
439 expected: "--woof-sound value\t(default: urp)",
440 },
441 {
442 name: "string-slice-flag",
443 fl: &StringSliceFlag{Name: "meow-sounds"},
444 expected: "--meow-sounds value [ --meow-sounds value ]\t",
445 },
446 {
447 name: "string-slice-flag-with-default-text",
448 fl: &StringSliceFlag{Name: "moo-sounds", DefaultText: "awoo"},
449 expected: "--moo-sounds value [ --moo-sounds value ]\t(default: awoo)",
450 },
451 {
452 name: "timestamp-flag",
453 fl: &TimestampFlag{Name: "eating"},
454 expected: "--eating value\t",
455 },
456 {
457 name: "timestamp-flag-with-default-text",
458 fl: &TimestampFlag{Name: "sleeping", DefaultText: "earlier"},
459 expected: "--sleeping value\t(default: earlier)",
460 },
461 {
462 name: "uint-flag",
463 fl: &UintFlag{Name: "jars"},
464 expected: "--jars value\t(default: 0)",
465 },
466 {
467 name: "uint-flag-with-default-text",
468 fl: &UintFlag{Name: "bottles", DefaultText: "99"},
469 expected: "--bottles value\t(default: 99)",
470 },
471 {
472 name: "uint64-flag",
473 fl: &Uint64Flag{Name: "cans"},
474 expected: "--cans value\t(default: 0)",
475 },
476 {
477 name: "uint64-flag-with-default-text",
478 fl: &UintFlag{Name: "tubes", DefaultText: "13"},
479 expected: "--tubes value\t(default: 13)",
480 },
481 {
482 name: "nodoc-flag",
483 fl: &nodocFlag{Name: "scarecrow"},
484 expected: "",
485 },
486 } {
487 t.Run(tc.name, func(ct *testing.T) {
488 s := stringifyFlag(tc.fl)
489 if s != tc.expected {
490 ct.Errorf("stringified flag %q does not match expected %q", s, tc.expected)
491 }
492 })
493 }
494 }
495
496 var stringFlagTests = []struct {
497 name string
498 aliases []string
499 usage string
500 value string
501 expected string
502 }{
503 {"foo", nil, "", "", "--foo value\t"},
504 {"f", nil, "", "", "-f value\t"},
505 {"f", nil, "The total `foo` desired", "all", "-f foo\tThe total foo desired (default: \"all\")"},
506 {"test", nil, "", "Something", "--test value\t(default: \"Something\")"},
507 {"config", []string{"c"}, "Load configuration from `FILE`", "", "--config FILE, -c FILE\tLoad configuration from FILE"},
508 {"config", []string{"c"}, "Load configuration from `CONFIG`", "config.json", "--config CONFIG, -c CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"},
509 }
510
511 func TestStringFlagHelpOutput(t *testing.T) {
512 for _, test := range stringFlagTests {
513 fl := &StringFlag{Name: test.name, Aliases: test.aliases, Usage: test.usage, Value: test.value}
514
515 tfs := flag.NewFlagSet("test", 0)
516 if err := fl.Apply(tfs); err != nil {
517 t.Error(err)
518 }
519 output := fl.String()
520
521 if output != test.expected {
522 t.Errorf("%q does not match %q", output, test.expected)
523 }
524 }
525 }
526
527 func TestStringFlagDefaultText(t *testing.T) {
528 fl := &StringFlag{Name: "foo", Aliases: nil, Usage: "amount of `foo` requested", Value: "none", DefaultText: "all of it"}
529 expected := "--foo foo\tamount of foo requested (default: all of it)"
530 output := fl.String()
531
532 if output != expected {
533 t.Errorf("%q does not match %q", output, expected)
534 }
535 }
536
537 func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
538 defer resetEnv(os.Environ())
539 os.Clearenv()
540 _ = os.Setenv("APP_FOO", "derp")
541
542 for _, test := range stringFlagTests {
543 fl := &StringFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_FOO"}}
544 output := fl.String()
545
546 expectedSuffix := withEnvHint([]string{"APP_FOO"}, "")
547 if !strings.HasSuffix(output, expectedSuffix) {
548 t.Errorf("%s does not end with"+expectedSuffix, output)
549 }
550 }
551 }
552
553 var _ = []struct {
554 name string
555 aliases []string
556 usage string
557 value string
558 prefixer FlagNamePrefixFunc
559 expected string
560 }{
561 {name: "foo", usage: "", value: "", prefixer: func(a []string, b string) string {
562 return fmt.Sprintf("name: %s, ph: %s", a, b)
563 }, expected: "name: foo, ph: value\t"},
564 {name: "f", usage: "", value: "", prefixer: func(a []string, b string) string {
565 return fmt.Sprintf("name: %s, ph: %s", a, b)
566 }, expected: "name: f, ph: value\t"},
567 {name: "f", usage: "The total `foo` desired", value: "all", prefixer: func(a []string, b string) string {
568 return fmt.Sprintf("name: %s, ph: %s", a, b)
569 }, expected: "name: f, ph: foo\tThe total foo desired (default: \"all\")"},
570 {name: "test", usage: "", value: "Something", prefixer: func(a []string, b string) string {
571 return fmt.Sprintf("name: %s, ph: %s", a, b)
572 }, expected: "name: test, ph: value\t(default: \"Something\")"},
573 {name: "config", aliases: []string{"c"}, usage: "Load configuration from `FILE`", value: "", prefixer: func(a []string, b string) string {
574 return fmt.Sprintf("name: %s, ph: %s", a, b)
575 }, expected: "name: config,c, ph: FILE\tLoad configuration from FILE"},
576 {name: "config", aliases: []string{"c"}, usage: "Load configuration from `CONFIG`", value: "config.json", prefixer: func(a []string, b string) string {
577 return fmt.Sprintf("name: %s, ph: %s", a, b)
578 }, expected: "name: config,c, ph: CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"},
579 }
580
581 func TestStringFlagApply_SetsAllNames(t *testing.T) {
582 v := "mmm"
583 fl := StringFlag{Name: "hay", Aliases: []string{"H", "hayyy"}, Destination: &v}
584 set := flag.NewFlagSet("test", 0)
585 if err := fl.Apply(set); err != nil {
586 t.Error(err)
587 }
588
589 err := set.Parse([]string{"--hay", "u", "-H", "yuu", "--hayyy", "YUUUU"})
590 expect(t, err, nil)
591 expect(t, v, "YUUUU")
592 }
593
594 func TestStringFlagValueFromContext(t *testing.T) {
595 set := flag.NewFlagSet("test", 0)
596 set.String("myflag", "foobar", "doc")
597 ctx := NewContext(nil, set, nil)
598 f := &StringFlag{Name: "myflag"}
599 expect(t, f.Get(ctx), "foobar")
600 }
601
602 var pathFlagTests = []struct {
603 name string
604 aliases []string
605 usage string
606 value string
607 expected string
608 }{
609 {"f", nil, "", "", "-f value\t"},
610 {"f", nil, "Path is the `path` of file", "/path/to/file", "-f path\tPath is the path of file (default: \"/path/to/file\")"},
611 }
612
613 func TestPathFlagHelpOutput(t *testing.T) {
614 for _, test := range pathFlagTests {
615 fl := &PathFlag{Name: test.name, Aliases: test.aliases, Usage: test.usage, Value: test.value}
616
617
618 tfs := flag.NewFlagSet("test", 0)
619 if err := fl.Apply(tfs); err != nil {
620 t.Error(err)
621 return
622 }
623
624 output := fl.String()
625
626 if output != test.expected {
627 t.Errorf("%q does not match %q", output, test.expected)
628 }
629 }
630 }
631
632 func TestPathFlagWithEnvVarHelpOutput(t *testing.T) {
633 defer resetEnv(os.Environ())
634 os.Clearenv()
635 _ = os.Setenv("APP_PATH", "/path/to/file")
636 for _, test := range pathFlagTests {
637 fl := &PathFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_PATH"}}
638 output := fl.String()
639
640 expectedSuffix := withEnvHint([]string{"APP_PATH"}, "")
641 if !strings.HasSuffix(output, expectedSuffix) {
642 t.Errorf("%s does not end with"+expectedSuffix, output)
643 }
644 }
645 }
646
647 func TestPathFlagApply_SetsAllNames(t *testing.T) {
648 v := "mmm"
649 fl := PathFlag{Name: "path", Aliases: []string{"p", "PATH"}, Destination: &v}
650 set := flag.NewFlagSet("test", 0)
651 if err := fl.Apply(set); err != nil {
652 t.Error(err)
653 return
654 }
655
656 err := set.Parse([]string{"--path", "/path/to/file/path", "-p", "/path/to/file/p", "--PATH", "/path/to/file/PATH"})
657 expect(t, err, nil)
658 expect(t, v, "/path/to/file/PATH")
659 }
660
661 func TestPathFlagValueFromContext(t *testing.T) {
662 set := flag.NewFlagSet("test", 0)
663 set.String("myflag", "/my/path", "doc")
664 ctx := NewContext(nil, set, nil)
665 f := &PathFlag{Name: "myflag"}
666 expect(t, f.Get(ctx), "/my/path")
667 }
668
669 var _ = []struct {
670 name string
671 env string
672 hinter FlagEnvHintFunc
673 expected string
674 }{
675 {"foo", "", func(a []string, b string) string {
676 return fmt.Sprintf("env: %s, str: %s", a, b)
677 }, "env: , str: --foo value\t"},
678 {"f", "", func(a []string, b string) string {
679 return fmt.Sprintf("env: %s, str: %s", a, b)
680 }, "env: , str: -f value\t"},
681 {"foo", "ENV_VAR", func(a []string, b string) string {
682 return fmt.Sprintf("env: %s, str: %s", a, b)
683 }, "env: ENV_VAR, str: --foo value\t"},
684 {"f", "ENV_VAR", func(a []string, b string) string {
685 return fmt.Sprintf("env: %s, str: %s", a, b)
686 }, "env: ENV_VAR, str: -f value\t"},
687 }
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704 var stringSliceFlagTests = []struct {
705 name string
706 aliases []string
707 value *StringSlice
708 expected string
709 }{
710 {"foo", nil, NewStringSlice(""), "--foo value [ --foo value ]\t"},
711 {"f", nil, NewStringSlice(""), "-f value [ -f value ]\t"},
712 {"f", nil, NewStringSlice("Lipstick"), "-f value [ -f value ]\t(default: \"Lipstick\")"},
713 {"test", nil, NewStringSlice("Something"), "--test value [ --test value ]\t(default: \"Something\")"},
714 {"dee", []string{"d"}, NewStringSlice("Inka", "Dinka", "dooo"), "--dee value, -d value [ --dee value, -d value ]\t(default: \"Inka\", \"Dinka\", \"dooo\")"},
715 }
716
717 func TestStringSliceFlagHelpOutput(t *testing.T) {
718 for _, test := range stringSliceFlagTests {
719 f := &StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
720 output := f.String()
721
722 if output != test.expected {
723 t.Errorf("%q does not match %q", output, test.expected)
724 }
725 }
726 }
727
728 func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
729 defer resetEnv(os.Environ())
730 os.Clearenv()
731 _ = os.Setenv("APP_QWWX", "11,4")
732
733 for _, test := range stringSliceFlagTests {
734 fl := &StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_QWWX"}}
735 output := fl.String()
736
737 expectedSuffix := withEnvHint([]string{"APP_QWWX"}, "")
738 if !strings.HasSuffix(output, expectedSuffix) {
739 t.Errorf("%q does not end with"+expectedSuffix, output)
740 }
741 }
742 }
743
744 func TestStringSliceFlagApply_SetsAllNames(t *testing.T) {
745 fl := StringSliceFlag{Name: "goat", Aliases: []string{"G", "gooots"}}
746 set := flag.NewFlagSet("test", 0)
747 if err := fl.Apply(set); err != nil {
748 t.Error(err)
749 }
750
751 err := set.Parse([]string{"--goat", "aaa", "-G", "bbb", "--gooots", "eeeee"})
752 expect(t, err, nil)
753 }
754
755 func TestStringSliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
756 defer resetEnv(os.Environ())
757 os.Clearenv()
758 _ = os.Setenv("MY_GOAT", "vincent van goat,scape goat")
759 var val StringSlice
760 fl := StringSliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
761 set := flag.NewFlagSet("test", 0)
762 if err := fl.Apply(set); err != nil {
763 t.Error(err)
764 }
765
766 err := set.Parse(nil)
767 expect(t, err, nil)
768 expect(t, val.Value(), []string(nil))
769 expect(t, set.Lookup("goat").Value.(*StringSlice).Value(), []string{"vincent van goat", "scape goat"})
770 }
771
772 func TestStringSliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
773 defer resetEnv(os.Environ())
774 os.Clearenv()
775 _ = os.Setenv("MY_GOAT", "vincent van goat,scape goat")
776 val := NewStringSlice(`some default`, `values here`)
777 fl := StringSliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
778 set := flag.NewFlagSet("test", 0)
779 if err := fl.Apply(set); err != nil {
780 t.Error(err)
781 }
782 err := set.Parse(nil)
783 expect(t, err, nil)
784 expect(t, val.Value(), []string{`some default`, `values here`})
785 expect(t, set.Lookup("goat").Value.(*StringSlice).Value(), []string{"vincent van goat", "scape goat"})
786 }
787
788 func TestStringSliceFlagApply_DefaultValueWithDestination(t *testing.T) {
789 defValue := []string{"UA", "US"}
790
791 fl := StringSliceFlag{Name: "country", Value: NewStringSlice(defValue...), Destination: NewStringSlice("CA")}
792 set := flag.NewFlagSet("test", 0)
793 if err := fl.Apply(set); err != nil {
794 t.Error(err)
795 }
796
797 err := set.Parse([]string{})
798 expect(t, err, nil)
799 expect(t, defValue, fl.Destination.Value())
800 }
801
802 func TestStringSliceFlagValueFromContext(t *testing.T) {
803 set := flag.NewFlagSet("test", 0)
804 set.Var(NewStringSlice("a", "b", "c"), "myflag", "doc")
805 ctx := NewContext(nil, set, nil)
806 f := &StringSliceFlag{Name: "myflag"}
807 expect(t, f.Get(ctx), []string{"a", "b", "c"})
808 }
809
810 func TestStringSliceFlag_MatchStringFlagBehavior(t *testing.T) {
811 t.Parallel()
812
813 values := []string{
814 "asd",
815 "123",
816 " asd ",
817 }
818 for testNum, value := range values {
819 value := value
820 t.Run(fmt.Sprintf("%d", testNum), func(t *testing.T) {
821 t.Parallel()
822
823 app := App{
824 Flags: []Flag{
825 &StringFlag{Name: "string"},
826 &StringSliceFlag{Name: "slice", KeepSpace: true},
827 },
828 Action: func(ctx *Context) error {
829 f1, f2 := ctx.String("string"), ctx.StringSlice("slice")
830 if l := len(f2); l != 1 {
831 t.Fatalf("StringSliceFlag should result in exactly one value, got %d", l)
832 }
833
834 v1, v2 := f1, f2[0]
835 if v1 != v2 {
836 t.Errorf("Expected StringSliceFlag to parse the same value as StringFlag (%q), got %q", v1, v2)
837 }
838 return nil
839 },
840 }
841
842 if err := app.Run([]string{"", "--string", value, "--slice", value}); err != nil {
843 t.Errorf("app run error: %s", err)
844 }
845 })
846 }
847 }
848
849 func TestStringSliceFlag_TrimSpace(t *testing.T) {
850 t.Parallel()
851
852 tests := []struct {
853 in, out string
854 }{
855 {" asd", "asd"},
856 {"123 ", "123"},
857 {" asd ", "asd"},
858 }
859 for testNum, tt := range tests {
860 tt := tt
861 t.Run(fmt.Sprintf("%d", testNum), func(t *testing.T) {
862 t.Parallel()
863
864 app := App{
865 Flags: []Flag{
866 &StringSliceFlag{Name: "trim"},
867 &StringSliceFlag{Name: "no-trim", KeepSpace: true},
868 },
869 Action: func(ctx *Context) error {
870 flagTrim, flagNoTrim := ctx.StringSlice("trim"), ctx.StringSlice("no-trim")
871 if l := len(flagTrim); l != 1 {
872 t.Fatalf("slice flag 'trim' should result in exactly one value, got %d", l)
873 }
874 if l := len(flagNoTrim); l != 1 {
875 t.Fatalf("slice flag 'no-trim' should result in exactly one value, got %d", l)
876 }
877
878 if v := flagTrim[0]; v != tt.out {
879 t.Errorf("Expected trimmed value %q, got %q", tt.out, v)
880 }
881 if v := flagNoTrim[0]; v != tt.in {
882 t.Errorf("Expected no trimmed value%q, got %q", tt.out, v)
883 }
884 return nil
885 },
886 }
887
888 if err := app.Run([]string{"", "--trim", tt.in, "--no-trim", tt.in}); err != nil {
889 t.Errorf("app run error: %s", err)
890 }
891 })
892 }
893 }
894
895 var intFlagTests = []struct {
896 name string
897 expected string
898 }{
899 {"hats", "--hats value\t(default: 9)"},
900 {"H", "-H value\t(default: 9)"},
901 }
902
903 func TestIntFlagHelpOutput(t *testing.T) {
904 for _, test := range intFlagTests {
905 fl := &IntFlag{Name: test.name, Value: 9}
906
907
908 tfs := flag.NewFlagSet("test", 0)
909 if err := fl.Apply(tfs); err != nil {
910 t.Error(err)
911 return
912 }
913
914 output := fl.String()
915
916 if output != test.expected {
917 t.Errorf("%s does not match %s", output, test.expected)
918 }
919 }
920 }
921
922 func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
923 defer resetEnv(os.Environ())
924 os.Clearenv()
925 _ = os.Setenv("APP_BAR", "2")
926
927 for _, test := range intFlagTests {
928 fl := &IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
929 output := fl.String()
930
931 expectedSuffix := withEnvHint([]string{"APP_BAR"}, "")
932 if !strings.HasSuffix(output, expectedSuffix) {
933 t.Errorf("%s does not end with"+expectedSuffix, output)
934 }
935 }
936 }
937
938 func TestIntFlagApply_SetsAllNames(t *testing.T) {
939 v := 3
940 fl := IntFlag{Name: "banana", Aliases: []string{"B", "banannanana"}, Destination: &v}
941 set := flag.NewFlagSet("test", 0)
942 if err := fl.Apply(set); err != nil {
943 t.Error(err)
944 }
945
946 err := set.Parse([]string{"--banana", "1", "-B", "2", "--banannanana", "5"})
947 expect(t, err, nil)
948 expect(t, v, 5)
949 }
950
951 func TestIntFlagValueFromContext(t *testing.T) {
952 set := flag.NewFlagSet("test", 0)
953 set.Int("myflag", 42, "doc")
954 ctx := NewContext(nil, set, nil)
955 f := &IntFlag{Name: "myflag"}
956 expect(t, f.Get(ctx), 42)
957 }
958
959 var int64FlagTests = []struct {
960 name string
961 expected string
962 }{
963 {"hats", "--hats value\t(default: 8589934592)"},
964 {"H", "-H value\t(default: 8589934592)"},
965 }
966
967 func TestInt64FlagHelpOutput(t *testing.T) {
968 for _, test := range int64FlagTests {
969 fl := Int64Flag{Name: test.name, Value: 8589934592}
970
971
972 tfs := flag.NewFlagSet("test", 0)
973 if err := fl.Apply(tfs); err != nil {
974 t.Error(err)
975 return
976 }
977
978 output := fl.String()
979
980 if output != test.expected {
981 t.Errorf("%s does not match %s", output, test.expected)
982 }
983 }
984 }
985
986 func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) {
987 defer resetEnv(os.Environ())
988 os.Clearenv()
989 _ = os.Setenv("APP_BAR", "2")
990
991 for _, test := range int64FlagTests {
992 fl := IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
993 output := fl.String()
994
995 expectedSuffix := withEnvHint([]string{"APP_BAR"}, "")
996 if !strings.HasSuffix(output, expectedSuffix) {
997 t.Errorf("%s does not end with"+expectedSuffix, output)
998 }
999 }
1000 }
1001
1002 func TestInt64FlagValueFromContext(t *testing.T) {
1003 set := flag.NewFlagSet("test", 0)
1004 set.Int64("myflag", 42, "doc")
1005 ctx := NewContext(nil, set, nil)
1006 f := &Int64Flag{Name: "myflag"}
1007 expect(t, f.Get(ctx), int64(42))
1008 }
1009
1010 var uintFlagTests = []struct {
1011 name string
1012 expected string
1013 }{
1014 {"nerfs", "--nerfs value\t(default: 41)"},
1015 {"N", "-N value\t(default: 41)"},
1016 }
1017
1018 func TestUintFlagHelpOutput(t *testing.T) {
1019 for _, test := range uintFlagTests {
1020 fl := UintFlag{Name: test.name, Value: 41}
1021
1022
1023 tfs := flag.NewFlagSet("test", 0)
1024 if err := fl.Apply(tfs); err != nil {
1025 t.Error(err)
1026 return
1027 }
1028
1029 output := fl.String()
1030
1031 if output != test.expected {
1032 t.Errorf("%s does not match %s", output, test.expected)
1033 }
1034 }
1035 }
1036
1037 func TestUintFlagWithEnvVarHelpOutput(t *testing.T) {
1038 defer resetEnv(os.Environ())
1039 os.Clearenv()
1040 _ = os.Setenv("APP_BAR", "2")
1041
1042 for _, test := range uintFlagTests {
1043 fl := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
1044 output := fl.String()
1045
1046 expectedSuffix := withEnvHint([]string{"APP_BAR"}, "")
1047 if !strings.HasSuffix(output, expectedSuffix) {
1048 t.Errorf("%s does not end with"+expectedSuffix, output)
1049 }
1050 }
1051 }
1052
1053 func TestUintFlagValueFromContext(t *testing.T) {
1054 set := flag.NewFlagSet("test", 0)
1055 set.Uint("myflag", 42, "doc")
1056 ctx := NewContext(nil, set, nil)
1057 f := &UintFlag{Name: "myflag"}
1058 expect(t, f.Get(ctx), uint(42))
1059 }
1060
1061 var uint64FlagTests = []struct {
1062 name string
1063 expected string
1064 }{
1065 {"gerfs", "--gerfs value\t(default: 8589934582)"},
1066 {"G", "-G value\t(default: 8589934582)"},
1067 }
1068
1069 func TestUint64FlagHelpOutput(t *testing.T) {
1070 for _, test := range uint64FlagTests {
1071 fl := Uint64Flag{Name: test.name, Value: 8589934582}
1072
1073
1074 tfs := flag.NewFlagSet("test", 0)
1075 if err := fl.Apply(tfs); err != nil {
1076 t.Error(err)
1077 return
1078 }
1079
1080 output := fl.String()
1081
1082 if output != test.expected {
1083 t.Errorf("%s does not match %s", output, test.expected)
1084 }
1085 }
1086 }
1087
1088 func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) {
1089 defer resetEnv(os.Environ())
1090 os.Clearenv()
1091 _ = os.Setenv("APP_BAR", "2")
1092
1093 for _, test := range uint64FlagTests {
1094 fl := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
1095 output := fl.String()
1096
1097 expectedSuffix := withEnvHint([]string{"APP_BAR"}, "")
1098 if !strings.HasSuffix(output, expectedSuffix) {
1099 t.Errorf("%s does not end with"+expectedSuffix, output)
1100 }
1101 }
1102 }
1103
1104 func TestUint64FlagValueFromContext(t *testing.T) {
1105 set := flag.NewFlagSet("test", 0)
1106 set.Uint64("myflag", 42, "doc")
1107 ctx := NewContext(nil, set, nil)
1108 f := &Uint64Flag{Name: "myflag"}
1109 expect(t, f.Get(ctx), uint64(42))
1110 }
1111
1112 var durationFlagTests = []struct {
1113 name string
1114 expected string
1115 }{
1116 {"hooting", "--hooting value\t(default: 1s)"},
1117 {"H", "-H value\t(default: 1s)"},
1118 }
1119
1120 func TestDurationFlagHelpOutput(t *testing.T) {
1121 for _, test := range durationFlagTests {
1122 fl := &DurationFlag{Name: test.name, Value: 1 * time.Second}
1123
1124
1125 tfs := flag.NewFlagSet("test", 0)
1126 if err := fl.Apply(tfs); err != nil {
1127 t.Error(err)
1128 return
1129 }
1130
1131 output := fl.String()
1132
1133 if output != test.expected {
1134 t.Errorf("%q does not match %q", output, test.expected)
1135 }
1136 }
1137 }
1138
1139 func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
1140 defer resetEnv(os.Environ())
1141 os.Clearenv()
1142 _ = os.Setenv("APP_BAR", "2h3m6s")
1143
1144 for _, test := range durationFlagTests {
1145 fl := &DurationFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
1146 output := fl.String()
1147
1148 expectedSuffix := withEnvHint([]string{"APP_BAR"}, "")
1149 if !strings.HasSuffix(output, expectedSuffix) {
1150 t.Errorf("%s does not end with"+expectedSuffix, output)
1151 }
1152 }
1153 }
1154
1155 func TestDurationFlagApply_SetsAllNames(t *testing.T) {
1156 v := time.Second * 20
1157 fl := DurationFlag{Name: "howmuch", Aliases: []string{"H", "whyyy"}, Destination: &v}
1158 set := flag.NewFlagSet("test", 0)
1159 if err := fl.Apply(set); err != nil {
1160 t.Error(err)
1161 }
1162
1163 err := set.Parse([]string{"--howmuch", "30s", "-H", "5m", "--whyyy", "30h"})
1164 expect(t, err, nil)
1165 expect(t, v, time.Hour*30)
1166 }
1167
1168 func TestDurationFlagValueFromContext(t *testing.T) {
1169 set := flag.NewFlagSet("test", 0)
1170 set.Duration("myflag", 42*time.Second, "doc")
1171 ctx := NewContext(nil, set, nil)
1172 f := &DurationFlag{Name: "myflag"}
1173 expect(t, f.Get(ctx), 42*time.Second)
1174 }
1175
1176 var intSliceFlagTests = []struct {
1177 name string
1178 aliases []string
1179 value *IntSlice
1180 expected string
1181 }{
1182 {"heads", nil, NewIntSlice(), "--heads value [ --heads value ]\t"},
1183 {"H", nil, NewIntSlice(), "-H value [ -H value ]\t"},
1184 {"H", []string{"heads"}, NewIntSlice(9, 3), "-H value, --heads value [ -H value, --heads value ]\t(default: 9, 3)"},
1185 }
1186
1187 func TestIntSliceFlagHelpOutput(t *testing.T) {
1188 for _, test := range intSliceFlagTests {
1189 fl := &IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
1190 output := fl.String()
1191
1192 if output != test.expected {
1193 t.Errorf("%q does not match %q", output, test.expected)
1194 }
1195 }
1196 }
1197
1198 func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
1199 defer resetEnv(os.Environ())
1200 os.Clearenv()
1201 _ = os.Setenv("APP_SMURF", "42,3")
1202
1203 for _, test := range intSliceFlagTests {
1204 fl := &IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_SMURF"}}
1205 output := fl.String()
1206
1207 expectedSuffix := withEnvHint([]string{"APP_SMURF"}, "")
1208 if !strings.HasSuffix(output, expectedSuffix) {
1209 t.Errorf("%q does not end with"+expectedSuffix, output)
1210 }
1211 }
1212 }
1213
1214 func TestIntSliceFlagApply_SetsAllNames(t *testing.T) {
1215 fl := IntSliceFlag{Name: "bits", Aliases: []string{"B", "bips"}}
1216 set := flag.NewFlagSet("test", 0)
1217 if err := fl.Apply(set); err != nil {
1218 t.Error(err)
1219 }
1220
1221 err := set.Parse([]string{"--bits", "23", "-B", "3", "--bips", "99"})
1222 expect(t, err, nil)
1223 }
1224
1225 func TestIntSliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
1226 defer resetEnv(os.Environ())
1227 os.Clearenv()
1228 _ = os.Setenv("MY_GOAT", "1 , 2")
1229 var val IntSlice
1230 fl := IntSliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
1231 set := flag.NewFlagSet("test", 0)
1232 if err := fl.Apply(set); err != nil {
1233 t.Error(err)
1234 }
1235
1236 err := set.Parse(nil)
1237 expect(t, err, nil)
1238 expect(t, val.Value(), []int(nil))
1239 expect(t, set.Lookup("goat").Value.(*IntSlice).Value(), []int{1, 2})
1240 }
1241
1242 func TestIntSliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
1243 defer resetEnv(os.Environ())
1244 os.Clearenv()
1245 _ = os.Setenv("MY_GOAT", "1 , 2")
1246 val := NewIntSlice(3, 4)
1247 fl := IntSliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
1248 set := flag.NewFlagSet("test", 0)
1249 if err := fl.Apply(set); err != nil {
1250 t.Error(err)
1251 }
1252 err := set.Parse(nil)
1253 expect(t, err, nil)
1254 expect(t, val.Value(), []int{3, 4})
1255 expect(t, set.Lookup("goat").Value.(*IntSlice).Value(), []int{1, 2})
1256 }
1257
1258 func TestIntSliceFlagApply_DefaultValueWithDestination(t *testing.T) {
1259 defValue := []int{1, 2}
1260
1261 fl := IntSliceFlag{Name: "country", Value: NewIntSlice(defValue...), Destination: NewIntSlice(3)}
1262 set := flag.NewFlagSet("test", 0)
1263 if err := fl.Apply(set); err != nil {
1264 t.Error(err)
1265 }
1266
1267 err := set.Parse([]string{})
1268 expect(t, err, nil)
1269 expect(t, defValue, fl.Destination.Value())
1270 }
1271
1272 func TestIntSliceFlagApply_ParentContext(t *testing.T) {
1273 _ = (&App{
1274 Flags: []Flag{
1275 &IntSliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewIntSlice(1, 2, 3)},
1276 },
1277 Commands: []*Command{
1278 {
1279 Name: "child",
1280 Action: func(ctx *Context) error {
1281 expected := []int{1, 2, 3}
1282 if !reflect.DeepEqual(ctx.IntSlice("numbers"), expected) {
1283 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.IntSlice("numbers"))
1284 }
1285 if !reflect.DeepEqual(ctx.IntSlice("n"), expected) {
1286 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.IntSlice("n"))
1287 }
1288 return nil
1289 },
1290 },
1291 },
1292 }).Run([]string{"run", "child"})
1293 }
1294
1295 func TestIntSliceFlag_SetFromParentContext(t *testing.T) {
1296 fl := &IntSliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewIntSlice(1, 2, 3, 4)}
1297 set := flag.NewFlagSet("test", 0)
1298 if err := fl.Apply(set); err != nil {
1299 t.Error(err)
1300 }
1301 ctx := &Context{
1302 parentContext: &Context{
1303 flagSet: set,
1304 },
1305 flagSet: flag.NewFlagSet("empty", 0),
1306 }
1307 expected := []int{1, 2, 3, 4}
1308 if !reflect.DeepEqual(ctx.IntSlice("numbers"), expected) {
1309 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.IntSlice("numbers"))
1310 }
1311 }
1312
1313 func TestIntSliceFlagValueFromContext(t *testing.T) {
1314 set := flag.NewFlagSet("test", 0)
1315 set.Var(NewIntSlice(1, 2, 3), "myflag", "doc")
1316 ctx := NewContext(nil, set, nil)
1317 f := &IntSliceFlag{Name: "myflag"}
1318 expect(t, f.Get(ctx), []int{1, 2, 3})
1319 }
1320
1321 var int64SliceFlagTests = []struct {
1322 name string
1323 aliases []string
1324 value *Int64Slice
1325 expected string
1326 }{
1327 {"heads", nil, NewInt64Slice(), "--heads value [ --heads value ]\t"},
1328 {"H", nil, NewInt64Slice(), "-H value [ -H value ]\t"},
1329 {"heads", []string{"H"}, NewInt64Slice(int64(2), int64(17179869184)),
1330 "--heads value, -H value [ --heads value, -H value ]\t(default: 2, 17179869184)"},
1331 }
1332
1333 func TestInt64SliceFlagHelpOutput(t *testing.T) {
1334 for _, test := range int64SliceFlagTests {
1335 fl := Int64SliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
1336 output := fl.String()
1337
1338 if output != test.expected {
1339 t.Errorf("%q does not match %q", output, test.expected)
1340 }
1341 }
1342 }
1343
1344 func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
1345 defer resetEnv(os.Environ())
1346 os.Clearenv()
1347 _ = os.Setenv("APP_SMURF", "42,17179869184")
1348
1349 for _, test := range int64SliceFlagTests {
1350 fl := Int64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
1351 output := fl.String()
1352
1353 expectedSuffix := withEnvHint([]string{"APP_SMURF"}, "")
1354 if !strings.HasSuffix(output, expectedSuffix) {
1355 t.Errorf("%q does not end with"+expectedSuffix, output)
1356 }
1357 }
1358 }
1359
1360 func TestInt64SliceFlagApply_SetsAllNames(t *testing.T) {
1361 fl := Int64SliceFlag{Name: "bits", Aliases: []string{"B", "bips"}}
1362 set := flag.NewFlagSet("test", 0)
1363 if err := fl.Apply(set); err != nil {
1364 t.Error(err)
1365 }
1366
1367 err := set.Parse([]string{"--bits", "23", "-B", "3", "--bips", "99"})
1368 expect(t, err, nil)
1369 }
1370
1371 func TestInt64SliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
1372 defer resetEnv(os.Environ())
1373 os.Clearenv()
1374 _ = os.Setenv("MY_GOAT", "1 , 2")
1375 var val Int64Slice
1376 fl := Int64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
1377 set := flag.NewFlagSet("test", 0)
1378 if err := fl.Apply(set); err != nil {
1379 t.Error(err)
1380 }
1381
1382 err := set.Parse(nil)
1383 expect(t, err, nil)
1384 expect(t, val.Value(), []int64(nil))
1385 expect(t, set.Lookup("goat").Value.(*Int64Slice).Value(), []int64{1, 2})
1386 }
1387
1388 func TestInt64SliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
1389 defer resetEnv(os.Environ())
1390 os.Clearenv()
1391 _ = os.Setenv("MY_GOAT", "1 , 2")
1392 val := NewInt64Slice(3, 4)
1393 fl := Int64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
1394 set := flag.NewFlagSet("test", 0)
1395 if err := fl.Apply(set); err != nil {
1396 t.Error(err)
1397 }
1398 err := set.Parse(nil)
1399 expect(t, err, nil)
1400 expect(t, val.Value(), []int64{3, 4})
1401 expect(t, set.Lookup("goat").Value.(*Int64Slice).Value(), []int64{1, 2})
1402 }
1403
1404 func TestInt64SliceFlagApply_DefaultValueWithDestination(t *testing.T) {
1405 defValue := []int64{1, 2}
1406
1407 fl := Int64SliceFlag{Name: "country", Value: NewInt64Slice(defValue...), Destination: NewInt64Slice(3)}
1408 set := flag.NewFlagSet("test", 0)
1409 if err := fl.Apply(set); err != nil {
1410 t.Error(err)
1411 }
1412
1413 err := set.Parse([]string{})
1414 expect(t, err, nil)
1415 expect(t, defValue, fl.Destination.Value())
1416 }
1417
1418 func TestInt64SliceFlagApply_ParentContext(t *testing.T) {
1419 _ = (&App{
1420 Flags: []Flag{
1421 &Int64SliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewInt64Slice(1, 2, 3)},
1422 },
1423 Commands: []*Command{
1424 {
1425 Name: "child",
1426 Action: func(ctx *Context) error {
1427 expected := []int64{1, 2, 3}
1428 if !reflect.DeepEqual(ctx.Int64Slice("numbers"), expected) {
1429 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Int64Slice("numbers"))
1430 }
1431 if !reflect.DeepEqual(ctx.Int64Slice("n"), expected) {
1432 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Int64Slice("n"))
1433 }
1434 return nil
1435 },
1436 },
1437 },
1438 }).Run([]string{"run", "child"})
1439 }
1440
1441 func TestInt64SliceFlag_SetFromParentContext(t *testing.T) {
1442 fl := &Int64SliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewInt64Slice(1, 2, 3, 4)}
1443 set := flag.NewFlagSet("test", 0)
1444 if err := fl.Apply(set); err != nil {
1445 t.Error(err)
1446 }
1447 ctx := &Context{
1448 parentContext: &Context{
1449 flagSet: set,
1450 },
1451 flagSet: flag.NewFlagSet("empty", 0),
1452 }
1453 expected := []int64{1, 2, 3, 4}
1454 if !reflect.DeepEqual(ctx.Int64Slice("numbers"), expected) {
1455 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Int64Slice("numbers"))
1456 }
1457 }
1458 func TestInt64SliceFlag_ReturnNil(t *testing.T) {
1459 fl := &Int64SliceFlag{}
1460 set := flag.NewFlagSet("test", 0)
1461 if err := fl.Apply(set); err != nil {
1462 t.Error(err)
1463 }
1464 ctx := &Context{
1465 parentContext: &Context{
1466 flagSet: set,
1467 },
1468 flagSet: flag.NewFlagSet("empty", 0),
1469 }
1470 expected := []int64(nil)
1471 if !reflect.DeepEqual(ctx.Int64Slice("numbers"), expected) {
1472 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Int64Slice("numbers"))
1473 }
1474 }
1475
1476 func TestInt64SliceFlagValueFromContext(t *testing.T) {
1477 set := flag.NewFlagSet("test", 0)
1478 set.Var(NewInt64Slice(1, 2, 3), "myflag", "doc")
1479 ctx := NewContext(nil, set, nil)
1480 f := &Int64SliceFlag{Name: "myflag"}
1481 expect(t, f.Get(ctx), []int64{1, 2, 3})
1482 }
1483
1484 var uintSliceFlagTests = []struct {
1485 name string
1486 aliases []string
1487 value *UintSlice
1488 expected string
1489 }{
1490 {"heads", nil, NewUintSlice(), "--heads value [ --heads value ]\t"},
1491 {"H", nil, NewUintSlice(), "-H value [ -H value ]\t"},
1492 {"heads", []string{"H"}, NewUintSlice(uint(2), uint(17179869184)),
1493 "--heads value, -H value [ --heads value, -H value ]\t(default: 2, 17179869184)"},
1494 }
1495
1496 func TestUintSliceFlagHelpOutput(t *testing.T) {
1497 for _, test := range uintSliceFlagTests {
1498 fl := UintSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
1499 output := fl.String()
1500
1501 if output != test.expected {
1502 t.Errorf("%q does not match %q", output, test.expected)
1503 }
1504 }
1505 }
1506
1507 func TestUintSliceFlagWithEnvVarHelpOutput(t *testing.T) {
1508 defer resetEnv(os.Environ())
1509 os.Clearenv()
1510 _ = os.Setenv("APP_SMURF", "42,17179869184")
1511
1512 for _, test := range uintSliceFlagTests {
1513 fl := UintSliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
1514 output := fl.String()
1515
1516 expectedSuffix := withEnvHint([]string{"APP_SMURF"}, "")
1517 if !strings.HasSuffix(output, expectedSuffix) {
1518 t.Errorf("%q does not end with"+expectedSuffix, output)
1519 }
1520 }
1521 }
1522
1523 func TestUintSliceFlagApply_SetsAllNames(t *testing.T) {
1524 fl := UintSliceFlag{Name: "bits", Aliases: []string{"B", "bips"}}
1525 set := flag.NewFlagSet("test", 0)
1526 if err := fl.Apply(set); err != nil {
1527 t.Error(err)
1528 }
1529
1530 err := set.Parse([]string{"--bits", "23", "-B", "3", "--bips", "99"})
1531 expect(t, err, nil)
1532 }
1533
1534 func TestUintSliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
1535 defer resetEnv(os.Environ())
1536 os.Clearenv()
1537 _ = os.Setenv("MY_GOAT", "1 , 2")
1538 var val UintSlice
1539 fl := UintSliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
1540 set := flag.NewFlagSet("test", 0)
1541 if err := fl.Apply(set); err != nil {
1542 t.Error(err)
1543 }
1544
1545 err := set.Parse(nil)
1546 expect(t, err, nil)
1547 expect(t, val.Value(), []uint(nil))
1548 expect(t, set.Lookup("goat").Value.(*UintSlice).Value(), []uint{1, 2})
1549 }
1550
1551 func TestUintSliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
1552 defer resetEnv(os.Environ())
1553 os.Clearenv()
1554 _ = os.Setenv("MY_GOAT", "1 , 2")
1555 val := NewUintSlice(3, 4)
1556 fl := UintSliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
1557 set := flag.NewFlagSet("test", 0)
1558 if err := fl.Apply(set); err != nil {
1559 t.Error(err)
1560 }
1561 err := set.Parse(nil)
1562 expect(t, err, nil)
1563 expect(t, val.Value(), []uint{3, 4})
1564 expect(t, set.Lookup("goat").Value.(*UintSlice).Value(), []uint{1, 2})
1565 }
1566
1567 func TestUintSliceFlagApply_DefaultValueWithDestination(t *testing.T) {
1568 defValue := []uint{1, 2}
1569
1570 fl := UintSliceFlag{Name: "country", Value: NewUintSlice(defValue...), Destination: NewUintSlice(3)}
1571 set := flag.NewFlagSet("test", 0)
1572 if err := fl.Apply(set); err != nil {
1573 t.Error(err)
1574 }
1575
1576 err := set.Parse([]string{})
1577 expect(t, err, nil)
1578 expect(t, defValue, fl.Destination.Value())
1579 }
1580
1581 func TestUintSliceFlagApply_ParentContext(t *testing.T) {
1582 _ = (&App{
1583 Flags: []Flag{
1584 &UintSliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewUintSlice(1, 2, 3)},
1585 },
1586 Commands: []*Command{
1587 {
1588 Name: "child",
1589 Action: func(ctx *Context) error {
1590 expected := []uint{1, 2, 3}
1591 if !reflect.DeepEqual(ctx.UintSlice("numbers"), expected) {
1592 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.UintSlice("numbers"))
1593 }
1594 if !reflect.DeepEqual(ctx.UintSlice("n"), expected) {
1595 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.UintSlice("n"))
1596 }
1597 return nil
1598 },
1599 },
1600 },
1601 }).Run([]string{"run", "child"})
1602 }
1603
1604 func TestUintSliceFlag_SetFromParentContext(t *testing.T) {
1605 fl := &UintSliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewUintSlice(1, 2, 3, 4)}
1606 set := flag.NewFlagSet("test", 0)
1607 if err := fl.Apply(set); err != nil {
1608 t.Error(err)
1609 }
1610 ctx := &Context{
1611 parentContext: &Context{
1612 flagSet: set,
1613 },
1614 flagSet: flag.NewFlagSet("empty", 0),
1615 }
1616 expected := []uint{1, 2, 3, 4}
1617 if !reflect.DeepEqual(ctx.UintSlice("numbers"), expected) {
1618 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.UintSlice("numbers"))
1619 }
1620 }
1621 func TestUintSliceFlag_ReturnNil(t *testing.T) {
1622 fl := &UintSliceFlag{}
1623 set := flag.NewFlagSet("test", 0)
1624 if err := fl.Apply(set); err != nil {
1625 t.Error(err)
1626 }
1627 ctx := &Context{
1628 parentContext: &Context{
1629 flagSet: set,
1630 },
1631 flagSet: flag.NewFlagSet("empty", 0),
1632 }
1633 expected := []uint(nil)
1634 if !reflect.DeepEqual(ctx.UintSlice("numbers"), expected) {
1635 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.UintSlice("numbers"))
1636 }
1637 }
1638
1639 var uint64SliceFlagTests = []struct {
1640 name string
1641 aliases []string
1642 value *Uint64Slice
1643 expected string
1644 }{
1645 {"heads", nil, NewUint64Slice(), "--heads value [ --heads value ]\t"},
1646 {"H", nil, NewUint64Slice(), "-H value [ -H value ]\t"},
1647 {"heads", []string{"H"}, NewUint64Slice(uint64(2), uint64(17179869184)),
1648 "--heads value, -H value [ --heads value, -H value ]\t(default: 2, 17179869184)"},
1649 }
1650
1651 func TestUint64SliceFlagHelpOutput(t *testing.T) {
1652 for _, test := range uint64SliceFlagTests {
1653 fl := Uint64SliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
1654 output := fl.String()
1655
1656 if output != test.expected {
1657 t.Errorf("%q does not match %q", output, test.expected)
1658 }
1659 }
1660 }
1661
1662 func TestUint64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
1663 defer resetEnv(os.Environ())
1664 os.Clearenv()
1665 _ = os.Setenv("APP_SMURF", "42,17179869184")
1666
1667 for _, test := range uint64SliceFlagTests {
1668 fl := Uint64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
1669 output := fl.String()
1670
1671 expectedSuffix := withEnvHint([]string{"APP_SMURF"}, "")
1672 if !strings.HasSuffix(output, expectedSuffix) {
1673 t.Errorf("%q does not end with"+expectedSuffix, output)
1674 }
1675 }
1676 }
1677
1678 func TestUint64SliceFlagApply_SetsAllNames(t *testing.T) {
1679 fl := Uint64SliceFlag{Name: "bits", Aliases: []string{"B", "bips"}}
1680 set := flag.NewFlagSet("test", 0)
1681 if err := fl.Apply(set); err != nil {
1682 t.Error(err)
1683 }
1684
1685 err := set.Parse([]string{"--bits", "23", "-B", "3", "--bips", "99"})
1686 expect(t, err, nil)
1687 }
1688
1689 func TestUint64SliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
1690 defer resetEnv(os.Environ())
1691 os.Clearenv()
1692 _ = os.Setenv("MY_GOAT", "1 , 2")
1693 var val Uint64Slice
1694 fl := Uint64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
1695 set := flag.NewFlagSet("test", 0)
1696 if err := fl.Apply(set); err != nil {
1697 t.Error(err)
1698 }
1699
1700 err := set.Parse(nil)
1701 expect(t, err, nil)
1702 expect(t, val.Value(), []uint64(nil))
1703 expect(t, set.Lookup("goat").Value.(*Uint64Slice).Value(), []uint64{1, 2})
1704 }
1705
1706 func TestUint64SliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
1707 defer resetEnv(os.Environ())
1708 os.Clearenv()
1709 _ = os.Setenv("MY_GOAT", "1 , 2")
1710 val := NewUint64Slice(3, 4)
1711 fl := Uint64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
1712 set := flag.NewFlagSet("test", 0)
1713 if err := fl.Apply(set); err != nil {
1714 t.Error(err)
1715 }
1716 err := set.Parse(nil)
1717 expect(t, err, nil)
1718 expect(t, val.Value(), []uint64{3, 4})
1719 expect(t, set.Lookup("goat").Value.(*Uint64Slice).Value(), []uint64{1, 2})
1720 }
1721
1722 func TestUint64SliceFlagApply_DefaultValueWithDestination(t *testing.T) {
1723 defValue := []uint64{1, 2}
1724
1725 fl := Uint64SliceFlag{Name: "country", Value: NewUint64Slice(defValue...), Destination: NewUint64Slice(3)}
1726 set := flag.NewFlagSet("test", 0)
1727 if err := fl.Apply(set); err != nil {
1728 t.Error(err)
1729 }
1730
1731 err := set.Parse([]string{})
1732 expect(t, err, nil)
1733 expect(t, defValue, fl.Destination.Value())
1734 }
1735
1736 func TestUint64SliceFlagApply_ParentContext(t *testing.T) {
1737 _ = (&App{
1738 Flags: []Flag{
1739 &Uint64SliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewUint64Slice(1, 2, 3)},
1740 },
1741 Commands: []*Command{
1742 {
1743 Name: "child",
1744 Action: func(ctx *Context) error {
1745 expected := []uint64{1, 2, 3}
1746 if !reflect.DeepEqual(ctx.Uint64Slice("numbers"), expected) {
1747 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Uint64Slice("numbers"))
1748 }
1749 if !reflect.DeepEqual(ctx.Uint64Slice("n"), expected) {
1750 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Uint64Slice("n"))
1751 }
1752 return nil
1753 },
1754 },
1755 },
1756 }).Run([]string{"run", "child"})
1757 }
1758
1759 func TestUint64SliceFlag_SetFromParentContext(t *testing.T) {
1760 fl := &Uint64SliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewUint64Slice(1, 2, 3, 4)}
1761 set := flag.NewFlagSet("test", 0)
1762 if err := fl.Apply(set); err != nil {
1763 t.Error(err)
1764 }
1765 ctx := &Context{
1766 parentContext: &Context{
1767 flagSet: set,
1768 },
1769 flagSet: flag.NewFlagSet("empty", 0),
1770 }
1771 expected := []uint64{1, 2, 3, 4}
1772 if !reflect.DeepEqual(ctx.Uint64Slice("numbers"), expected) {
1773 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Uint64Slice("numbers"))
1774 }
1775 }
1776 func TestUint64SliceFlag_ReturnNil(t *testing.T) {
1777 fl := &Uint64SliceFlag{}
1778 set := flag.NewFlagSet("test", 0)
1779 if err := fl.Apply(set); err != nil {
1780 t.Error(err)
1781 }
1782 ctx := &Context{
1783 parentContext: &Context{
1784 flagSet: set,
1785 },
1786 flagSet: flag.NewFlagSet("empty", 0),
1787 }
1788 expected := []uint64(nil)
1789 if !reflect.DeepEqual(ctx.Uint64Slice("numbers"), expected) {
1790 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Uint64Slice("numbers"))
1791 }
1792 }
1793
1794 var float64FlagTests = []struct {
1795 name string
1796 expected string
1797 }{
1798 {"hooting", "--hooting value\t(default: 0.1)"},
1799 {"H", "-H value\t(default: 0.1)"},
1800 }
1801
1802 func TestFloat64FlagHelpOutput(t *testing.T) {
1803 for _, test := range float64FlagTests {
1804 f := &Float64Flag{Name: test.name, Value: 0.1}
1805 output := f.String()
1806
1807 if output != test.expected {
1808 t.Errorf("%q does not match %q", output, test.expected)
1809 }
1810 }
1811 }
1812
1813 func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
1814 defer resetEnv(os.Environ())
1815 os.Clearenv()
1816 _ = os.Setenv("APP_BAZ", "99.4")
1817
1818 for _, test := range float64FlagTests {
1819 fl := &Float64Flag{Name: test.name, EnvVars: []string{"APP_BAZ"}}
1820 output := fl.String()
1821
1822 expectedSuffix := withEnvHint([]string{"APP_BAZ"}, "")
1823 if !strings.HasSuffix(output, expectedSuffix) {
1824 t.Errorf("%s does not end with"+expectedSuffix, output)
1825 }
1826 }
1827 }
1828
1829 func TestFloat64FlagApply_SetsAllNames(t *testing.T) {
1830 v := 99.1
1831 fl := Float64Flag{Name: "noodles", Aliases: []string{"N", "nurbles"}, Destination: &v}
1832 set := flag.NewFlagSet("test", 0)
1833 if err := fl.Apply(set); err != nil {
1834 t.Error(err)
1835 }
1836
1837 err := set.Parse([]string{"--noodles", "1.3", "-N", "11", "--nurbles", "43.33333"})
1838 expect(t, err, nil)
1839 expect(t, v, float64(43.33333))
1840 }
1841
1842 func TestFloat64FlagValueFromContext(t *testing.T) {
1843 set := flag.NewFlagSet("test", 0)
1844 set.Float64("myflag", 1.23, "doc")
1845 ctx := NewContext(nil, set, nil)
1846 f := &Float64Flag{Name: "myflag"}
1847 expect(t, f.Get(ctx), 1.23)
1848 }
1849
1850 var float64SliceFlagTests = []struct {
1851 name string
1852 aliases []string
1853 value *Float64Slice
1854 expected string
1855 }{
1856 {"heads", nil, NewFloat64Slice(), "--heads value [ --heads value ]\t"},
1857 {"H", nil, NewFloat64Slice(), "-H value [ -H value ]\t"},
1858 {"heads", []string{"H"}, NewFloat64Slice(0.1234, -10.5),
1859 "--heads value, -H value [ --heads value, -H value ]\t(default: 0.1234, -10.5)"},
1860 }
1861
1862 func TestFloat64SliceFlagHelpOutput(t *testing.T) {
1863 for _, test := range float64SliceFlagTests {
1864 fl := Float64SliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value}
1865 output := fl.String()
1866
1867 if output != test.expected {
1868 t.Errorf("%q does not match %q", output, test.expected)
1869 }
1870 }
1871 }
1872
1873 func TestFloat64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
1874 defer resetEnv(os.Environ())
1875 os.Clearenv()
1876 _ = os.Setenv("APP_SMURF", "0.1234,-10.5")
1877 for _, test := range float64SliceFlagTests {
1878 fl := Float64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
1879 output := fl.String()
1880
1881 expectedSuffix := withEnvHint([]string{"APP_SMURF"}, "")
1882 if !strings.HasSuffix(output, expectedSuffix) {
1883 t.Errorf("%q does not end with"+expectedSuffix, output)
1884 }
1885 }
1886 }
1887
1888 func TestFloat64SliceFlagApply_SetsAllNames(t *testing.T) {
1889 fl := Float64SliceFlag{Name: "bits", Aliases: []string{"B", "bips"}}
1890 set := flag.NewFlagSet("test", 0)
1891 if err := fl.Apply(set); err != nil {
1892 t.Error(err)
1893 }
1894
1895 err := set.Parse([]string{"--bits", "23", "-B", "3", "--bips", "99"})
1896 expect(t, err, nil)
1897 }
1898
1899 func TestFloat64SliceFlagApply_UsesEnvValues_noDefault(t *testing.T) {
1900 defer resetEnv(os.Environ())
1901 os.Clearenv()
1902 _ = os.Setenv("MY_GOAT", "1.0 , 2.0")
1903 var val Float64Slice
1904 fl := Float64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: &val}
1905 set := flag.NewFlagSet("test", 0)
1906 if err := fl.Apply(set); err != nil {
1907 t.Error(err)
1908 }
1909
1910 err := set.Parse(nil)
1911 expect(t, err, nil)
1912 expect(t, val.Value(), []float64(nil))
1913 expect(t, set.Lookup("goat").Value.(*Float64Slice).Value(), []float64{1, 2})
1914 }
1915
1916 func TestFloat64SliceFlagApply_UsesEnvValues_withDefault(t *testing.T) {
1917 defer resetEnv(os.Environ())
1918 os.Clearenv()
1919 _ = os.Setenv("MY_GOAT", "1.0 , 2.0")
1920 val := NewFloat64Slice(3.0, 4.0)
1921 fl := Float64SliceFlag{Name: "goat", EnvVars: []string{"MY_GOAT"}, Value: val}
1922 set := flag.NewFlagSet("test", 0)
1923 if err := fl.Apply(set); err != nil {
1924 t.Error(err)
1925 }
1926 err := set.Parse(nil)
1927 expect(t, err, nil)
1928 expect(t, val.Value(), []float64{3, 4})
1929 expect(t, set.Lookup("goat").Value.(*Float64Slice).Value(), []float64{1, 2})
1930 }
1931
1932 func TestFloat64SliceFlagApply_DefaultValueWithDestination(t *testing.T) {
1933 defValue := []float64{1.0, 2.0}
1934
1935 fl := Float64SliceFlag{Name: "country", Value: NewFloat64Slice(defValue...), Destination: NewFloat64Slice(3)}
1936 set := flag.NewFlagSet("test", 0)
1937 if err := fl.Apply(set); err != nil {
1938 t.Error(err)
1939 }
1940
1941 err := set.Parse([]string{})
1942 expect(t, err, nil)
1943 expect(t, defValue, fl.Destination.Value())
1944 }
1945
1946 func TestFloat64SliceFlagValueFromContext(t *testing.T) {
1947 set := flag.NewFlagSet("test", 0)
1948 set.Var(NewFloat64Slice(1.23, 4.56), "myflag", "doc")
1949 ctx := NewContext(nil, set, nil)
1950 f := &Float64SliceFlag{Name: "myflag"}
1951 expect(t, f.Get(ctx), []float64{1.23, 4.56})
1952 }
1953
1954 func TestFloat64SliceFlagApply_ParentContext(t *testing.T) {
1955 _ = (&App{
1956 Flags: []Flag{
1957 &Float64SliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewFloat64Slice(1.0, 2.0, 3.0)},
1958 },
1959 Commands: []*Command{
1960 {
1961 Name: "child",
1962 Action: func(ctx *Context) error {
1963 expected := []float64{1.0, 2.0, 3.0}
1964 if !reflect.DeepEqual(ctx.Float64Slice("numbers"), expected) {
1965 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Float64Slice("numbers"))
1966 }
1967 if !reflect.DeepEqual(ctx.Float64Slice("n"), expected) {
1968 t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Float64Slice("n"))
1969 }
1970 return nil
1971 },
1972 },
1973 },
1974 }).Run([]string{"run", "child"})
1975 }
1976
1977 var genericFlagTests = []struct {
1978 name string
1979 value Generic
1980 expected string
1981 }{
1982 {"toads", &Parser{"abc", "def"}, "--toads value\ttest flag (default: abc,def)"},
1983 {"t", &Parser{"abc", "def"}, "-t value\ttest flag (default: abc,def)"},
1984 }
1985
1986 func TestGenericFlagHelpOutput(t *testing.T) {
1987 for _, test := range genericFlagTests {
1988 fl := &GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"}
1989
1990 tfs := flag.NewFlagSet("test", 0)
1991 if err := fl.Apply(tfs); err != nil {
1992 t.Error(err)
1993 return
1994 }
1995 output := fl.String()
1996
1997 if output != test.expected {
1998 t.Errorf("%q does not match %q", output, test.expected)
1999 }
2000 }
2001 }
2002
2003 func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
2004 defer resetEnv(os.Environ())
2005 os.Clearenv()
2006 _ = os.Setenv("APP_ZAP", "3")
2007
2008 for _, test := range genericFlagTests {
2009 fl := &GenericFlag{Name: test.name, EnvVars: []string{"APP_ZAP"}}
2010 output := fl.String()
2011
2012 expectedSuffix := withEnvHint([]string{"APP_ZAP"}, "")
2013 if !strings.HasSuffix(output, expectedSuffix) {
2014 t.Errorf("%s does not end with"+expectedSuffix, output)
2015 }
2016 }
2017 }
2018
2019 func TestGenericFlagApply_SetsAllNames(t *testing.T) {
2020 fl := GenericFlag{Name: "orbs", Aliases: []string{"O", "obrs"}, Value: &Parser{}}
2021 set := flag.NewFlagSet("test", 0)
2022 if err := fl.Apply(set); err != nil {
2023 t.Error(err)
2024 }
2025
2026 err := set.Parse([]string{"--orbs", "eleventy,3", "-O", "4,bloop", "--obrs", "19,s"})
2027 expect(t, err, nil)
2028 }
2029
2030 func TestGenericFlagValueFromContext(t *testing.T) {
2031 set := flag.NewFlagSet("test", 0)
2032 set.Var(&Parser{"abc", "def"}, "myflag", "doc")
2033 ctx := NewContext(nil, set, nil)
2034 f := &GenericFlag{Name: "myflag"}
2035 expect(t, f.Get(ctx), &Parser{"abc", "def"})
2036 }
2037
2038 func TestParseMultiString(t *testing.T) {
2039 _ = (&App{
2040 Flags: []Flag{
2041 &StringFlag{Name: "serve", Aliases: []string{"s"}},
2042 },
2043 Action: func(ctx *Context) error {
2044 if ctx.String("serve") != "10" {
2045 t.Errorf("main name not set")
2046 }
2047 if ctx.String("s") != "10" {
2048 t.Errorf("short name not set")
2049 }
2050 return nil
2051 },
2052 }).Run([]string{"run", "-s", "10"})
2053 }
2054
2055 func TestParseDestinationString(t *testing.T) {
2056 var dest string
2057 _ = (&App{
2058 Flags: []Flag{
2059 &StringFlag{
2060 Name: "dest",
2061 Destination: &dest,
2062 },
2063 },
2064 Action: func(ctx *Context) error {
2065 if dest != "10" {
2066 t.Errorf("expected destination String 10")
2067 }
2068 return nil
2069 },
2070 }).Run([]string{"run", "--dest", "10"})
2071 }
2072
2073 func TestParseMultiStringFromEnv(t *testing.T) {
2074 defer resetEnv(os.Environ())
2075 os.Clearenv()
2076 _ = os.Setenv("APP_COUNT", "20")
2077 _ = (&App{
2078 Flags: []Flag{
2079 &StringFlag{Name: "count", Aliases: []string{"c"}, EnvVars: []string{"APP_COUNT"}},
2080 },
2081 Action: func(ctx *Context) error {
2082 if ctx.String("count") != "20" {
2083 t.Errorf("main name not set")
2084 }
2085 if ctx.String("c") != "20" {
2086 t.Errorf("short name not set")
2087 }
2088 return nil
2089 },
2090 }).Run([]string{"run"})
2091 }
2092
2093 func TestParseMultiStringFromEnvCascade(t *testing.T) {
2094 defer resetEnv(os.Environ())
2095 os.Clearenv()
2096 _ = os.Setenv("APP_COUNT", "20")
2097 _ = (&App{
2098 Flags: []Flag{
2099 &StringFlag{Name: "count", Aliases: []string{"c"}, EnvVars: []string{"COMPAT_COUNT", "APP_COUNT"}},
2100 },
2101 Action: func(ctx *Context) error {
2102 if ctx.String("count") != "20" {
2103 t.Errorf("main name not set")
2104 }
2105 if ctx.String("c") != "20" {
2106 t.Errorf("short name not set")
2107 }
2108 return nil
2109 },
2110 }).Run([]string{"run"})
2111 }
2112
2113 func TestParseMultiStringSlice(t *testing.T) {
2114 _ = (&App{
2115 Flags: []Flag{
2116 &StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice()},
2117 },
2118 Action: func(ctx *Context) error {
2119 expected := []string{"10", "20"}
2120 if !reflect.DeepEqual(ctx.StringSlice("serve"), expected) {
2121 t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
2122 }
2123 if !reflect.DeepEqual(ctx.StringSlice("s"), expected) {
2124 t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
2125 }
2126 return nil
2127 },
2128 }).Run([]string{"run", "-s", "10", "-s", "20"})
2129 }
2130
2131 func TestParseMultiStringSliceWithDefaults(t *testing.T) {
2132 _ = (&App{
2133 Flags: []Flag{
2134 &StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice("9", "2")},
2135 },
2136 Action: func(ctx *Context) error {
2137 expected := []string{"10", "20"}
2138 if !reflect.DeepEqual(ctx.StringSlice("serve"), expected) {
2139 t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
2140 }
2141 if !reflect.DeepEqual(ctx.StringSlice("s"), expected) {
2142 t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
2143 }
2144 return nil
2145 },
2146 }).Run([]string{"run", "-s", "10", "-s", "20"})
2147 }
2148
2149 func TestParseMultiStringSliceWithDestination(t *testing.T) {
2150 dest := &StringSlice{}
2151 _ = (&App{
2152 Flags: []Flag{
2153 &StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Destination: dest},
2154 },
2155 Action: func(ctx *Context) error {
2156 expected := []string{"10", "20"}
2157 if !reflect.DeepEqual(dest.slice, expected) {
2158 t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
2159 }
2160 if !reflect.DeepEqual(dest.slice, expected) {
2161 t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
2162 }
2163 return nil
2164 },
2165 }).Run([]string{"run", "-s", "10", "-s", "20"})
2166 }
2167
2168 func TestParseMultiStringSliceWithDestinationAndEnv(t *testing.T) {
2169 defer resetEnv(os.Environ())
2170 os.Clearenv()
2171 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2172
2173 dest := &StringSlice{}
2174 _ = (&App{
2175 Flags: []Flag{
2176 &StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Destination: dest, EnvVars: []string{"APP_INTERVALS"}},
2177 },
2178 Action: func(ctx *Context) error {
2179 expected := []string{"10", "20"}
2180 if !reflect.DeepEqual(dest.slice, expected) {
2181 t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
2182 }
2183 if !reflect.DeepEqual(dest.slice, expected) {
2184 t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
2185 }
2186 return nil
2187 },
2188 }).Run([]string{"run", "-s", "10", "-s", "20"})
2189 }
2190
2191 func TestParseMultiFloat64SliceWithDestinationAndEnv(t *testing.T) {
2192 defer resetEnv(os.Environ())
2193 os.Clearenv()
2194 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2195
2196 dest := &Float64Slice{}
2197 _ = (&App{
2198 Flags: []Flag{
2199 &Float64SliceFlag{Name: "serve", Aliases: []string{"s"}, Destination: dest, EnvVars: []string{"APP_INTERVALS"}},
2200 },
2201 Action: func(ctx *Context) error {
2202 expected := []float64{10, 20}
2203 if !reflect.DeepEqual(dest.slice, expected) {
2204 t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
2205 }
2206 if !reflect.DeepEqual(dest.slice, expected) {
2207 t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
2208 }
2209 return nil
2210 },
2211 }).Run([]string{"run", "-s", "10", "-s", "20"})
2212 }
2213
2214 func TestParseMultiInt64SliceWithDestinationAndEnv(t *testing.T) {
2215 defer resetEnv(os.Environ())
2216 os.Clearenv()
2217 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2218
2219 dest := &Int64Slice{}
2220 _ = (&App{
2221 Flags: []Flag{
2222 &Int64SliceFlag{Name: "serve", Aliases: []string{"s"}, Destination: dest, EnvVars: []string{"APP_INTERVALS"}},
2223 },
2224 Action: func(ctx *Context) error {
2225 expected := []int64{10, 20}
2226 if !reflect.DeepEqual(dest.slice, expected) {
2227 t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
2228 }
2229 if !reflect.DeepEqual(dest.slice, expected) {
2230 t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
2231 }
2232 return nil
2233 },
2234 }).Run([]string{"run", "-s", "10", "-s", "20"})
2235 }
2236
2237 func TestParseMultiIntSliceWithDestinationAndEnv(t *testing.T) {
2238 defer resetEnv(os.Environ())
2239 os.Clearenv()
2240 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2241
2242 dest := &IntSlice{}
2243 _ = (&App{
2244 Flags: []Flag{
2245 &IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Destination: dest, EnvVars: []string{"APP_INTERVALS"}},
2246 },
2247 Action: func(ctx *Context) error {
2248 expected := []int{10, 20}
2249 if !reflect.DeepEqual(dest.slice, expected) {
2250 t.Errorf("main name not set: %v != %v", expected, ctx.StringSlice("serve"))
2251 }
2252 if !reflect.DeepEqual(dest.slice, expected) {
2253 t.Errorf("short name not set: %v != %v", expected, ctx.StringSlice("s"))
2254 }
2255 return nil
2256 },
2257 }).Run([]string{"run", "-s", "10", "-s", "20"})
2258 }
2259
2260 func TestParseMultiStringSliceWithDefaultsUnset(t *testing.T) {
2261 _ = (&App{
2262 Flags: []Flag{
2263 &StringSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewStringSlice("9", "2")},
2264 },
2265 Action: func(ctx *Context) error {
2266 if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"9", "2"}) {
2267 t.Errorf("main name not set: %v", ctx.StringSlice("serve"))
2268 }
2269 if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"9", "2"}) {
2270 t.Errorf("short name not set: %v", ctx.StringSlice("s"))
2271 }
2272 return nil
2273 },
2274 }).Run([]string{"run"})
2275 }
2276
2277 func TestParseMultiStringSliceFromEnv(t *testing.T) {
2278 defer resetEnv(os.Environ())
2279 os.Clearenv()
2280 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2281
2282 _ = (&App{
2283 Flags: []Flag{
2284 &StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice(), EnvVars: []string{"APP_INTERVALS"}},
2285 },
2286 Action: func(ctx *Context) error {
2287 if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
2288 t.Errorf("main name not set from env")
2289 }
2290 if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
2291 t.Errorf("short name not set from env")
2292 }
2293 return nil
2294 },
2295 }).Run([]string{"run"})
2296 }
2297
2298 func TestParseMultiStringSliceFromEnvWithDefaults(t *testing.T) {
2299 defer resetEnv(os.Environ())
2300 os.Clearenv()
2301 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2302
2303 _ = (&App{
2304 Flags: []Flag{
2305 &StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice("1", "2", "5"), EnvVars: []string{"APP_INTERVALS"}},
2306 },
2307 Action: func(ctx *Context) error {
2308 if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
2309 t.Errorf("main name not set from env")
2310 }
2311 if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
2312 t.Errorf("short name not set from env")
2313 }
2314 return nil
2315 },
2316 }).Run([]string{"run"})
2317 }
2318
2319 func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
2320 defer resetEnv(os.Environ())
2321 os.Clearenv()
2322 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2323
2324 _ = (&App{
2325 Flags: []Flag{
2326 &StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice(), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}},
2327 },
2328 Action: func(ctx *Context) error {
2329 if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
2330 t.Errorf("main name not set from env")
2331 }
2332 if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
2333 t.Errorf("short name not set from env")
2334 }
2335 return nil
2336 },
2337 }).Run([]string{"run"})
2338 }
2339
2340 func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) {
2341 defer resetEnv(os.Environ())
2342 os.Clearenv()
2343 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2344
2345 _ = (&App{
2346 Flags: []Flag{
2347 &StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewStringSlice("1", "2", "5"), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}},
2348 },
2349 Action: func(ctx *Context) error {
2350 if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
2351 t.Errorf("main name not set from env")
2352 }
2353 if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
2354 t.Errorf("short name not set from env")
2355 }
2356 return nil
2357 },
2358 }).Run([]string{"run"})
2359 }
2360
2361 func TestParseMultiStringSliceFromEnvWithDestination(t *testing.T) {
2362 defer resetEnv(os.Environ())
2363 os.Clearenv()
2364 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2365
2366 dest := &StringSlice{}
2367 _ = (&App{
2368 Flags: []Flag{
2369 &StringSliceFlag{Name: "intervals", Aliases: []string{"i"}, Destination: dest, EnvVars: []string{"APP_INTERVALS"}},
2370 },
2371 Action: func(ctx *Context) error {
2372 if !reflect.DeepEqual(dest.slice, []string{"20", "30", "40"}) {
2373 t.Errorf("main name not set from env")
2374 }
2375 if !reflect.DeepEqual(dest.slice, []string{"20", "30", "40"}) {
2376 t.Errorf("short name not set from env")
2377 }
2378 return nil
2379 },
2380 }).Run([]string{"run"})
2381 }
2382
2383 func TestParseMultiInt(t *testing.T) {
2384 _ = (&App{
2385 Flags: []Flag{
2386 &IntFlag{Name: "serve", Aliases: []string{"s"}},
2387 },
2388 Action: func(ctx *Context) error {
2389 if ctx.Int("serve") != 10 {
2390 t.Errorf("main name not set")
2391 }
2392 if ctx.Int("s") != 10 {
2393 t.Errorf("short name not set")
2394 }
2395 return nil
2396 },
2397 }).Run([]string{"run", "-s", "10"})
2398 }
2399
2400 func TestParseDestinationInt(t *testing.T) {
2401 var dest int
2402 _ = (&App{
2403 Flags: []Flag{
2404 &IntFlag{
2405 Name: "dest",
2406 Destination: &dest,
2407 },
2408 },
2409 Action: func(ctx *Context) error {
2410 if dest != 10 {
2411 t.Errorf("expected destination Int 10")
2412 }
2413 return nil
2414 },
2415 }).Run([]string{"run", "--dest", "10"})
2416 }
2417
2418 func TestParseMultiIntFromEnv(t *testing.T) {
2419 defer resetEnv(os.Environ())
2420 os.Clearenv()
2421 _ = os.Setenv("APP_TIMEOUT_SECONDS", "10")
2422 _ = (&App{
2423 Flags: []Flag{
2424 &IntFlag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"APP_TIMEOUT_SECONDS"}},
2425 },
2426 Action: func(ctx *Context) error {
2427 if ctx.Int("timeout") != 10 {
2428 t.Errorf("main name not set")
2429 }
2430 if ctx.Int("t") != 10 {
2431 t.Errorf("short name not set")
2432 }
2433 return nil
2434 },
2435 }).Run([]string{"run"})
2436 }
2437
2438 func TestParseMultiIntFromEnvCascade(t *testing.T) {
2439 defer resetEnv(os.Environ())
2440 os.Clearenv()
2441 _ = os.Setenv("APP_TIMEOUT_SECONDS", "10")
2442 _ = (&App{
2443 Flags: []Flag{
2444 &IntFlag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"COMPAT_TIMEOUT_SECONDS", "APP_TIMEOUT_SECONDS"}},
2445 },
2446 Action: func(ctx *Context) error {
2447 if ctx.Int("timeout") != 10 {
2448 t.Errorf("main name not set")
2449 }
2450 if ctx.Int("t") != 10 {
2451 t.Errorf("short name not set")
2452 }
2453 return nil
2454 },
2455 }).Run([]string{"run"})
2456 }
2457
2458 func TestParseMultiIntSlice(t *testing.T) {
2459 _ = (&App{
2460 Flags: []Flag{
2461 &IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewIntSlice()},
2462 },
2463 Action: func(ctx *Context) error {
2464 if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
2465 t.Errorf("main name not set")
2466 }
2467 if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
2468 t.Errorf("short name not set")
2469 }
2470 return nil
2471 },
2472 }).Run([]string{"run", "-s", "10", "-s", "20"})
2473 }
2474
2475 func TestParseMultiIntSliceWithDefaults(t *testing.T) {
2476 _ = (&App{
2477 Flags: []Flag{
2478 &IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewIntSlice(9, 2)},
2479 },
2480 Action: func(ctx *Context) error {
2481 if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
2482 t.Errorf("main name not set")
2483 }
2484 if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
2485 t.Errorf("short name not set")
2486 }
2487 return nil
2488 },
2489 }).Run([]string{"run", "-s", "10", "-s", "20"})
2490 }
2491
2492 func TestParseMultiIntSliceWithDefaultsUnset(t *testing.T) {
2493 _ = (&App{
2494 Flags: []Flag{
2495 &IntSliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewIntSlice(9, 2)},
2496 },
2497 Action: func(ctx *Context) error {
2498 if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{9, 2}) {
2499 t.Errorf("main name not set")
2500 }
2501 if !reflect.DeepEqual(ctx.IntSlice("s"), []int{9, 2}) {
2502 t.Errorf("short name not set")
2503 }
2504 return nil
2505 },
2506 }).Run([]string{"run"})
2507 }
2508
2509 func TestParseMultiIntSliceFromEnv(t *testing.T) {
2510 defer resetEnv(os.Environ())
2511 os.Clearenv()
2512 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2513
2514 _ = (&App{
2515 Flags: []Flag{
2516 &IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(), EnvVars: []string{"APP_INTERVALS"}},
2517 },
2518 Action: func(ctx *Context) error {
2519 if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
2520 t.Errorf("main name not set from env")
2521 }
2522 if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
2523 t.Errorf("short name not set from env")
2524 }
2525 return nil
2526 },
2527 }).Run([]string{"run"})
2528 }
2529
2530 func TestParseMultiIntSliceFromEnvWithDefaults(t *testing.T) {
2531 defer resetEnv(os.Environ())
2532 os.Clearenv()
2533 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2534
2535 _ = (&App{
2536 Flags: []Flag{
2537 &IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(1, 2, 5), EnvVars: []string{"APP_INTERVALS"}},
2538 },
2539 Action: func(ctx *Context) error {
2540 if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
2541 t.Errorf("main name not set from env")
2542 }
2543 if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
2544 t.Errorf("short name not set from env")
2545 }
2546 return nil
2547 },
2548 }).Run([]string{"run"})
2549 }
2550
2551 func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
2552 defer resetEnv(os.Environ())
2553 os.Clearenv()
2554 _ = os.Setenv("APP_INTERVALS", "20,30,40")
2555
2556 _ = (&App{
2557 Flags: []Flag{
2558 &IntSliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewIntSlice(), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}},
2559 },
2560 Action: func(ctx *Context) error {
2561 if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
2562 t.Errorf("main name not set from env")
2563 }
2564 if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
2565 t.Errorf("short name not set from env")
2566 }
2567 return nil
2568 },
2569 }).Run([]string{"run"})
2570 }
2571
2572 func TestParseMultiInt64Slice(t *testing.T) {
2573 _ = (&App{
2574 Flags: []Flag{
2575 &Int64SliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewInt64Slice()},
2576 },
2577 Action: func(ctx *Context) error {
2578 if !reflect.DeepEqual(ctx.Int64Slice("serve"), []int64{10, 17179869184}) {
2579 t.Errorf("main name not set")
2580 }
2581 if !reflect.DeepEqual(ctx.Int64Slice("s"), []int64{10, 17179869184}) {
2582 t.Errorf("short name not set")
2583 }
2584 return nil
2585 },
2586 }).Run([]string{"run", "-s", "10", "-s", "17179869184"})
2587 }
2588
2589 func TestParseMultiInt64SliceFromEnv(t *testing.T) {
2590 defer resetEnv(os.Environ())
2591 os.Clearenv()
2592 _ = os.Setenv("APP_INTERVALS", "20,30,17179869184")
2593
2594 _ = (&App{
2595 Flags: []Flag{
2596 &Int64SliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewInt64Slice(), EnvVars: []string{"APP_INTERVALS"}},
2597 },
2598 Action: func(ctx *Context) error {
2599 if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) {
2600 t.Errorf("main name not set from env")
2601 }
2602 if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) {
2603 t.Errorf("short name not set from env")
2604 }
2605 return nil
2606 },
2607 }).Run([]string{"run"})
2608 }
2609
2610 func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) {
2611 defer resetEnv(os.Environ())
2612 os.Clearenv()
2613 _ = os.Setenv("APP_INTERVALS", "20,30,17179869184")
2614
2615 _ = (&App{
2616 Flags: []Flag{
2617 &Int64SliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewInt64Slice(), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}},
2618 },
2619 Action: func(ctx *Context) error {
2620 if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) {
2621 t.Errorf("main name not set from env")
2622 }
2623 if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) {
2624 t.Errorf("short name not set from env")
2625 }
2626 return nil
2627 },
2628 }).Run([]string{"run"})
2629 }
2630
2631 func TestParseMultiFloat64(t *testing.T) {
2632 _ = (&App{
2633 Flags: []Flag{
2634 &Float64Flag{Name: "serve", Aliases: []string{"s"}},
2635 },
2636 Action: func(ctx *Context) error {
2637 if ctx.Float64("serve") != 10.2 {
2638 t.Errorf("main name not set")
2639 }
2640 if ctx.Float64("s") != 10.2 {
2641 t.Errorf("short name not set")
2642 }
2643 return nil
2644 },
2645 }).Run([]string{"run", "-s", "10.2"})
2646 }
2647
2648 func TestParseDestinationFloat64(t *testing.T) {
2649 var dest float64
2650 _ = (&App{
2651 Flags: []Flag{
2652 &Float64Flag{
2653 Name: "dest",
2654 Destination: &dest,
2655 },
2656 },
2657 Action: func(ctx *Context) error {
2658 if dest != 10.2 {
2659 t.Errorf("expected destination Float64 10.2")
2660 }
2661 return nil
2662 },
2663 }).Run([]string{"run", "--dest", "10.2"})
2664 }
2665
2666 func TestParseMultiFloat64FromEnv(t *testing.T) {
2667 defer resetEnv(os.Environ())
2668 os.Clearenv()
2669 _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
2670 _ = (&App{
2671 Flags: []Flag{
2672 &Float64Flag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"APP_TIMEOUT_SECONDS"}},
2673 },
2674 Action: func(ctx *Context) error {
2675 if ctx.Float64("timeout") != 15.5 {
2676 t.Errorf("main name not set")
2677 }
2678 if ctx.Float64("t") != 15.5 {
2679 t.Errorf("short name not set")
2680 }
2681 return nil
2682 },
2683 }).Run([]string{"run"})
2684 }
2685
2686 func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
2687 defer resetEnv(os.Environ())
2688 os.Clearenv()
2689 _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
2690 _ = (&App{
2691 Flags: []Flag{
2692 &Float64Flag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"COMPAT_TIMEOUT_SECONDS", "APP_TIMEOUT_SECONDS"}},
2693 },
2694 Action: func(ctx *Context) error {
2695 if ctx.Float64("timeout") != 15.5 {
2696 t.Errorf("main name not set")
2697 }
2698 if ctx.Float64("t") != 15.5 {
2699 t.Errorf("short name not set")
2700 }
2701 return nil
2702 },
2703 }).Run([]string{"run"})
2704 }
2705
2706 func TestParseMultiFloat64SliceFromEnv(t *testing.T) {
2707 defer resetEnv(os.Environ())
2708 os.Clearenv()
2709 _ = os.Setenv("APP_INTERVALS", "0.1,-10.5")
2710
2711 _ = (&App{
2712 Flags: []Flag{
2713 &Float64SliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewFloat64Slice(), EnvVars: []string{"APP_INTERVALS"}},
2714 },
2715 Action: func(ctx *Context) error {
2716 if !reflect.DeepEqual(ctx.Float64Slice("intervals"), []float64{0.1, -10.5}) {
2717 t.Errorf("main name not set from env")
2718 }
2719 if !reflect.DeepEqual(ctx.Float64Slice("i"), []float64{0.1, -10.5}) {
2720 t.Errorf("short name not set from env")
2721 }
2722 return nil
2723 },
2724 }).Run([]string{"run"})
2725 }
2726
2727 func TestParseMultiFloat64SliceFromEnvCascade(t *testing.T) {
2728 defer resetEnv(os.Environ())
2729 os.Clearenv()
2730 _ = os.Setenv("APP_INTERVALS", "0.1234,-10.5")
2731
2732 _ = (&App{
2733 Flags: []Flag{
2734 &Float64SliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewFloat64Slice(), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}},
2735 },
2736 Action: func(ctx *Context) error {
2737 if !reflect.DeepEqual(ctx.Float64Slice("intervals"), []float64{0.1234, -10.5}) {
2738 t.Errorf("main name not set from env")
2739 }
2740 if !reflect.DeepEqual(ctx.Float64Slice("i"), []float64{0.1234, -10.5}) {
2741 t.Errorf("short name not set from env")
2742 }
2743 return nil
2744 },
2745 }).Run([]string{"run"})
2746 }
2747
2748 func TestParseMultiBool(t *testing.T) {
2749 _ = (&App{
2750 Flags: []Flag{
2751 &BoolFlag{Name: "serve", Aliases: []string{"s"}},
2752 },
2753 Action: func(ctx *Context) error {
2754 if ctx.Bool("serve") != true {
2755 t.Errorf("main name not set")
2756 }
2757 if ctx.Bool("s") != true {
2758 t.Errorf("short name not set")
2759 }
2760 return nil
2761 },
2762 }).Run([]string{"run", "--serve"})
2763 }
2764
2765 func TestParseBoolShortOptionHandle(t *testing.T) {
2766 _ = (&App{
2767 Commands: []*Command{
2768 {
2769 Name: "foobar",
2770 UseShortOptionHandling: true,
2771 Action: func(ctx *Context) error {
2772 if ctx.Bool("serve") != true {
2773 t.Errorf("main name not set")
2774 }
2775 if ctx.Bool("option") != true {
2776 t.Errorf("short name not set")
2777 }
2778 return nil
2779 },
2780 Flags: []Flag{
2781 &BoolFlag{Name: "serve", Aliases: []string{"s"}},
2782 &BoolFlag{Name: "option", Aliases: []string{"o"}},
2783 },
2784 },
2785 },
2786 }).Run([]string{"run", "foobar", "-so"})
2787 }
2788
2789 func TestParseDestinationBool(t *testing.T) {
2790 var dest bool
2791 _ = (&App{
2792 Flags: []Flag{
2793 &BoolFlag{
2794 Name: "dest",
2795 Destination: &dest,
2796 },
2797 },
2798 Action: func(ctx *Context) error {
2799 if dest != true {
2800 t.Errorf("expected destination Bool true")
2801 }
2802 return nil
2803 },
2804 }).Run([]string{"run", "--dest"})
2805 }
2806
2807 func TestParseMultiBoolFromEnv(t *testing.T) {
2808 defer resetEnv(os.Environ())
2809 os.Clearenv()
2810 _ = os.Setenv("APP_DEBUG", "1")
2811 _ = (&App{
2812 Flags: []Flag{
2813 &BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"APP_DEBUG"}},
2814 },
2815 Action: func(ctx *Context) error {
2816 if ctx.Bool("debug") != true {
2817 t.Errorf("main name not set from env")
2818 }
2819 if ctx.Bool("d") != true {
2820 t.Errorf("short name not set from env")
2821 }
2822 return nil
2823 },
2824 }).Run([]string{"run"})
2825 }
2826
2827 func TestParseMultiBoolFromEnvCascade(t *testing.T) {
2828 defer resetEnv(os.Environ())
2829 os.Clearenv()
2830 _ = os.Setenv("APP_DEBUG", "1")
2831 _ = (&App{
2832 Flags: []Flag{
2833 &BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"COMPAT_DEBUG", "APP_DEBUG"}},
2834 },
2835 Action: func(ctx *Context) error {
2836 if ctx.Bool("debug") != true {
2837 t.Errorf("main name not set from env")
2838 }
2839 if ctx.Bool("d") != true {
2840 t.Errorf("short name not set from env")
2841 }
2842 return nil
2843 },
2844 }).Run([]string{"run"})
2845 }
2846
2847 func TestParseBoolFromEnv(t *testing.T) {
2848 var boolFlagTests = []struct {
2849 input string
2850 output bool
2851 }{
2852 {"", false},
2853 {"1", true},
2854 {"false", false},
2855 {"true", true},
2856 }
2857
2858 for _, test := range boolFlagTests {
2859 defer resetEnv(os.Environ())
2860 os.Clearenv()
2861 _ = os.Setenv("DEBUG", test.input)
2862 _ = (&App{
2863 Flags: []Flag{
2864 &BoolFlag{Name: "debug", Aliases: []string{"d"}, EnvVars: []string{"DEBUG"}},
2865 },
2866 Action: func(ctx *Context) error {
2867 if ctx.Bool("debug") != test.output {
2868 t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("debug"))
2869 }
2870 if ctx.Bool("d") != test.output {
2871 t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("d"))
2872 }
2873 return nil
2874 },
2875 }).Run([]string{"run"})
2876 }
2877 }
2878
2879 func TestParseMultiBoolT(t *testing.T) {
2880 _ = (&App{
2881 Flags: []Flag{
2882 &BoolFlag{Name: "implode", Aliases: []string{"i"}, Value: true},
2883 },
2884 Action: func(ctx *Context) error {
2885 if ctx.Bool("implode") {
2886 t.Errorf("main name not set")
2887 }
2888 if ctx.Bool("i") {
2889 t.Errorf("short name not set")
2890 }
2891 return nil
2892 },
2893 }).Run([]string{"run", "--implode=false"})
2894 }
2895
2896 type Parser [2]string
2897
2898 func (p *Parser) Set(value string) error {
2899 parts := strings.Split(value, ",")
2900 if len(parts) != 2 {
2901 return fmt.Errorf("invalid format")
2902 }
2903
2904 (*p)[0] = parts[0]
2905 (*p)[1] = parts[1]
2906
2907 return nil
2908 }
2909
2910 func (p *Parser) String() string {
2911 return fmt.Sprintf("%s,%s", p[0], p[1])
2912 }
2913
2914 func (p *Parser) Get() interface{} {
2915 return p
2916 }
2917
2918 func TestParseGeneric(t *testing.T) {
2919 _ = (&App{
2920 Flags: []Flag{
2921 &GenericFlag{Name: "serve", Aliases: []string{"s"}, Value: &Parser{}},
2922 },
2923 Action: func(ctx *Context) error {
2924 if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) {
2925 t.Errorf("main name not set")
2926 }
2927 if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) {
2928 t.Errorf("short name not set")
2929 }
2930 return nil
2931 },
2932 }).Run([]string{"run", "-s", "10,20"})
2933 }
2934
2935 type genericType struct {
2936 s []string
2937 }
2938
2939 func (g *genericType) Set(value string) error {
2940 g.s = strings.Split(value, "-")
2941 return nil
2942 }
2943
2944 func (g *genericType) String() string {
2945 return strings.Join(g.s, "-")
2946 }
2947
2948 func TestParseDestinationGeneric(t *testing.T) {
2949 expectedString := "abc1-123d"
2950 expectedGeneric := &genericType{[]string{"abc1", "123d"}}
2951 dest := &genericType{}
2952
2953 _ = (&App{
2954 Flags: []Flag{
2955 &GenericFlag{
2956 Name: "dest",
2957 Destination: dest,
2958 },
2959 },
2960 Action: func(ctx *Context) error {
2961
2962 if !reflect.DeepEqual(dest, expectedGeneric) {
2963 t.Errorf(
2964 "expected destination generic: %+v, actual: %+v",
2965 expectedGeneric,
2966 dest,
2967 )
2968 }
2969
2970 if dest.String() != expectedString {
2971 t.Errorf(
2972 "expected destination string: %s, actual: %s",
2973 expectedString,
2974 dest.String(),
2975 )
2976 }
2977 return nil
2978 },
2979 }).Run([]string{"run", "--dest", expectedString})
2980 }
2981
2982 func TestParseGenericFromEnv(t *testing.T) {
2983 defer resetEnv(os.Environ())
2984 os.Clearenv()
2985 _ = os.Setenv("APP_SERVE", "20,30")
2986 _ = (&App{
2987 Flags: []Flag{
2988 &GenericFlag{
2989 Name: "serve",
2990 Aliases: []string{"s"},
2991 Value: &Parser{},
2992 EnvVars: []string{"APP_SERVE"},
2993 },
2994 },
2995 Action: func(ctx *Context) error {
2996 if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) {
2997 t.Errorf("main name not set from env")
2998 }
2999 if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) {
3000 t.Errorf("short name not set from env")
3001 }
3002 return nil
3003 },
3004 }).Run([]string{"run"})
3005 }
3006
3007 func TestParseGenericFromEnvCascade(t *testing.T) {
3008 defer resetEnv(os.Environ())
3009 os.Clearenv()
3010 _ = os.Setenv("APP_FOO", "99,2000")
3011 _ = (&App{
3012 Flags: []Flag{
3013 &GenericFlag{
3014 Name: "foos",
3015 Value: &Parser{},
3016 EnvVars: []string{"COMPAT_FOO", "APP_FOO"},
3017 },
3018 },
3019 Action: func(ctx *Context) error {
3020 if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) {
3021 t.Errorf("value not set from env")
3022 }
3023 return nil
3024 },
3025 }).Run([]string{"run"})
3026 }
3027
3028 func TestFlagFromFile(t *testing.T) {
3029 temp, err := os.CreateTemp("", "urfave_cli_test")
3030 if err != nil {
3031 t.Error(err)
3032 return
3033 }
3034
3035 defer resetEnv(os.Environ())
3036 os.Clearenv()
3037 os.Setenv("APP_FOO", "123")
3038
3039 _, _ = io.WriteString(temp, "abc")
3040 _ = temp.Close()
3041 defer func() {
3042 _ = os.Remove(temp.Name())
3043 }()
3044
3045 var filePathTests = []struct {
3046 path string
3047 name []string
3048 expected string
3049 }{
3050 {"file-does-not-exist", []string{"APP_BAR"}, ""},
3051 {"file-does-not-exist", []string{"APP_FOO"}, "123"},
3052 {"file-does-not-exist", []string{"APP_FOO", "APP_BAR"}, "123"},
3053 {temp.Name(), []string{"APP_FOO"}, "123"},
3054 {temp.Name(), []string{"APP_BAR"}, "abc"},
3055 }
3056
3057 for _, filePathTest := range filePathTests {
3058 got, _, _ := flagFromEnvOrFile(filePathTest.name, filePathTest.path)
3059 if want := filePathTest.expected; got != want {
3060 t.Errorf("Did not expect %v - Want %v", got, want)
3061 }
3062 }
3063 }
3064
3065 func TestStringSlice_Serialized_Set(t *testing.T) {
3066 sl0 := NewStringSlice("a", "b")
3067 ser0 := sl0.Serialize()
3068
3069 if len(ser0) < len(slPfx) {
3070 t.Fatalf("serialized shorter than expected: %q", ser0)
3071 }
3072
3073 sl1 := NewStringSlice("c", "d")
3074 _ = sl1.Set(ser0)
3075
3076 if sl0.String() != sl1.String() {
3077 t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
3078 }
3079 }
3080
3081 func TestIntSlice_Serialized_Set(t *testing.T) {
3082 sl0 := NewIntSlice(1, 2)
3083 ser0 := sl0.Serialize()
3084
3085 if len(ser0) < len(slPfx) {
3086 t.Fatalf("serialized shorter than expected: %q", ser0)
3087 }
3088
3089 sl1 := NewIntSlice(3, 4)
3090 _ = sl1.Set(ser0)
3091
3092 if sl0.String() != sl1.String() {
3093 t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
3094 }
3095 }
3096
3097 func TestInt64Slice_Serialized_Set(t *testing.T) {
3098 sl0 := NewInt64Slice(int64(1), int64(2))
3099 ser0 := sl0.Serialize()
3100
3101 if len(ser0) < len(slPfx) {
3102 t.Fatalf("serialized shorter than expected: %q", ser0)
3103 }
3104
3105 sl1 := NewInt64Slice(int64(3), int64(4))
3106 _ = sl1.Set(ser0)
3107
3108 if sl0.String() != sl1.String() {
3109 t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
3110 }
3111 }
3112
3113 func TestUintSlice_Serialized_Set(t *testing.T) {
3114 sl0 := NewUintSlice(1, 2)
3115 ser0 := sl0.Serialize()
3116
3117 if len(ser0) < len(slPfx) {
3118 t.Fatalf("serialized shorter than expected: %q", ser0)
3119 }
3120
3121 sl1 := NewUintSlice(3, 4)
3122 _ = sl1.Set(ser0)
3123
3124 if sl0.String() != sl1.String() {
3125 t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
3126 }
3127 }
3128
3129 func TestUint64Slice_Serialized_Set(t *testing.T) {
3130 sl0 := NewUint64Slice(1, 2)
3131 ser0 := sl0.Serialize()
3132
3133 if len(ser0) < len(slPfx) {
3134 t.Fatalf("serialized shorter than expected: %q", ser0)
3135 }
3136
3137 sl1 := NewUint64Slice(3, 4)
3138 _ = sl1.Set(ser0)
3139
3140 if sl0.String() != sl1.String() {
3141 t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
3142 }
3143 }
3144
3145 func TestTimestamp_set(t *testing.T) {
3146 ts := Timestamp{
3147 timestamp: nil,
3148 hasBeenSet: false,
3149 layout: "Jan 2, 2006 at 3:04pm (MST)",
3150 }
3151
3152 time1 := "Feb 3, 2013 at 7:54pm (PST)"
3153 if err := ts.Set(time1); err != nil {
3154 t.Fatalf("Failed to parse time %s with layout %s", time1, ts.layout)
3155 }
3156 if ts.hasBeenSet == false {
3157 t.Fatalf("hasBeenSet is not true after setting a time")
3158 }
3159
3160 ts.hasBeenSet = false
3161 ts.SetLayout(time.RFC3339)
3162 time2 := "2006-01-02T15:04:05Z"
3163 if err := ts.Set(time2); err != nil {
3164 t.Fatalf("Failed to parse time %s with layout %s", time2, ts.layout)
3165 }
3166 if ts.hasBeenSet == false {
3167 t.Fatalf("hasBeenSet is not true after setting a time")
3168 }
3169 }
3170
3171 func TestTimestampFlagApply(t *testing.T) {
3172 expectedResult, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
3173 fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: time.RFC3339}
3174 set := flag.NewFlagSet("test", 0)
3175 if err := fl.Apply(set); err != nil {
3176 t.Error(err)
3177 }
3178
3179 err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
3180 expect(t, err, nil)
3181 expect(t, *fl.Value.timestamp, expectedResult)
3182 }
3183
3184 func TestTimestampFlagApplyValue(t *testing.T) {
3185 expectedResult, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
3186 fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: time.RFC3339, Value: NewTimestamp(expectedResult)}
3187 set := flag.NewFlagSet("test", 0)
3188 if err := fl.Apply(set); err != nil {
3189 t.Error(err)
3190 }
3191
3192 err := set.Parse([]string{""})
3193 expect(t, err, nil)
3194 expect(t, *fl.Value.timestamp, expectedResult)
3195 }
3196
3197 func TestTimestampFlagApply_Fail_Parse_Wrong_Layout(t *testing.T) {
3198 fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: "randomlayout"}
3199 set := flag.NewFlagSet("test", 0)
3200 set.SetOutput(io.Discard)
3201 if err := fl.Apply(set); err != nil {
3202 t.Error(err)
3203 }
3204
3205 err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
3206 expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"randomlayout\": cannot parse \"2006-01-02T15:04:05Z\" as \"randomlayout\""))
3207 }
3208
3209 func TestTimestampFlagApply_Fail_Parse_Wrong_Time(t *testing.T) {
3210 fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: "Jan 2, 2006 at 3:04pm (MST)"}
3211 set := flag.NewFlagSet("test", 0)
3212 set.SetOutput(io.Discard)
3213 if err := fl.Apply(set); err != nil {
3214 t.Error(err)
3215 }
3216
3217 err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
3218 expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"Jan 2, 2006 at 3:04pm (MST)\": cannot parse \"2006-01-02T15:04:05Z\" as \"Jan\""))
3219 }
3220
3221 func TestTimestampFlagApply_Timezoned(t *testing.T) {
3222 pdt := time.FixedZone("PDT", -7*60*60)
3223 expectedResult, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
3224 fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: time.ANSIC, Timezone: pdt}
3225 set := flag.NewFlagSet("test", 0)
3226 if err := fl.Apply(set); err != nil {
3227 t.Error(err)
3228 }
3229
3230 err := set.Parse([]string{"--time", "Mon Jan 2 08:04:05 2006"})
3231 expect(t, err, nil)
3232 expect(t, *fl.Value.timestamp, expectedResult.In(pdt))
3233 }
3234
3235 func TestTimestampFlagValueFromContext(t *testing.T) {
3236 set := flag.NewFlagSet("test", 0)
3237 now := time.Now()
3238 set.Var(NewTimestamp(now), "myflag", "doc")
3239 ctx := NewContext(nil, set, nil)
3240 f := &TimestampFlag{Name: "myflag"}
3241 expect(t, f.Get(ctx), &now)
3242 }
3243
3244 type flagDefaultTestCase struct {
3245 name string
3246 flag Flag
3247 toParse []string
3248 expect string
3249 }
3250
3251 func TestFlagDefaultValue(t *testing.T) {
3252 cases := []*flagDefaultTestCase{
3253 {
3254 name: "stringSlice",
3255 flag: &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")},
3256 toParse: []string{"--flag", "parsed"},
3257 expect: `--flag value [ --flag value ] (default: "default1", "default2")`,
3258 },
3259 {
3260 name: "float64Slice",
3261 flag: &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)},
3262 toParse: []string{"--flag", "13.3"},
3263 expect: `--flag value [ --flag value ] (default: 1.1, 2.2)`,
3264 },
3265 {
3266 name: "int64Slice",
3267 flag: &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)},
3268 toParse: []string{"--flag", "13"},
3269 expect: `--flag value [ --flag value ] (default: 1, 2)`,
3270 },
3271 {
3272 name: "intSlice",
3273 flag: &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)},
3274 toParse: []string{"--flag", "13"},
3275 expect: `--flag value [ --flag value ] (default: 1, 2)`,
3276 },
3277 {
3278 name: "uint64Slice",
3279 flag: &Uint64SliceFlag{Name: "flag", Value: NewUint64Slice(1, 2)},
3280 toParse: []string{"--flag", "13"},
3281 expect: `--flag value [ --flag value ] (default: 1, 2)`,
3282 },
3283 {
3284 name: "uintSlice",
3285 flag: &UintSliceFlag{Name: "flag", Value: NewUintSlice(1, 2)},
3286 toParse: []string{"--flag", "13"},
3287 expect: `--flag value [ --flag value ] (default: 1, 2)`,
3288 },
3289 {
3290 name: "string",
3291 flag: &StringFlag{Name: "flag", Value: "default"},
3292 toParse: []string{"--flag", "parsed"},
3293 expect: `--flag value (default: "default")`,
3294 },
3295 {
3296 name: "bool",
3297 flag: &BoolFlag{Name: "flag", Value: true},
3298 toParse: []string{"--flag", "false"},
3299 expect: `--flag (default: true)`,
3300 },
3301 {
3302 name: "uint64",
3303 flag: &Uint64Flag{Name: "flag", Value: 1},
3304 toParse: []string{"--flag", "13"},
3305 expect: `--flag value (default: 1)`,
3306 },
3307 }
3308 for i, v := range cases {
3309 set := flag.NewFlagSet("test", 0)
3310 set.SetOutput(io.Discard)
3311 _ = v.flag.Apply(set)
3312 if err := set.Parse(v.toParse); err != nil {
3313 t.Error(err)
3314 }
3315 if got := v.flag.String(); got != v.expect {
3316 t.Errorf("TestFlagDefaultValue %d %s\nexpect:%s\ngot:%s", i, v.name, v.expect, got)
3317 }
3318 }
3319 }
3320
3321 type flagDefaultTestCaseWithEnv struct {
3322 name string
3323 flag Flag
3324 toParse []string
3325 expect string
3326 environ map[string]string
3327 }
3328
3329 func TestFlagDefaultValueWithEnv(t *testing.T) {
3330 defer resetEnv(os.Environ())
3331 os.Clearenv()
3332
3333 ts, err := time.Parse(time.RFC3339, "2005-01-02T15:04:05Z")
3334 if err != nil {
3335 t.Fatal(err)
3336 }
3337 cases := []*flagDefaultTestCaseWithEnv{
3338 {
3339 name: "stringSlice",
3340 flag: &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2"), EnvVars: []string{"ssflag"}},
3341 toParse: []string{"--flag", "parsed"},
3342 expect: `--flag value [ --flag value ] (default: "default1", "default2")` + withEnvHint([]string{"ssflag"}, ""),
3343 environ: map[string]string{
3344 "ssflag": "some-other-env_value",
3345 },
3346 },
3347 {
3348 name: "float64Slice",
3349 flag: &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2), EnvVars: []string{"fsflag"}},
3350 toParse: []string{"--flag", "13.3"},
3351 expect: `--flag value [ --flag value ] (default: 1.1, 2.2)` + withEnvHint([]string{"fsflag"}, ""),
3352 environ: map[string]string{
3353 "fsflag": "20304.222",
3354 },
3355 },
3356 {
3357 name: "int64Slice",
3358 flag: &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2), EnvVars: []string{"isflag"}},
3359 toParse: []string{"--flag", "13"},
3360 expect: `--flag value [ --flag value ] (default: 1, 2)` + withEnvHint([]string{"isflag"}, ""),
3361 environ: map[string]string{
3362 "isflag": "101",
3363 },
3364 },
3365 {
3366 name: "intSlice",
3367 flag: &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2), EnvVars: []string{"isflag"}},
3368 toParse: []string{"--flag", "13"},
3369 expect: `--flag value [ --flag value ] (default: 1, 2)` + withEnvHint([]string{"isflag"}, ""),
3370 environ: map[string]string{
3371 "isflag": "101",
3372 },
3373 },
3374 {
3375 name: "uint64Slice",
3376 flag: &Uint64SliceFlag{Name: "flag", Value: NewUint64Slice(1, 2), EnvVars: []string{"uisflag"}},
3377 toParse: []string{"--flag", "13"},
3378 expect: `--flag value [ --flag value ] (default: 1, 2)` + withEnvHint([]string{"uisflag"}, ""),
3379 environ: map[string]string{
3380 "uisflag": "3",
3381 },
3382 },
3383 {
3384 name: "uintSlice",
3385 flag: &UintSliceFlag{Name: "flag", Value: NewUintSlice(1, 2), EnvVars: []string{"uisflag"}},
3386 toParse: []string{"--flag", "13"},
3387 expect: `--flag value [ --flag value ] (default: 1, 2)` + withEnvHint([]string{"uisflag"}, ""),
3388 environ: map[string]string{
3389 "uisflag": "3",
3390 },
3391 },
3392 {
3393 name: "string",
3394 flag: &StringFlag{Name: "flag", Value: "default", EnvVars: []string{"uflag"}},
3395 toParse: []string{"--flag", "parsed"},
3396 expect: `--flag value (default: "default")` + withEnvHint([]string{"uflag"}, ""),
3397 environ: map[string]string{
3398 "uflag": "some-other-string",
3399 },
3400 },
3401 {
3402 name: "bool",
3403 flag: &BoolFlag{Name: "flag", Value: true, EnvVars: []string{"uflag"}},
3404 toParse: []string{"--flag", "false"},
3405 expect: `--flag (default: true)` + withEnvHint([]string{"uflag"}, ""),
3406 environ: map[string]string{
3407 "uflag": "false",
3408 },
3409 },
3410 {
3411 name: "uint64",
3412 flag: &Uint64Flag{Name: "flag", Value: 1, EnvVars: []string{"uflag"}},
3413 toParse: []string{"--flag", "13"},
3414 expect: `--flag value (default: 1)` + withEnvHint([]string{"uflag"}, ""),
3415 environ: map[string]string{
3416 "uflag": "10",
3417 },
3418 },
3419 {
3420 name: "uint",
3421 flag: &UintFlag{Name: "flag", Value: 1, EnvVars: []string{"uflag"}},
3422 toParse: []string{"--flag", "13"},
3423 expect: `--flag value (default: 1)` + withEnvHint([]string{"uflag"}, ""),
3424 environ: map[string]string{
3425 "uflag": "10",
3426 },
3427 },
3428 {
3429 name: "int64",
3430 flag: &Int64Flag{Name: "flag", Value: 1, EnvVars: []string{"uflag"}},
3431 toParse: []string{"--flag", "13"},
3432 expect: `--flag value (default: 1)` + withEnvHint([]string{"uflag"}, ""),
3433 environ: map[string]string{
3434 "uflag": "10",
3435 },
3436 },
3437 {
3438 name: "int",
3439 flag: &IntFlag{Name: "flag", Value: 1, EnvVars: []string{"uflag"}},
3440 toParse: []string{"--flag", "13"},
3441 expect: `--flag value (default: 1)` + withEnvHint([]string{"uflag"}, ""),
3442 environ: map[string]string{
3443 "uflag": "10",
3444 },
3445 },
3446 {
3447 name: "duration",
3448 flag: &DurationFlag{Name: "flag", Value: time.Second, EnvVars: []string{"uflag"}},
3449 toParse: []string{"--flag", "2m"},
3450 expect: `--flag value (default: 1s)` + withEnvHint([]string{"uflag"}, ""),
3451 environ: map[string]string{
3452 "uflag": "2h4m10s",
3453 },
3454 },
3455 {
3456 name: "path",
3457 flag: &PathFlag{Name: "flag", Value: "/tmp/foo", EnvVars: []string{"uflag"}},
3458 toParse: []string{"--flag", "/bar/ddfr"},
3459 expect: `--flag value (default: "/tmp/foo")` + withEnvHint([]string{"uflag"}, ""),
3460 environ: map[string]string{
3461 "uflag": "/bar/t/err",
3462 },
3463 },
3464 {
3465 name: "timestamp",
3466 flag: &TimestampFlag{Name: "flag", Value: NewTimestamp(ts), Layout: time.RFC3339, EnvVars: []string{"tflag"}},
3467 toParse: []string{"--flag", "2006-11-02T15:04:05Z"},
3468 expect: `--flag value (default: 2005-01-02 15:04:05 +0000 UTC)` + withEnvHint([]string{"tflag"}, ""),
3469 environ: map[string]string{
3470 "tflag": "2010-01-02T15:04:05Z",
3471 },
3472 },
3473 {
3474 name: "generic",
3475 flag: &GenericFlag{Name: "flag", Value: &Parser{"11", "12"}, EnvVars: []string{"gflag"}},
3476 toParse: []string{"--flag", "15,16"},
3477 expect: `--flag value (default: 11,12)` + withEnvHint([]string{"gflag"}, ""),
3478 environ: map[string]string{
3479 "gflag": "13,14",
3480 },
3481 },
3482 }
3483 for i, v := range cases {
3484 for key, val := range v.environ {
3485 os.Setenv(key, val)
3486 }
3487 set := flag.NewFlagSet("test", 0)
3488 set.SetOutput(io.Discard)
3489 if err := v.flag.Apply(set); err != nil {
3490 t.Fatal(err)
3491 }
3492 if err := set.Parse(v.toParse); err != nil {
3493 t.Error(err)
3494 }
3495 if got := v.flag.String(); got != v.expect {
3496 t.Errorf("TestFlagDefaultValue %d %s\nexpect:%s\ngot:%s", i, v.name, v.expect, got)
3497 }
3498 }
3499 }
3500
3501 type flagValueTestCase struct {
3502 name string
3503 flag Flag
3504 toParse []string
3505 expect string
3506 }
3507
3508 func TestFlagValue(t *testing.T) {
3509 cases := []*flagValueTestCase{
3510 &flagValueTestCase{
3511 name: "stringSlice",
3512 flag: &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")},
3513 toParse: []string{"--flag", "parsed,parsed2", "--flag", "parsed3,parsed4"},
3514 expect: `[parsed parsed2 parsed3 parsed4]`,
3515 },
3516 &flagValueTestCase{
3517 name: "float64Slice",
3518 flag: &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)},
3519 toParse: []string{"--flag", "13.3,14.4", "--flag", "15.5,16.6"},
3520 expect: `[]float64{13.3, 14.4, 15.5, 16.6}`,
3521 },
3522 &flagValueTestCase{
3523 name: "int64Slice",
3524 flag: &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)},
3525 toParse: []string{"--flag", "13,14", "--flag", "15,16"},
3526 expect: `[]int64{13, 14, 15, 16}`,
3527 },
3528 &flagValueTestCase{
3529 name: "intSlice",
3530 flag: &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)},
3531 toParse: []string{"--flag", "13,14", "--flag", "15,16"},
3532 expect: `[]int{13, 14, 15, 16}`,
3533 },
3534 &flagValueTestCase{
3535 name: "uint64Slice",
3536 flag: &Uint64SliceFlag{Name: "flag", Value: NewUint64Slice(1, 2)},
3537 toParse: []string{"--flag", "13,14", "--flag", "15,16"},
3538 expect: `[]uint64{13, 14, 15, 16}`,
3539 },
3540 &flagValueTestCase{
3541 name: "uintSlice",
3542 flag: &UintSliceFlag{Name: "flag", Value: NewUintSlice(1, 2)},
3543 toParse: []string{"--flag", "13,14", "--flag", "15,16"},
3544 expect: `[]uint{13, 14, 15, 16}`,
3545 },
3546 }
3547 for i, v := range cases {
3548 set := flag.NewFlagSet("test", 0)
3549 set.SetOutput(io.Discard)
3550 _ = v.flag.Apply(set)
3551 if err := set.Parse(v.toParse); err != nil {
3552 t.Error(err)
3553 }
3554 f := set.Lookup("flag")
3555 if got := f.Value.String(); got != v.expect {
3556 t.Errorf("TestFlagValue %d-%s\nexpect:%s\ngot:%s", i, v.name, v.expect, got)
3557 }
3558 }
3559 }
3560
3561 func TestTimestampFlagApply_WithDestination(t *testing.T) {
3562 var destination Timestamp
3563 expectedResult, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
3564 fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: time.RFC3339, Destination: &destination}
3565 set := flag.NewFlagSet("test", 0)
3566 if err := fl.Apply(set); err != nil {
3567 t.Error(err)
3568 }
3569
3570 err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
3571 expect(t, err, nil)
3572 expect(t, *fl.Destination.timestamp, expectedResult)
3573 }
3574
3575 func TestTimestampFlagApply_EnvWithDestination(t *testing.T) {
3576 var tsValue Timestamp
3577
3578 app := NewApp()
3579 app.Flags = []Flag{
3580 &TimestampFlag{
3581 Name: "some-timestamp-flag",
3582 EnvVars: []string{"SOME_TIMESTAMP_FLAG"},
3583 Layout: time.RFC3339,
3584 Destination: &tsValue,
3585 Required: true,
3586 },
3587 }
3588
3589 t.Setenv("SOME_TIMESTAMP_FLAG", "2021-03-02T06:20:00Z")
3590
3591 err := app.Run([]string{})
3592
3593 expect(t, err, nil)
3594 expect(t, tsValue.Value().Format(time.RFC3339), "2021-03-02T06:20:00Z")
3595 }
3596
3597
3598
3599 func TestSliceShortOptionHandle(t *testing.T) {
3600 wasCalled := false
3601 err := (&App{
3602 Commands: []*Command{
3603 {
3604 Name: "foobar",
3605 UseShortOptionHandling: true,
3606 Action: func(ctx *Context) error {
3607 wasCalled = true
3608 if ctx.Bool("i") != true {
3609 t.Error("bool i not set")
3610 }
3611 if ctx.Bool("t") != true {
3612 t.Error("bool i not set")
3613 }
3614 ss := ctx.StringSlice("net")
3615 if !reflect.DeepEqual(ss, []string{"foo"}) {
3616 t.Errorf("Got different slice(%v) than expected", ss)
3617 }
3618 return nil
3619 },
3620 Flags: []Flag{
3621 &StringSliceFlag{Name: "net"},
3622 &BoolFlag{Name: "i"},
3623 &BoolFlag{Name: "t"},
3624 },
3625 },
3626 },
3627 }).Run([]string{"run", "foobar", "--net=foo", "-it"})
3628 if err != nil {
3629 t.Fatal(err)
3630 }
3631 if !wasCalled {
3632 t.Fatal("Action callback was never called")
3633 }
3634 }
3635
3636
3637 func TestDefaultSliceFlagSeparator(t *testing.T) {
3638 separator := separatorSpec{}
3639 opts := []string{"opt1", "opt2", "opt3", "opt4"}
3640 ret := separator.flagSplitMultiValues(strings.Join(opts, ","))
3641 if len(ret) != 4 {
3642 t.Fatalf("split slice flag failed, want: 4, but get: %d", len(ret))
3643 }
3644 for idx, r := range ret {
3645 if r != opts[idx] {
3646 t.Fatalf("get %dth failed, wanted: %s, but get: %s", idx, opts[idx], r)
3647 }
3648 }
3649 }
3650
3651 func TestCustomizedSliceFlagSeparator(t *testing.T) {
3652 separator := separatorSpec{
3653 customized: true,
3654 sep: ";",
3655 }
3656 opts := []string{"opt1", "opt2", "opt3,op", "opt4"}
3657 ret := separator.flagSplitMultiValues(strings.Join(opts, ";"))
3658 if len(ret) != 4 {
3659 t.Fatalf("split slice flag failed, want: 4, but get: %d", len(ret))
3660 }
3661 for idx, r := range ret {
3662 if r != opts[idx] {
3663 t.Fatalf("get %dth failed, wanted: %s, but get: %s", idx, opts[idx], r)
3664 }
3665 }
3666 }
3667
3668 func TestFlagSplitMultiValues_Disabled(t *testing.T) {
3669 separator := separatorSpec{
3670 customized: true,
3671 disabled: true,
3672 }
3673
3674 opts := []string{"opt1", "opt2", "opt3,op", "opt4"}
3675 ret := separator.flagSplitMultiValues(strings.Join(opts, defaultSliceFlagSeparator))
3676 if len(ret) != 1 {
3677 t.Fatalf("failed to disable split slice flag, want: 1, but got: %d", len(ret))
3678 }
3679
3680 if ret[0] != strings.Join(opts, defaultSliceFlagSeparator) {
3681 t.Fatalf("failed to disable split slice flag, want: %s, but got: %s", strings.Join(opts, defaultSliceFlagSeparator), ret[0])
3682 }
3683 }
3684
View as plain text