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