1package cli
2
3import (
4	"bytes"
5	"errors"
6	"flag"
7	"fmt"
8	"io"
9	"io/ioutil"
10	"os"
11	"reflect"
12	"strings"
13	"testing"
14)
15
16var (
17	lastExitCode = 0
18	fakeOsExiter = func(rc int) {
19		lastExitCode = rc
20	}
21	fakeErrWriter = &bytes.Buffer{}
22)
23
24func init() {
25	OsExiter = fakeOsExiter
26	ErrWriter = fakeErrWriter
27}
28
29type opCounts struct {
30	Total, BashComplete, OnUsageError, Before, CommandNotFound, Action, After, SubCommand int
31}
32
33func ExampleApp_Run() {
34	// set args for examples sake
35	os.Args = []string{"greet", "--name", "Jeremy"}
36
37	app := NewApp()
38	app.Name = "greet"
39	app.Flags = []Flag{
40		StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
41	}
42	app.Action = func(c *Context) error {
43		fmt.Printf("Hello %v\n", c.String("name"))
44		return nil
45	}
46	app.UsageText = "app [first_arg] [second_arg]"
47	app.Author = "Harrison"
48	app.Email = "harrison@lolwut.com"
49	app.Authors = []Author{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
50	_ = app.Run(os.Args)
51	// Output:
52	// Hello Jeremy
53}
54
55func ExampleApp_Run_subcommand() {
56	// set args for examples sake
57	os.Args = []string{"say", "hi", "english", "--name", "Jeremy"}
58	app := NewApp()
59	app.Name = "say"
60	app.Commands = []Command{
61		{
62			Name:        "hello",
63			Aliases:     []string{"hi"},
64			Usage:       "use it to see a description",
65			Description: "This is how we describe hello the function",
66			Subcommands: []Command{
67				{
68					Name:        "english",
69					Aliases:     []string{"en"},
70					Usage:       "sends a greeting in english",
71					Description: "greets someone in english",
72					Flags: []Flag{
73						StringFlag{
74							Name:  "name",
75							Value: "Bob",
76							Usage: "Name of the person to greet",
77						},
78					},
79					Action: func(c *Context) error {
80						fmt.Println("Hello,", c.String("name"))
81						return nil
82					},
83				},
84			},
85		},
86	}
87
88	_ = app.Run(os.Args)
89	// Output:
90	// Hello, Jeremy
91}
92
93func ExampleApp_Run_appHelp() {
94	// set args for examples sake
95	os.Args = []string{"greet", "help"}
96
97	app := NewApp()
98	app.Name = "greet"
99	app.Version = "0.1.0"
100	app.Description = "This is how we describe greet the app"
101	app.Authors = []Author{
102		{Name: "Harrison", Email: "harrison@lolwut.com"},
103		{Name: "Oliver Allen", Email: "oliver@toyshop.com"},
104	}
105	app.Flags = []Flag{
106		StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
107	}
108	app.Commands = []Command{
109		{
110			Name:        "describeit",
111			Aliases:     []string{"d"},
112			Usage:       "use it to see a description",
113			Description: "This is how we describe describeit the function",
114			Action: func(c *Context) error {
115				fmt.Printf("i like to describe things")
116				return nil
117			},
118		},
119	}
120	_ = app.Run(os.Args)
121	// Output:
122	// NAME:
123	//    greet - A new cli application
124	//
125	// USAGE:
126	//    greet [global options] command [command options] [arguments...]
127	//
128	// VERSION:
129	//    0.1.0
130	//
131	// DESCRIPTION:
132	//    This is how we describe greet the app
133	//
134	// AUTHORS:
135	//    Harrison <harrison@lolwut.com>
136	//    Oliver Allen <oliver@toyshop.com>
137	//
138	// COMMANDS:
139	//    describeit, d  use it to see a description
140	//    help, h        Shows a list of commands or help for one command
141	//
142	// GLOBAL OPTIONS:
143	//    --name value   a name to say (default: "bob")
144	//    --help, -h     show help
145	//    --version, -v  print the version
146}
147
148func ExampleApp_Run_commandHelp() {
149	// set args for examples sake
150	os.Args = []string{"greet", "h", "describeit"}
151
152	app := NewApp()
153	app.Name = "greet"
154	app.Flags = []Flag{
155		StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
156	}
157	app.Commands = []Command{
158		{
159			Name:        "describeit",
160			Aliases:     []string{"d"},
161			Usage:       "use it to see a description",
162			Description: "This is how we describe describeit the function",
163			Action: func(c *Context) error {
164				fmt.Printf("i like to describe things")
165				return nil
166			},
167		},
168	}
169	_ = app.Run(os.Args)
170	// Output:
171	// NAME:
172	//    greet describeit - use it to see a description
173	//
174	// USAGE:
175	//    greet describeit [arguments...]
176	//
177	// DESCRIPTION:
178	//    This is how we describe describeit the function
179}
180
181func ExampleApp_Run_noAction() {
182	app := App{}
183	app.Name = "greet"
184	_ = app.Run([]string{"greet"})
185	// Output:
186	// NAME:
187	//    greet
188	//
189	// USAGE:
190	//     [global options] command [command options] [arguments...]
191	//
192	// COMMANDS:
193	//    help, h  Shows a list of commands or help for one command
194	//
195	// GLOBAL OPTIONS:
196	//    --help, -h  show help
197}
198
199func ExampleApp_Run_subcommandNoAction() {
200	app := App{}
201	app.Name = "greet"
202	app.Commands = []Command{
203		{
204			Name:        "describeit",
205			Aliases:     []string{"d"},
206			Usage:       "use it to see a description",
207			Description: "This is how we describe describeit the function",
208		},
209	}
210	_ = app.Run([]string{"greet", "describeit"})
211	// Output:
212	// NAME:
213	//     describeit - use it to see a description
214	//
215	// USAGE:
216	//     describeit [arguments...]
217	//
218	// DESCRIPTION:
219	//    This is how we describe describeit the function
220
221}
222
223func ExampleApp_Run_bashComplete_withShortFlag() {
224	os.Args = []string{"greet", "-", "--generate-bash-completion"}
225
226	app := NewApp()
227	app.Name = "greet"
228	app.EnableBashCompletion = true
229	app.Flags = []Flag{
230		IntFlag{
231			Name: "other,o",
232		},
233		StringFlag{
234			Name: "xyz,x",
235		},
236	}
237
238	_ = app.Run(os.Args)
239	// Output:
240	// --other
241	// -o
242	// --xyz
243	// -x
244	// --help
245	// -h
246}
247
248func ExampleApp_Run_bashComplete_withLongFlag() {
249	os.Args = []string{"greet", "--s", "--generate-bash-completion"}
250
251	app := NewApp()
252	app.Name = "greet"
253	app.EnableBashCompletion = true
254	app.Flags = []Flag{
255		IntFlag{
256			Name: "other,o",
257		},
258		StringFlag{
259			Name: "xyz,x",
260		},
261		StringFlag{
262			Name: "some-flag,s",
263		},
264		StringFlag{
265			Name: "similar-flag",
266		},
267	}
268
269	_ = app.Run(os.Args)
270	// Output:
271	// --some-flag
272	// --similar-flag
273}
274func ExampleApp_Run_bashComplete_withMultipleLongFlag() {
275	os.Args = []string{"greet", "--st", "--generate-bash-completion"}
276
277	app := NewApp()
278	app.Name = "greet"
279	app.EnableBashCompletion = true
280	app.Flags = []Flag{
281		IntFlag{
282			Name: "int-flag,i",
283		},
284		StringFlag{
285			Name: "string,s",
286		},
287		StringFlag{
288			Name: "string-flag-2",
289		},
290		StringFlag{
291			Name: "similar-flag",
292		},
293		StringFlag{
294			Name: "some-flag",
295		},
296	}
297
298	_ = app.Run(os.Args)
299	// Output:
300	// --string
301	// --string-flag-2
302}
303
304func ExampleApp_Run_bashComplete() {
305	// set args for examples sake
306	os.Args = []string{"greet", "--generate-bash-completion"}
307
308	app := NewApp()
309	app.Name = "greet"
310	app.EnableBashCompletion = true
311	app.Commands = []Command{
312		{
313			Name:        "describeit",
314			Aliases:     []string{"d"},
315			Usage:       "use it to see a description",
316			Description: "This is how we describe describeit the function",
317			Action: func(c *Context) error {
318				fmt.Printf("i like to describe things")
319				return nil
320			},
321		}, {
322			Name:        "next",
323			Usage:       "next example",
324			Description: "more stuff to see when generating bash completion",
325			Action: func(c *Context) error {
326				fmt.Printf("the next example")
327				return nil
328			},
329		},
330	}
331
332	_ = app.Run(os.Args)
333	// Output:
334	// describeit
335	// d
336	// next
337	// help
338	// h
339}
340
341func ExampleApp_Run_zshComplete() {
342	// set args for examples sake
343	os.Args = []string{"greet", "--generate-bash-completion"}
344	_ = os.Setenv("_CLI_ZSH_AUTOCOMPLETE_HACK", "1")
345
346	app := NewApp()
347	app.Name = "greet"
348	app.EnableBashCompletion = true
349	app.Commands = []Command{
350		{
351			Name:        "describeit",
352			Aliases:     []string{"d"},
353			Usage:       "use it to see a description",
354			Description: "This is how we describe describeit the function",
355			Action: func(c *Context) error {
356				fmt.Printf("i like to describe things")
357				return nil
358			},
359		}, {
360			Name:        "next",
361			Usage:       "next example",
362			Description: "more stuff to see when generating bash completion",
363			Action: func(c *Context) error {
364				fmt.Printf("the next example")
365				return nil
366			},
367		},
368	}
369
370	_ = app.Run(os.Args)
371	// Output:
372	// describeit:use it to see a description
373	// d:use it to see a description
374	// next:next example
375	// help:Shows a list of commands or help for one command
376	// h:Shows a list of commands or help for one command
377}
378
379func TestApp_Run(t *testing.T) {
380	s := ""
381
382	app := NewApp()
383	app.Action = func(c *Context) error {
384		s = s + c.Args().First()
385		return nil
386	}
387
388	err := app.Run([]string{"command", "foo"})
389	expect(t, err, nil)
390	err = app.Run([]string{"command", "bar"})
391	expect(t, err, nil)
392	expect(t, s, "foobar")
393}
394
395var commandAppTests = []struct {
396	name     string
397	expected bool
398}{
399	{"foobar", true},
400	{"batbaz", true},
401	{"b", true},
402	{"f", true},
403	{"bat", false},
404	{"nothing", false},
405}
406
407func TestApp_Command(t *testing.T) {
408	app := NewApp()
409	fooCommand := Command{Name: "foobar", Aliases: []string{"f"}}
410	batCommand := Command{Name: "batbaz", Aliases: []string{"b"}}
411	app.Commands = []Command{
412		fooCommand,
413		batCommand,
414	}
415
416	for _, test := range commandAppTests {
417		expect(t, app.Command(test.name) != nil, test.expected)
418	}
419}
420
421func TestApp_Setup_defaultsWriter(t *testing.T) {
422	app := &App{}
423	app.Setup()
424	expect(t, app.Writer, os.Stdout)
425}
426
427func TestApp_CommandWithArgBeforeFlags(t *testing.T) {
428	var parsedOption, firstArg string
429
430	app := NewApp()
431	command := Command{
432		Name: "cmd",
433		Flags: []Flag{
434			StringFlag{Name: "option", Value: "", Usage: "some option"},
435		},
436		Action: func(c *Context) error {
437			parsedOption = c.String("option")
438			firstArg = c.Args().First()
439			return nil
440		},
441	}
442	app.Commands = []Command{command}
443
444	_ = app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"})
445
446	expect(t, parsedOption, "my-option")
447	expect(t, firstArg, "my-arg")
448}
449
450func TestApp_CommandWithArgBeforeBoolFlags(t *testing.T) {
451	var parsedOption, parsedSecondOption, firstArg string
452	var parsedBool, parsedSecondBool bool
453
454	app := NewApp()
455	command := Command{
456		Name: "cmd",
457		Flags: []Flag{
458			StringFlag{Name: "option", Value: "", Usage: "some option"},
459			StringFlag{Name: "secondOption", Value: "", Usage: "another option"},
460			BoolFlag{Name: "boolflag", Usage: "some bool"},
461			BoolFlag{Name: "b", Usage: "another bool"},
462		},
463		Action: func(c *Context) error {
464			parsedOption = c.String("option")
465			parsedSecondOption = c.String("secondOption")
466			parsedBool = c.Bool("boolflag")
467			parsedSecondBool = c.Bool("b")
468			firstArg = c.Args().First()
469			return nil
470		},
471	}
472	app.Commands = []Command{command}
473
474	_ = app.Run([]string{"", "cmd", "my-arg", "--boolflag", "--option", "my-option", "-b", "--secondOption", "fancy-option"})
475
476	expect(t, parsedOption, "my-option")
477	expect(t, parsedSecondOption, "fancy-option")
478	expect(t, parsedBool, true)
479	expect(t, parsedSecondBool, true)
480	expect(t, firstArg, "my-arg")
481}
482
483func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
484	var context *Context
485
486	a := NewApp()
487	a.Commands = []Command{
488		{
489			Name: "foo",
490			Action: func(c *Context) error {
491				context = c
492				return nil
493			},
494			Flags: []Flag{
495				StringFlag{
496					Name:  "lang",
497					Value: "english",
498					Usage: "language for the greeting",
499				},
500			},
501			Before: func(_ *Context) error { return nil },
502		},
503	}
504	_ = a.Run([]string{"", "foo", "--lang", "spanish", "abcd"})
505
506	expect(t, context.Args().Get(0), "abcd")
507	expect(t, context.String("lang"), "spanish")
508}
509
510func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) {
511	a := App{
512		Flags: []Flag{
513			StringFlag{Name: "--foo"},
514		},
515		Writer: bytes.NewBufferString(""),
516	}
517
518	set := flag.NewFlagSet("", flag.ContinueOnError)
519	_ = set.Parse([]string{"", "---foo"})
520	c := &Context{flagSet: set}
521
522	err := a.RunAsSubcommand(c)
523
524	expect(t, err, errors.New("bad flag syntax: ---foo"))
525}
526
527func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
528	var parsedOption string
529	var args []string
530
531	app := NewApp()
532	command := Command{
533		Name: "cmd",
534		Flags: []Flag{
535			StringFlag{Name: "option", Value: "", Usage: "some option"},
536		},
537		Action: func(c *Context) error {
538			parsedOption = c.String("option")
539			args = c.Args()
540			return nil
541		},
542	}
543	app.Commands = []Command{command}
544
545	_ = app.Run([]string{"", "cmd", "my-arg", "--option", "my-option", "--", "--notARealFlag"})
546
547	expect(t, parsedOption, "my-option")
548	expect(t, args[0], "my-arg")
549	expect(t, args[1], "--")
550	expect(t, args[2], "--notARealFlag")
551}
552
553func TestApp_CommandWithDash(t *testing.T) {
554	var args []string
555
556	app := NewApp()
557	command := Command{
558		Name: "cmd",
559		Action: func(c *Context) error {
560			args = c.Args()
561			return nil
562		},
563	}
564	app.Commands = []Command{command}
565
566	_ = app.Run([]string{"", "cmd", "my-arg", "-"})
567
568	expect(t, args[0], "my-arg")
569	expect(t, args[1], "-")
570}
571
572func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
573	var args []string
574
575	app := NewApp()
576	command := Command{
577		Name: "cmd",
578		Action: func(c *Context) error {
579			args = c.Args()
580			return nil
581		},
582	}
583	app.Commands = []Command{command}
584
585	_ = app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"})
586
587	expect(t, args[0], "my-arg")
588	expect(t, args[1], "--")
589	expect(t, args[2], "notAFlagAtAll")
590}
591
592func TestApp_VisibleCommands(t *testing.T) {
593	app := NewApp()
594	app.Commands = []Command{
595		{
596			Name:     "frob",
597			HelpName: "foo frob",
598			Action:   func(_ *Context) error { return nil },
599		},
600		{
601			Name:     "frib",
602			HelpName: "foo frib",
603			Hidden:   true,
604			Action:   func(_ *Context) error { return nil },
605		},
606	}
607
608	app.Setup()
609	expected := []Command{
610		app.Commands[0],
611		app.Commands[2], // help
612	}
613	actual := app.VisibleCommands()
614	expect(t, len(expected), len(actual))
615	for i, actualCommand := range actual {
616		expectedCommand := expected[i]
617
618		if expectedCommand.Action != nil {
619			// comparing func addresses is OK!
620			expect(t, fmt.Sprintf("%p", expectedCommand.Action), fmt.Sprintf("%p", actualCommand.Action))
621		}
622
623		// nil out funcs, as they cannot be compared
624		// (https://github.com/golang/go/issues/8554)
625		expectedCommand.Action = nil
626		actualCommand.Action = nil
627
628		if !reflect.DeepEqual(expectedCommand, actualCommand) {
629			t.Errorf("expected\n%#v\n!=\n%#v", expectedCommand, actualCommand)
630		}
631	}
632}
633
634func TestApp_UseShortOptionHandling(t *testing.T) {
635	var one, two bool
636	var name string
637	expected := "expectedName"
638
639	app := NewApp()
640	app.UseShortOptionHandling = true
641	app.Flags = []Flag{
642		BoolFlag{Name: "one, o"},
643		BoolFlag{Name: "two, t"},
644		StringFlag{Name: "name, n"},
645	}
646	app.Action = func(c *Context) error {
647		one = c.Bool("one")
648		two = c.Bool("two")
649		name = c.String("name")
650		return nil
651	}
652
653	app.Run([]string{"", "-on", expected})
654	expect(t, one, true)
655	expect(t, two, false)
656	expect(t, name, expected)
657}
658
659func TestApp_UseShortOptionHandling_missing_value(t *testing.T) {
660	app := NewApp()
661	app.UseShortOptionHandling = true
662	app.Flags = []Flag{
663		StringFlag{Name: "name, n"},
664	}
665
666	err := app.Run([]string{"", "-n"})
667	expect(t, err, errors.New("flag needs an argument: -n"))
668}
669
670func TestApp_UseShortOptionHandlingCommand(t *testing.T) {
671	var one, two bool
672	var name string
673	expected := "expectedName"
674
675	app := NewApp()
676	app.UseShortOptionHandling = true
677	command := Command{
678		Name: "cmd",
679		Flags: []Flag{
680			BoolFlag{Name: "one, o"},
681			BoolFlag{Name: "two, t"},
682			StringFlag{Name: "name, n"},
683		},
684		Action: func(c *Context) error {
685			one = c.Bool("one")
686			two = c.Bool("two")
687			name = c.String("name")
688			return nil
689		},
690	}
691	app.Commands = []Command{command}
692
693	app.Run([]string{"", "cmd", "-on", expected})
694	expect(t, one, true)
695	expect(t, two, false)
696	expect(t, name, expected)
697}
698
699func TestApp_UseShortOptionHandlingCommand_missing_value(t *testing.T) {
700	app := NewApp()
701	app.UseShortOptionHandling = true
702	command := Command{
703		Name: "cmd",
704		Flags: []Flag{
705			StringFlag{Name: "name, n"},
706		},
707	}
708	app.Commands = []Command{command}
709
710	err := app.Run([]string{"", "cmd", "-n"})
711	expect(t, err, errors.New("flag needs an argument: -n"))
712}
713
714func TestApp_UseShortOptionHandlingSubCommand(t *testing.T) {
715	var one, two bool
716	var name string
717	expected := "expectedName"
718
719	app := NewApp()
720	app.UseShortOptionHandling = true
721	command := Command{
722		Name: "cmd",
723	}
724	subCommand := Command{
725		Name: "sub",
726		Flags: []Flag{
727			BoolFlag{Name: "one, o"},
728			BoolFlag{Name: "two, t"},
729			StringFlag{Name: "name, n"},
730		},
731		Action: func(c *Context) error {
732			one = c.Bool("one")
733			two = c.Bool("two")
734			name = c.String("name")
735			return nil
736		},
737	}
738	command.Subcommands = []Command{subCommand}
739	app.Commands = []Command{command}
740
741	err := app.Run([]string{"", "cmd", "sub", "-on", expected})
742	expect(t, err, nil)
743	expect(t, one, true)
744	expect(t, two, false)
745	expect(t, name, expected)
746}
747
748func TestApp_UseShortOptionHandlingSubCommand_missing_value(t *testing.T) {
749	app := NewApp()
750	app.UseShortOptionHandling = true
751	command := Command{
752		Name: "cmd",
753	}
754	subCommand := Command{
755		Name: "sub",
756		Flags: []Flag{
757			StringFlag{Name: "name, n"},
758		},
759	}
760	command.Subcommands = []Command{subCommand}
761	app.Commands = []Command{command}
762
763	err := app.Run([]string{"", "cmd", "sub", "-n"})
764	expect(t, err, errors.New("flag needs an argument: -n"))
765}
766
767func TestApp_Float64Flag(t *testing.T) {
768	var meters float64
769
770	app := NewApp()
771	app.Flags = []Flag{
772		Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"},
773	}
774	app.Action = func(c *Context) error {
775		meters = c.Float64("height")
776		return nil
777	}
778
779	_ = app.Run([]string{"", "--height", "1.93"})
780	expect(t, meters, 1.93)
781}
782
783func TestApp_ParseSliceFlags(t *testing.T) {
784	var parsedIntSlice []int
785	var parsedStringSlice []string
786
787	app := NewApp()
788	command := Command{
789		Name: "cmd",
790		Flags: []Flag{
791			IntSliceFlag{Name: "p", Value: &IntSlice{}, Usage: "set one or more ip addr"},
792			StringSliceFlag{Name: "ip", Value: &StringSlice{}, Usage: "set one or more ports to open"},
793		},
794		Action: func(c *Context) error {
795			parsedIntSlice = c.IntSlice("p")
796			parsedStringSlice = c.StringSlice("ip")
797			return nil
798		},
799	}
800	app.Commands = []Command{command}
801
802	_ = app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"})
803
804	IntsEquals := func(a, b []int) bool {
805		if len(a) != len(b) {
806			return false
807		}
808		for i, v := range a {
809			if v != b[i] {
810				return false
811			}
812		}
813		return true
814	}
815
816	StrsEquals := func(a, b []string) bool {
817		if len(a) != len(b) {
818			return false
819		}
820		for i, v := range a {
821			if v != b[i] {
822				return false
823			}
824		}
825		return true
826	}
827	var expectedIntSlice = []int{22, 80}
828	var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"}
829
830	if !IntsEquals(parsedIntSlice, expectedIntSlice) {
831		t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice)
832	}
833
834	if !StrsEquals(parsedStringSlice, expectedStringSlice) {
835		t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice)
836	}
837}
838
839func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) {
840	var parsedIntSlice []int
841	var parsedStringSlice []string
842
843	app := NewApp()
844	command := Command{
845		Name: "cmd",
846		Flags: []Flag{
847			IntSliceFlag{Name: "a", Usage: "set numbers"},
848			StringSliceFlag{Name: "str", Usage: "set strings"},
849		},
850		Action: func(c *Context) error {
851			parsedIntSlice = c.IntSlice("a")
852			parsedStringSlice = c.StringSlice("str")
853			return nil
854		},
855	}
856	app.Commands = []Command{command}
857
858	_ = app.Run([]string{"", "cmd", "my-arg", "-a", "2", "-str", "A"})
859
860	var expectedIntSlice = []int{2}
861	var expectedStringSlice = []string{"A"}
862
863	if parsedIntSlice[0] != expectedIntSlice[0] {
864		t.Errorf("%v does not match %v", parsedIntSlice[0], expectedIntSlice[0])
865	}
866
867	if parsedStringSlice[0] != expectedStringSlice[0] {
868		t.Errorf("%v does not match %v", parsedIntSlice[0], expectedIntSlice[0])
869	}
870}
871
872func TestApp_DefaultStdout(t *testing.T) {
873	app := NewApp()
874
875	if app.Writer != os.Stdout {
876		t.Error("Default output writer not set.")
877	}
878}
879
880type mockWriter struct {
881	written []byte
882}
883
884func (fw *mockWriter) Write(p []byte) (n int, err error) {
885	if fw.written == nil {
886		fw.written = p
887	} else {
888		fw.written = append(fw.written, p...)
889	}
890
891	return len(p), nil
892}
893
894func (fw *mockWriter) GetWritten() (b []byte) {
895	return fw.written
896}
897
898func TestApp_SetStdout(t *testing.T) {
899	w := &mockWriter{}
900
901	app := NewApp()
902	app.Name = "test"
903	app.Writer = w
904
905	err := app.Run([]string{"help"})
906
907	if err != nil {
908		t.Fatalf("Run error: %s", err)
909	}
910
911	if len(w.written) == 0 {
912		t.Error("App did not write output to desired writer.")
913	}
914}
915
916func TestApp_BeforeFunc(t *testing.T) {
917	counts := &opCounts{}
918	beforeError := fmt.Errorf("fail")
919	var err error
920
921	app := NewApp()
922
923	app.Before = func(c *Context) error {
924		counts.Total++
925		counts.Before = counts.Total
926		s := c.String("opt")
927		if s == "fail" {
928			return beforeError
929		}
930
931		return nil
932	}
933
934	app.Commands = []Command{
935		{
936			Name: "sub",
937			Action: func(c *Context) error {
938				counts.Total++
939				counts.SubCommand = counts.Total
940				return nil
941			},
942		},
943	}
944
945	app.Flags = []Flag{
946		StringFlag{Name: "opt"},
947	}
948
949	// run with the Before() func succeeding
950	err = app.Run([]string{"command", "--opt", "succeed", "sub"})
951
952	if err != nil {
953		t.Fatalf("Run error: %s", err)
954	}
955
956	if counts.Before != 1 {
957		t.Errorf("Before() not executed when expected")
958	}
959
960	if counts.SubCommand != 2 {
961		t.Errorf("Subcommand not executed when expected")
962	}
963
964	// reset
965	counts = &opCounts{}
966
967	// run with the Before() func failing
968	err = app.Run([]string{"command", "--opt", "fail", "sub"})
969
970	// should be the same error produced by the Before func
971	if err != beforeError {
972		t.Errorf("Run error expected, but not received")
973	}
974
975	if counts.Before != 1 {
976		t.Errorf("Before() not executed when expected")
977	}
978
979	if counts.SubCommand != 0 {
980		t.Errorf("Subcommand executed when NOT expected")
981	}
982
983	// reset
984	counts = &opCounts{}
985
986	afterError := errors.New("fail again")
987	app.After = func(_ *Context) error {
988		return afterError
989	}
990
991	// run with the Before() func failing, wrapped by After()
992	err = app.Run([]string{"command", "--opt", "fail", "sub"})
993
994	// should be the same error produced by the Before func
995	if _, ok := err.(MultiError); !ok {
996		t.Errorf("MultiError expected, but not received")
997	}
998
999	if counts.Before != 1 {
1000		t.Errorf("Before() not executed when expected")
1001	}
1002
1003	if counts.SubCommand != 0 {
1004		t.Errorf("Subcommand executed when NOT expected")
1005	}
1006}
1007
1008func TestApp_AfterFunc(t *testing.T) {
1009	counts := &opCounts{}
1010	afterError := fmt.Errorf("fail")
1011	var err error
1012
1013	app := NewApp()
1014
1015	app.After = func(c *Context) error {
1016		counts.Total++
1017		counts.After = counts.Total
1018		s := c.String("opt")
1019		if s == "fail" {
1020			return afterError
1021		}
1022
1023		return nil
1024	}
1025
1026	app.Commands = []Command{
1027		{
1028			Name: "sub",
1029			Action: func(c *Context) error {
1030				counts.Total++
1031				counts.SubCommand = counts.Total
1032				return nil
1033			},
1034		},
1035	}
1036
1037	app.Flags = []Flag{
1038		StringFlag{Name: "opt"},
1039	}
1040
1041	// run with the After() func succeeding
1042	err = app.Run([]string{"command", "--opt", "succeed", "sub"})
1043
1044	if err != nil {
1045		t.Fatalf("Run error: %s", err)
1046	}
1047
1048	if counts.After != 2 {
1049		t.Errorf("After() not executed when expected")
1050	}
1051
1052	if counts.SubCommand != 1 {
1053		t.Errorf("Subcommand not executed when expected")
1054	}
1055
1056	// reset
1057	counts = &opCounts{}
1058
1059	// run with the Before() func failing
1060	err = app.Run([]string{"command", "--opt", "fail", "sub"})
1061
1062	// should be the same error produced by the Before func
1063	if err != afterError {
1064		t.Errorf("Run error expected, but not received")
1065	}
1066
1067	if counts.After != 2 {
1068		t.Errorf("After() not executed when expected")
1069	}
1070
1071	if counts.SubCommand != 1 {
1072		t.Errorf("Subcommand not executed when expected")
1073	}
1074}
1075
1076func TestAppNoHelpFlag(t *testing.T) {
1077	oldFlag := HelpFlag
1078	defer func() {
1079		HelpFlag = oldFlag
1080	}()
1081
1082	HelpFlag = BoolFlag{}
1083
1084	app := NewApp()
1085	app.Writer = ioutil.Discard
1086	err := app.Run([]string{"test", "-h"})
1087
1088	if err != flag.ErrHelp {
1089		t.Errorf("expected error about missing help flag, but got: %s (%T)", err, err)
1090	}
1091}
1092
1093func TestRequiredFlagAppRunBehavior(t *testing.T) {
1094	tdata := []struct {
1095		testCase        string
1096		appFlags        []Flag
1097		appRunInput     []string
1098		appCommands     []Command
1099		expectedAnError bool
1100	}{
1101		// assertion: empty input, when a required flag is present, errors
1102		{
1103			testCase:        "error_case_empty_input_with_required_flag_on_app",
1104			appRunInput:     []string{"myCLI"},
1105			appFlags:        []Flag{StringFlag{Name: "requiredFlag", Required: true}},
1106			expectedAnError: true,
1107		},
1108		{
1109			testCase:    "error_case_empty_input_with_required_flag_on_command",
1110			appRunInput: []string{"myCLI", "myCommand"},
1111			appCommands: []Command{{
1112				Name:  "myCommand",
1113				Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}},
1114			}},
1115			expectedAnError: true,
1116		},
1117		{
1118			testCase:    "error_case_empty_input_with_required_flag_on_subcommand",
1119			appRunInput: []string{"myCLI", "myCommand", "mySubCommand"},
1120			appCommands: []Command{{
1121				Name: "myCommand",
1122				Subcommands: []Command{{
1123					Name:  "mySubCommand",
1124					Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}},
1125				}},
1126			}},
1127			expectedAnError: true,
1128		},
1129		// assertion: inputing --help, when a required flag is present, does not error
1130		{
1131			testCase:    "valid_case_help_input_with_required_flag_on_app",
1132			appRunInput: []string{"myCLI", "--help"},
1133			appFlags:    []Flag{StringFlag{Name: "requiredFlag", Required: true}},
1134		},
1135		{
1136			testCase:    "valid_case_help_input_with_required_flag_on_command",
1137			appRunInput: []string{"myCLI", "myCommand", "--help"},
1138			appCommands: []Command{{
1139				Name:  "myCommand",
1140				Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}},
1141			}},
1142		},
1143		{
1144			testCase:    "valid_case_help_input_with_required_flag_on_subcommand",
1145			appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--help"},
1146			appCommands: []Command{{
1147				Name: "myCommand",
1148				Subcommands: []Command{{
1149					Name:  "mySubCommand",
1150					Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}},
1151				}},
1152			}},
1153		},
1154		// assertion: giving optional input, when a required flag is present, errors
1155		{
1156			testCase:        "error_case_optional_input_with_required_flag_on_app",
1157			appRunInput:     []string{"myCLI", "--optional", "cats"},
1158			appFlags:        []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}},
1159			expectedAnError: true,
1160		},
1161		{
1162			testCase:    "error_case_optional_input_with_required_flag_on_command",
1163			appRunInput: []string{"myCLI", "myCommand", "--optional", "cats"},
1164			appCommands: []Command{{
1165				Name:  "myCommand",
1166				Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}},
1167			}},
1168			expectedAnError: true,
1169		},
1170		{
1171			testCase:    "error_case_optional_input_with_required_flag_on_subcommand",
1172			appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--optional", "cats"},
1173			appCommands: []Command{{
1174				Name: "myCommand",
1175				Subcommands: []Command{{
1176					Name:  "mySubCommand",
1177					Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}},
1178				}},
1179			}},
1180			expectedAnError: true,
1181		},
1182		// assertion: when a required flag is present, inputting that required flag does not error
1183		{
1184			testCase:    "valid_case_required_flag_input_on_app",
1185			appRunInput: []string{"myCLI", "--requiredFlag", "cats"},
1186			appFlags:    []Flag{StringFlag{Name: "requiredFlag", Required: true}},
1187		},
1188		{
1189			testCase:    "valid_case_required_flag_input_on_command",
1190			appRunInput: []string{"myCLI", "myCommand", "--requiredFlag", "cats"},
1191			appCommands: []Command{{
1192				Name:  "myCommand",
1193				Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}},
1194			}},
1195		},
1196		{
1197			testCase:    "valid_case_required_flag_input_on_subcommand",
1198			appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--requiredFlag", "cats"},
1199			appCommands: []Command{{
1200				Name: "myCommand",
1201				Subcommands: []Command{{
1202					Name:  "mySubCommand",
1203					Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}},
1204				}},
1205			}},
1206		},
1207	}
1208	for _, test := range tdata {
1209		t.Run(test.testCase, func(t *testing.T) {
1210			// setup
1211			app := NewApp()
1212			app.Flags = test.appFlags
1213			app.Commands = test.appCommands
1214
1215			// logic under test
1216			err := app.Run(test.appRunInput)
1217
1218			// assertions
1219			if test.expectedAnError && err == nil {
1220				t.Errorf("expected an error, but there was none")
1221			}
1222			if _, ok := err.(requiredFlagsErr); test.expectedAnError && !ok {
1223				t.Errorf("expected a requiredFlagsErr, but got: %s", err)
1224			}
1225			if !test.expectedAnError && err != nil {
1226				t.Errorf("did not expected an error, but there was one: %s", err)
1227			}
1228		})
1229	}
1230}
1231
1232func TestAppHelpPrinter(t *testing.T) {
1233	oldPrinter := HelpPrinter
1234	defer func() {
1235		HelpPrinter = oldPrinter
1236	}()
1237
1238	var wasCalled = false
1239	HelpPrinter = func(w io.Writer, template string, data interface{}) {
1240		wasCalled = true
1241	}
1242
1243	app := NewApp()
1244	_ = app.Run([]string{"-h"})
1245
1246	if wasCalled == false {
1247		t.Errorf("Help printer expected to be called, but was not")
1248	}
1249}
1250
1251func TestApp_VersionPrinter(t *testing.T) {
1252	oldPrinter := VersionPrinter
1253	defer func() {
1254		VersionPrinter = oldPrinter
1255	}()
1256
1257	var wasCalled = false
1258	VersionPrinter = func(c *Context) {
1259		wasCalled = true
1260	}
1261
1262	app := NewApp()
1263	ctx := NewContext(app, nil, nil)
1264	ShowVersion(ctx)
1265
1266	if wasCalled == false {
1267		t.Errorf("Version printer expected to be called, but was not")
1268	}
1269}
1270
1271func TestApp_CommandNotFound(t *testing.T) {
1272	counts := &opCounts{}
1273	app := NewApp()
1274
1275	app.CommandNotFound = func(c *Context, command string) {
1276		counts.Total++
1277		counts.CommandNotFound = counts.Total
1278	}
1279
1280	app.Commands = []Command{
1281		{
1282			Name: "bar",
1283			Action: func(c *Context) error {
1284				counts.Total++
1285				counts.SubCommand = counts.Total
1286				return nil
1287			},
1288		},
1289	}
1290
1291	_ = app.Run([]string{"command", "foo"})
1292
1293	expect(t, counts.CommandNotFound, 1)
1294	expect(t, counts.SubCommand, 0)
1295	expect(t, counts.Total, 1)
1296}
1297
1298func TestApp_OrderOfOperations(t *testing.T) {
1299	counts := &opCounts{}
1300
1301	resetCounts := func() { counts = &opCounts{} }
1302
1303	app := NewApp()
1304	app.EnableBashCompletion = true
1305	app.BashComplete = func(c *Context) {
1306		counts.Total++
1307		counts.BashComplete = counts.Total
1308	}
1309
1310	app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
1311		counts.Total++
1312		counts.OnUsageError = counts.Total
1313		return errors.New("hay OnUsageError")
1314	}
1315
1316	beforeNoError := func(c *Context) error {
1317		counts.Total++
1318		counts.Before = counts.Total
1319		return nil
1320	}
1321
1322	beforeError := func(c *Context) error {
1323		counts.Total++
1324		counts.Before = counts.Total
1325		return errors.New("hay Before")
1326	}
1327
1328	app.Before = beforeNoError
1329	app.CommandNotFound = func(c *Context, command string) {
1330		counts.Total++
1331		counts.CommandNotFound = counts.Total
1332	}
1333
1334	afterNoError := func(c *Context) error {
1335		counts.Total++
1336		counts.After = counts.Total
1337		return nil
1338	}
1339
1340	afterError := func(c *Context) error {
1341		counts.Total++
1342		counts.After = counts.Total
1343		return errors.New("hay After")
1344	}
1345
1346	app.After = afterNoError
1347	app.Commands = []Command{
1348		{
1349			Name: "bar",
1350			Action: func(c *Context) error {
1351				counts.Total++
1352				counts.SubCommand = counts.Total
1353				return nil
1354			},
1355		},
1356	}
1357
1358	app.Action = func(c *Context) error {
1359		counts.Total++
1360		counts.Action = counts.Total
1361		return nil
1362	}
1363
1364	_ = app.Run([]string{"command", "--nope"})
1365	expect(t, counts.OnUsageError, 1)
1366	expect(t, counts.Total, 1)
1367
1368	resetCounts()
1369
1370	_ = app.Run([]string{"command", "--generate-bash-completion"})
1371	expect(t, counts.BashComplete, 1)
1372	expect(t, counts.Total, 1)
1373
1374	resetCounts()
1375
1376	oldOnUsageError := app.OnUsageError
1377	app.OnUsageError = nil
1378	_ = app.Run([]string{"command", "--nope"})
1379	expect(t, counts.Total, 0)
1380	app.OnUsageError = oldOnUsageError
1381
1382	resetCounts()
1383
1384	_ = app.Run([]string{"command", "foo"})
1385	expect(t, counts.OnUsageError, 0)
1386	expect(t, counts.Before, 1)
1387	expect(t, counts.CommandNotFound, 0)
1388	expect(t, counts.Action, 2)
1389	expect(t, counts.After, 3)
1390	expect(t, counts.Total, 3)
1391
1392	resetCounts()
1393
1394	app.Before = beforeError
1395	_ = app.Run([]string{"command", "bar"})
1396	expect(t, counts.OnUsageError, 0)
1397	expect(t, counts.Before, 1)
1398	expect(t, counts.After, 2)
1399	expect(t, counts.Total, 2)
1400	app.Before = beforeNoError
1401
1402	resetCounts()
1403
1404	app.After = nil
1405	_ = app.Run([]string{"command", "bar"})
1406	expect(t, counts.OnUsageError, 0)
1407	expect(t, counts.Before, 1)
1408	expect(t, counts.SubCommand, 2)
1409	expect(t, counts.Total, 2)
1410	app.After = afterNoError
1411
1412	resetCounts()
1413
1414	app.After = afterError
1415	err := app.Run([]string{"command", "bar"})
1416	if err == nil {
1417		t.Fatalf("expected a non-nil error")
1418	}
1419	expect(t, counts.OnUsageError, 0)
1420	expect(t, counts.Before, 1)
1421	expect(t, counts.SubCommand, 2)
1422	expect(t, counts.After, 3)
1423	expect(t, counts.Total, 3)
1424	app.After = afterNoError
1425
1426	resetCounts()
1427
1428	oldCommands := app.Commands
1429	app.Commands = nil
1430	_ = app.Run([]string{"command"})
1431	expect(t, counts.OnUsageError, 0)
1432	expect(t, counts.Before, 1)
1433	expect(t, counts.Action, 2)
1434	expect(t, counts.After, 3)
1435	expect(t, counts.Total, 3)
1436	app.Commands = oldCommands
1437}
1438
1439func TestApp_Run_CommandWithSubcommandHasHelpTopic(t *testing.T) {
1440	var subcommandHelpTopics = [][]string{
1441		{"command", "foo", "--help"},
1442		{"command", "foo", "-h"},
1443		{"command", "foo", "help"},
1444	}
1445
1446	for _, flagSet := range subcommandHelpTopics {
1447		t.Logf("==> checking with flags %v", flagSet)
1448
1449		app := NewApp()
1450		buf := new(bytes.Buffer)
1451		app.Writer = buf
1452
1453		subCmdBar := Command{
1454			Name:  "bar",
1455			Usage: "does bar things",
1456		}
1457		subCmdBaz := Command{
1458			Name:  "baz",
1459			Usage: "does baz things",
1460		}
1461		cmd := Command{
1462			Name:        "foo",
1463			Description: "descriptive wall of text about how it does foo things",
1464			Subcommands: []Command{subCmdBar, subCmdBaz},
1465			Action:      func(c *Context) error { return nil },
1466		}
1467
1468		app.Commands = []Command{cmd}
1469		err := app.Run(flagSet)
1470
1471		if err != nil {
1472			t.Error(err)
1473		}
1474
1475		output := buf.String()
1476		t.Logf("output: %q\n", buf.Bytes())
1477
1478		if strings.Contains(output, "No help topic for") {
1479			t.Errorf("expect a help topic, got none: \n%q", output)
1480		}
1481
1482		for _, shouldContain := range []string{
1483			cmd.Name, cmd.Description,
1484			subCmdBar.Name, subCmdBar.Usage,
1485			subCmdBaz.Name, subCmdBaz.Usage,
1486		} {
1487			if !strings.Contains(output, shouldContain) {
1488				t.Errorf("want help to contain %q, did not: \n%q", shouldContain, output)
1489			}
1490		}
1491	}
1492}
1493
1494func TestApp_Run_SubcommandFullPath(t *testing.T) {
1495	app := NewApp()
1496	buf := new(bytes.Buffer)
1497	app.Writer = buf
1498	app.Name = "command"
1499	subCmd := Command{
1500		Name:  "bar",
1501		Usage: "does bar things",
1502	}
1503	cmd := Command{
1504		Name:        "foo",
1505		Description: "foo commands",
1506		Subcommands: []Command{subCmd},
1507	}
1508	app.Commands = []Command{cmd}
1509
1510	err := app.Run([]string{"command", "foo", "bar", "--help"})
1511	if err != nil {
1512		t.Error(err)
1513	}
1514
1515	output := buf.String()
1516	if !strings.Contains(output, "command foo bar - does bar things") {
1517		t.Errorf("expected full path to subcommand: %s", output)
1518	}
1519	if !strings.Contains(output, "command foo bar [arguments...]") {
1520		t.Errorf("expected full path to subcommand: %s", output)
1521	}
1522}
1523
1524func TestApp_Run_SubcommandHelpName(t *testing.T) {
1525	app := NewApp()
1526	buf := new(bytes.Buffer)
1527	app.Writer = buf
1528	app.Name = "command"
1529	subCmd := Command{
1530		Name:     "bar",
1531		HelpName: "custom",
1532		Usage:    "does bar things",
1533	}
1534	cmd := Command{
1535		Name:        "foo",
1536		Description: "foo commands",
1537		Subcommands: []Command{subCmd},
1538	}
1539	app.Commands = []Command{cmd}
1540
1541	err := app.Run([]string{"command", "foo", "bar", "--help"})
1542	if err != nil {
1543		t.Error(err)
1544	}
1545
1546	output := buf.String()
1547	if !strings.Contains(output, "custom - does bar things") {
1548		t.Errorf("expected HelpName for subcommand: %s", output)
1549	}
1550	if !strings.Contains(output, "custom [arguments...]") {
1551		t.Errorf("expected HelpName to subcommand: %s", output)
1552	}
1553}
1554
1555func TestApp_Run_CommandHelpName(t *testing.T) {
1556	app := NewApp()
1557	buf := new(bytes.Buffer)
1558	app.Writer = buf
1559	app.Name = "command"
1560	subCmd := Command{
1561		Name:  "bar",
1562		Usage: "does bar things",
1563	}
1564	cmd := Command{
1565		Name:        "foo",
1566		HelpName:    "custom",
1567		Description: "foo commands",
1568		Subcommands: []Command{subCmd},
1569	}
1570	app.Commands = []Command{cmd}
1571
1572	err := app.Run([]string{"command", "foo", "bar", "--help"})
1573	if err != nil {
1574		t.Error(err)
1575	}
1576
1577	output := buf.String()
1578	if !strings.Contains(output, "command foo bar - does bar things") {
1579		t.Errorf("expected full path to subcommand: %s", output)
1580	}
1581	if !strings.Contains(output, "command foo bar [arguments...]") {
1582		t.Errorf("expected full path to subcommand: %s", output)
1583	}
1584}
1585
1586func TestApp_Run_CommandSubcommandHelpName(t *testing.T) {
1587	app := NewApp()
1588	buf := new(bytes.Buffer)
1589	app.Writer = buf
1590	app.Name = "base"
1591	subCmd := Command{
1592		Name:     "bar",
1593		HelpName: "custom",
1594		Usage:    "does bar things",
1595	}
1596	cmd := Command{
1597		Name:        "foo",
1598		Description: "foo commands",
1599		Subcommands: []Command{subCmd},
1600	}
1601	app.Commands = []Command{cmd}
1602
1603	err := app.Run([]string{"command", "foo", "--help"})
1604	if err != nil {
1605		t.Error(err)
1606	}
1607
1608	output := buf.String()
1609	if !strings.Contains(output, "base foo - foo commands") {
1610		t.Errorf("expected full path to subcommand: %s", output)
1611	}
1612	if !strings.Contains(output, "base foo command [command options] [arguments...]") {
1613		t.Errorf("expected full path to subcommand: %s", output)
1614	}
1615}
1616
1617func TestApp_Run_Help(t *testing.T) {
1618	var helpArguments = [][]string{{"boom", "--help"}, {"boom", "-h"}, {"boom", "help"}}
1619
1620	for _, args := range helpArguments {
1621		buf := new(bytes.Buffer)
1622
1623		t.Logf("==> checking with arguments %v", args)
1624
1625		app := NewApp()
1626		app.Name = "boom"
1627		app.Usage = "make an explosive entrance"
1628		app.Writer = buf
1629		app.Action = func(c *Context) error {
1630			buf.WriteString("boom I say!")
1631			return nil
1632		}
1633
1634		err := app.Run(args)
1635		if err != nil {
1636			t.Error(err)
1637		}
1638
1639		output := buf.String()
1640		t.Logf("output: %q\n", buf.Bytes())
1641
1642		if !strings.Contains(output, "boom - make an explosive entrance") {
1643			t.Errorf("want help to contain %q, did not: \n%q", "boom - make an explosive entrance", output)
1644		}
1645	}
1646}
1647
1648func TestApp_Run_Version(t *testing.T) {
1649	var versionArguments = [][]string{{"boom", "--version"}, {"boom", "-v"}}
1650
1651	for _, args := range versionArguments {
1652		buf := new(bytes.Buffer)
1653
1654		t.Logf("==> checking with arguments %v", args)
1655
1656		app := NewApp()
1657		app.Name = "boom"
1658		app.Usage = "make an explosive entrance"
1659		app.Version = "0.1.0"
1660		app.Writer = buf
1661		app.Action = func(c *Context) error {
1662			buf.WriteString("boom I say!")
1663			return nil
1664		}
1665
1666		err := app.Run(args)
1667		if err != nil {
1668			t.Error(err)
1669		}
1670
1671		output := buf.String()
1672		t.Logf("output: %q\n", buf.Bytes())
1673
1674		if !strings.Contains(output, "0.1.0") {
1675			t.Errorf("want version to contain %q, did not: \n%q", "0.1.0", output)
1676		}
1677	}
1678}
1679
1680func TestApp_Run_Categories(t *testing.T) {
1681	app := NewApp()
1682	app.Name = "categories"
1683	app.HideHelp = true
1684	app.Commands = []Command{
1685		{
1686			Name:     "command1",
1687			Category: "1",
1688		},
1689		{
1690			Name:     "command2",
1691			Category: "1",
1692		},
1693		{
1694			Name:     "command3",
1695			Category: "2",
1696		},
1697	}
1698	buf := new(bytes.Buffer)
1699	app.Writer = buf
1700
1701	_ = app.Run([]string{"categories"})
1702
1703	expect := CommandCategories{
1704		&CommandCategory{
1705			Name: "1",
1706			Commands: []Command{
1707				app.Commands[0],
1708				app.Commands[1],
1709			},
1710		},
1711		&CommandCategory{
1712			Name: "2",
1713			Commands: []Command{
1714				app.Commands[2],
1715			},
1716		},
1717	}
1718	if !reflect.DeepEqual(app.Categories(), expect) {
1719		t.Fatalf("expected categories %#v, to equal %#v", app.Categories(), expect)
1720	}
1721
1722	output := buf.String()
1723	t.Logf("output: %q\n", buf.Bytes())
1724
1725	if !strings.Contains(output, "1:\n     command1") {
1726		t.Errorf("want buffer to include category %q, did not: \n%q", "1:\n     command1", output)
1727	}
1728}
1729
1730func TestApp_VisibleCategories(t *testing.T) {
1731	app := NewApp()
1732	app.Name = "visible-categories"
1733	app.HideHelp = true
1734	app.Commands = []Command{
1735		{
1736			Name:     "command1",
1737			Category: "1",
1738			HelpName: "foo command1",
1739			Hidden:   true,
1740		},
1741		{
1742			Name:     "command2",
1743			Category: "2",
1744			HelpName: "foo command2",
1745		},
1746		{
1747			Name:     "command3",
1748			Category: "3",
1749			HelpName: "foo command3",
1750		},
1751	}
1752
1753	expected := []*CommandCategory{
1754		{
1755			Name: "2",
1756			Commands: []Command{
1757				app.Commands[1],
1758			},
1759		},
1760		{
1761			Name: "3",
1762			Commands: []Command{
1763				app.Commands[2],
1764			},
1765		},
1766	}
1767
1768	app.Setup()
1769	expect(t, expected, app.VisibleCategories())
1770
1771	app = NewApp()
1772	app.Name = "visible-categories"
1773	app.HideHelp = true
1774	app.Commands = []Command{
1775		{
1776			Name:     "command1",
1777			Category: "1",
1778			HelpName: "foo command1",
1779			Hidden:   true,
1780		},
1781		{
1782			Name:     "command2",
1783			Category: "2",
1784			HelpName: "foo command2",
1785			Hidden:   true,
1786		},
1787		{
1788			Name:     "command3",
1789			Category: "3",
1790			HelpName: "foo command3",
1791		},
1792	}
1793
1794	expected = []*CommandCategory{
1795		{
1796			Name: "3",
1797			Commands: []Command{
1798				app.Commands[2],
1799			},
1800		},
1801	}
1802
1803	app.Setup()
1804	expect(t, expected, app.VisibleCategories())
1805
1806	app = NewApp()
1807	app.Name = "visible-categories"
1808	app.HideHelp = true
1809	app.Commands = []Command{
1810		{
1811			Name:     "command1",
1812			Category: "1",
1813			HelpName: "foo command1",
1814			Hidden:   true,
1815		},
1816		{
1817			Name:     "command2",
1818			Category: "2",
1819			HelpName: "foo command2",
1820			Hidden:   true,
1821		},
1822		{
1823			Name:     "command3",
1824			Category: "3",
1825			HelpName: "foo command3",
1826			Hidden:   true,
1827		},
1828	}
1829
1830	expected = []*CommandCategory{}
1831
1832	app.Setup()
1833	expect(t, expected, app.VisibleCategories())
1834}
1835
1836func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
1837	app := NewApp()
1838	app.Action = func(c *Context) error { return nil }
1839	app.Before = func(c *Context) error { return fmt.Errorf("before error") }
1840	app.After = func(c *Context) error { return fmt.Errorf("after error") }
1841
1842	err := app.Run([]string{"foo"})
1843	if err == nil {
1844		t.Fatalf("expected to receive error from Run, got none")
1845	}
1846
1847	if !strings.Contains(err.Error(), "before error") {
1848		t.Errorf("expected text of error from Before method, but got none in \"%v\"", err)
1849	}
1850	if !strings.Contains(err.Error(), "after error") {
1851		t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
1852	}
1853}
1854
1855func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
1856	app := NewApp()
1857	app.Commands = []Command{
1858		{
1859			Subcommands: []Command{
1860				{
1861					Name: "sub",
1862				},
1863			},
1864			Name:   "bar",
1865			Before: func(c *Context) error { return fmt.Errorf("before error") },
1866			After:  func(c *Context) error { return fmt.Errorf("after error") },
1867		},
1868	}
1869
1870	err := app.Run([]string{"foo", "bar"})
1871	if err == nil {
1872		t.Fatalf("expected to receive error from Run, got none")
1873	}
1874
1875	if !strings.Contains(err.Error(), "before error") {
1876		t.Errorf("expected text of error from Before method, but got none in \"%v\"", err)
1877	}
1878	if !strings.Contains(err.Error(), "after error") {
1879		t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
1880	}
1881}
1882
1883func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) {
1884	app := NewApp()
1885	app.Flags = []Flag{
1886		IntFlag{Name: "flag"},
1887	}
1888	app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
1889		if isSubcommand {
1890			t.Errorf("Expect no subcommand")
1891		}
1892		if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
1893			t.Errorf("Expect an invalid value error, but got \"%v\"", err)
1894		}
1895		return errors.New("intercepted: " + err.Error())
1896	}
1897	app.Commands = []Command{
1898		{
1899			Name: "bar",
1900		},
1901	}
1902
1903	err := app.Run([]string{"foo", "--flag=wrong"})
1904	if err == nil {
1905		t.Fatalf("expected to receive error from Run, got none")
1906	}
1907
1908	if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
1909		t.Errorf("Expect an intercepted error, but got \"%v\"", err)
1910	}
1911}
1912
1913func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) {
1914	app := NewApp()
1915	app.Flags = []Flag{
1916		IntFlag{Name: "flag"},
1917	}
1918	app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
1919		if isSubcommand {
1920			t.Errorf("Expect subcommand")
1921		}
1922		if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
1923			t.Errorf("Expect an invalid value error, but got \"%v\"", err)
1924		}
1925		return errors.New("intercepted: " + err.Error())
1926	}
1927	app.Commands = []Command{
1928		{
1929			Name: "bar",
1930		},
1931	}
1932
1933	err := app.Run([]string{"foo", "--flag=wrong", "bar"})
1934	if err == nil {
1935		t.Fatalf("expected to receive error from Run, got none")
1936	}
1937
1938	if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
1939		t.Errorf("Expect an intercepted error, but got \"%v\"", err)
1940	}
1941}
1942
1943// A custom flag that conforms to the relevant interfaces, but has none of the
1944// fields that the other flag types do.
1945type customBoolFlag struct {
1946	Nombre string
1947}
1948
1949// Don't use the normal FlagStringer
1950func (c *customBoolFlag) String() string {
1951	return "***" + c.Nombre + "***"
1952}
1953
1954func (c *customBoolFlag) GetName() string {
1955	return c.Nombre
1956}
1957
1958func (c *customBoolFlag) TakesValue() bool {
1959	return false
1960}
1961
1962func (c *customBoolFlag) GetValue() string {
1963	return "value"
1964}
1965
1966func (c *customBoolFlag) GetUsage() string {
1967	return "usage"
1968}
1969
1970func (c *customBoolFlag) Apply(set *flag.FlagSet) {
1971	set.String(c.Nombre, c.Nombre, "")
1972}
1973
1974func TestCustomFlagsUnused(t *testing.T) {
1975	app := NewApp()
1976	app.Flags = []Flag{&customBoolFlag{"custom"}}
1977
1978	err := app.Run([]string{"foo"})
1979	if err != nil {
1980		t.Errorf("Run returned unexpected error: %v", err)
1981	}
1982}
1983
1984func TestCustomFlagsUsed(t *testing.T) {
1985	app := NewApp()
1986	app.Flags = []Flag{&customBoolFlag{"custom"}}
1987
1988	err := app.Run([]string{"foo", "--custom=bar"})
1989	if err != nil {
1990		t.Errorf("Run returned unexpected error: %v", err)
1991	}
1992}
1993
1994func TestCustomHelpVersionFlags(t *testing.T) {
1995	app := NewApp()
1996
1997	// Be sure to reset the global flags
1998	defer func(helpFlag Flag, versionFlag Flag) {
1999		HelpFlag = helpFlag
2000		VersionFlag = versionFlag
2001	}(HelpFlag, VersionFlag)
2002
2003	HelpFlag = &customBoolFlag{"help-custom"}
2004	VersionFlag = &customBoolFlag{"version-custom"}
2005
2006	err := app.Run([]string{"foo", "--help-custom=bar"})
2007	if err != nil {
2008		t.Errorf("Run returned unexpected error: %v", err)
2009	}
2010}
2011
2012func TestHandleAction_WithNonFuncAction(t *testing.T) {
2013	app := NewApp()
2014	app.Action = 42
2015	fs, err := flagSet(app.Name, app.Flags)
2016	if err != nil {
2017		t.Errorf("error creating FlagSet: %s", err)
2018	}
2019	err = HandleAction(app.Action, NewContext(app, fs, nil))
2020
2021	if err == nil {
2022		t.Fatalf("expected to receive error from Run, got none")
2023	}
2024
2025	exitErr, ok := err.(*ExitError)
2026
2027	if !ok {
2028		t.Fatalf("expected to receive a *ExitError")
2029	}
2030
2031	if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type.") {
2032		t.Fatalf("expected an unknown Action error, but got: %v", exitErr.Error())
2033	}
2034
2035	if exitErr.ExitCode() != 2 {
2036		t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode())
2037	}
2038}
2039
2040func TestHandleAction_WithInvalidFuncSignature(t *testing.T) {
2041	app := NewApp()
2042	app.Action = func() string { return "" }
2043	fs, err := flagSet(app.Name, app.Flags)
2044	if err != nil {
2045		t.Errorf("error creating FlagSet: %s", err)
2046	}
2047	err = HandleAction(app.Action, NewContext(app, fs, nil))
2048
2049	if err == nil {
2050		t.Fatalf("expected to receive error from Run, got none")
2051	}
2052
2053	exitErr, ok := err.(*ExitError)
2054
2055	if !ok {
2056		t.Fatalf("expected to receive a *ExitError")
2057	}
2058
2059	if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type") {
2060		t.Fatalf("expected an unknown Action error, but got: %v", exitErr.Error())
2061	}
2062
2063	if exitErr.ExitCode() != 2 {
2064		t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode())
2065	}
2066}
2067
2068func TestHandleAction_WithInvalidFuncReturnSignature(t *testing.T) {
2069	app := NewApp()
2070	app.Action = func(_ *Context) (int, error) { return 0, nil }
2071	fs, err := flagSet(app.Name, app.Flags)
2072	if err != nil {
2073		t.Errorf("error creating FlagSet: %s", err)
2074	}
2075	err = HandleAction(app.Action, NewContext(app, fs, nil))
2076
2077	if err == nil {
2078		t.Fatalf("expected to receive error from Run, got none")
2079	}
2080
2081	exitErr, ok := err.(*ExitError)
2082
2083	if !ok {
2084		t.Fatalf("expected to receive a *ExitError")
2085	}
2086
2087	if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type") {
2088		t.Fatalf("expected an invalid Action signature error, but got: %v", exitErr.Error())
2089	}
2090
2091	if exitErr.ExitCode() != 2 {
2092		t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode())
2093	}
2094}
2095
2096func TestHandleExitCoder_Default(t *testing.T) {
2097	app := NewApp()
2098	fs, err := flagSet(app.Name, app.Flags)
2099	if err != nil {
2100		t.Errorf("error creating FlagSet: %s", err)
2101	}
2102
2103	ctx := NewContext(app, fs, nil)
2104	app.handleExitCoder(ctx, NewExitError("Default Behavior Error", 42))
2105
2106	output := fakeErrWriter.String()
2107	if !strings.Contains(output, "Default") {
2108		t.Fatalf("Expected Default Behavior from Error Handler but got: %s", output)
2109	}
2110}
2111
2112func TestHandleExitCoder_Custom(t *testing.T) {
2113	app := NewApp()
2114	fs, err := flagSet(app.Name, app.Flags)
2115	if err != nil {
2116		t.Errorf("error creating FlagSet: %s", err)
2117	}
2118
2119	app.ExitErrHandler = func(_ *Context, _ error) {
2120		_, _ = fmt.Fprintln(ErrWriter, "I'm a Custom error handler, I print what I want!")
2121	}
2122
2123	ctx := NewContext(app, fs, nil)
2124	app.handleExitCoder(ctx, NewExitError("Default Behavior Error", 42))
2125
2126	output := fakeErrWriter.String()
2127	if !strings.Contains(output, "Custom") {
2128		t.Fatalf("Expected Custom Behavior from Error Handler but got: %s", output)
2129	}
2130}
2131
2132func TestHandleAction_WithUnknownPanic(t *testing.T) {
2133	defer func() { refute(t, recover(), nil) }()
2134
2135	var fn ActionFunc
2136
2137	app := NewApp()
2138	app.Action = func(ctx *Context) error {
2139		_ = fn(ctx)
2140		return nil
2141	}
2142	fs, err := flagSet(app.Name, app.Flags)
2143	if err != nil {
2144		t.Errorf("error creating FlagSet: %s", err)
2145	}
2146	_ = HandleAction(app.Action, NewContext(app, fs, nil))
2147}
2148
2149func TestShellCompletionForIncompleteFlags(t *testing.T) {
2150	app := NewApp()
2151	app.Flags = []Flag{
2152		IntFlag{
2153			Name: "test-completion",
2154		},
2155	}
2156	app.EnableBashCompletion = true
2157	app.BashComplete = func(ctx *Context) {
2158		for _, command := range ctx.App.Commands {
2159			if command.Hidden {
2160				continue
2161			}
2162
2163			for _, name := range command.Names() {
2164				_, _ = fmt.Fprintln(ctx.App.Writer, name)
2165			}
2166		}
2167
2168		for _, f := range ctx.App.Flags {
2169			for _, name := range strings.Split(f.GetName(), ",") {
2170				if name == BashCompletionFlag.GetName() {
2171					continue
2172				}
2173
2174				switch name = strings.TrimSpace(name); len(name) {
2175				case 0:
2176				case 1:
2177					_, _ = fmt.Fprintln(ctx.App.Writer, "-"+name)
2178				default:
2179					_, _ = fmt.Fprintln(ctx.App.Writer, "--"+name)
2180				}
2181			}
2182		}
2183	}
2184	app.Action = func(ctx *Context) error {
2185		return fmt.Errorf("should not get here")
2186	}
2187	err := app.Run([]string{"", "--test-completion", "--" + BashCompletionFlag.GetName()})
2188	if err != nil {
2189		t.Errorf("app should not return an error: %s", err)
2190	}
2191}
2192
2193func TestHandleActionActuallyWorksWithActions(t *testing.T) {
2194	var f ActionFunc
2195	called := false
2196	f = func(c *Context) error {
2197		called = true
2198		return nil
2199	}
2200
2201	err := HandleAction(f, nil)
2202
2203	if err != nil {
2204		t.Errorf("Should not have errored: %v", err)
2205	}
2206
2207	if !called {
2208		t.Errorf("Function was not called")
2209	}
2210}
2211
2212func TestWhenExitSubCommandWithCodeThenAppQuitUnexpectedly(t *testing.T) {
2213	testCode := 104
2214
2215	app := NewApp()
2216	app.Commands = []Command{
2217		Command{
2218			Name: "cmd",
2219			Subcommands: []Command{
2220				Command{
2221					Name: "subcmd",
2222					Action: func(c *Context) error {
2223						return NewExitError("exit error", testCode)
2224					},
2225				},
2226			},
2227		},
2228	}
2229
2230	// set user function as ExitErrHandler
2231	var exitCodeFromExitErrHandler int
2232	app.ExitErrHandler = func(c *Context, err error) {
2233		if exitErr, ok := err.(ExitCoder); ok {
2234			t.Log(exitErr)
2235			exitCodeFromExitErrHandler = exitErr.ExitCode()
2236		}
2237	}
2238
2239	// keep and restore original OsExiter
2240	origExiter := OsExiter
2241	defer func() {
2242		OsExiter = origExiter
2243	}()
2244
2245	// set user function as OsExiter
2246	var exitCodeFromOsExiter int
2247	OsExiter = func(exitCode int) {
2248		exitCodeFromOsExiter = exitCode
2249	}
2250
2251	app.Run([]string{
2252		"myapp",
2253		"cmd",
2254		"subcmd",
2255	})
2256
2257	if exitCodeFromOsExiter != 0 {
2258		t.Errorf("exitCodeFromOsExiter should not change, but its value is %v", exitCodeFromOsExiter)
2259	}
2260
2261	if exitCodeFromExitErrHandler != testCode {
2262		t.Errorf("exitCodeFromOsExiter valeu should be %v, but its value is %v", testCode, exitCodeFromExitErrHandler)
2263	}
2264}
2265