summaryrefslogtreecommitdiff
path: root/vendor/github.com/mitchellh/cli/cli_test.go
diff options
context:
space:
mode:
authorThomas Hipp <thipp@suse.de>2017-09-15 09:37:45 +0200
committerThomas Hipp <thipp@suse.de>2017-09-15 10:01:36 +0200
commit651fe263818d1c4ad5ba8adf8c8485ce647dc40b (patch)
treefc73bb74bdcce03c48169ce624698bf240b55e6b /vendor/github.com/mitchellh/cli/cli_test.go
parent23635373aed10aab286a2436459670b686b581bb (diff)
downloadterraform-provider-libvirt-651fe263818d1c4ad5ba8adf8c8485ce647dc40b.tar
terraform-provider-libvirt-651fe263818d1c4ad5ba8adf8c8485ce647dc40b.tar.gz
vendor: update deps
This updates all vendored packages. The Glide config has been changed to try and stick to versions instead of commit IDs if possible. Signed-off-by: Thomas Hipp <thipp@suse.de>
Diffstat (limited to 'vendor/github.com/mitchellh/cli/cli_test.go')
-rw-r--r--vendor/github.com/mitchellh/cli/cli_test.go1296
1 files changed, 1296 insertions, 0 deletions
diff --git a/vendor/github.com/mitchellh/cli/cli_test.go b/vendor/github.com/mitchellh/cli/cli_test.go
new file mode 100644
index 00000000..864d6fae
--- /dev/null
+++ b/vendor/github.com/mitchellh/cli/cli_test.go
@@ -0,0 +1,1296 @@
+package cli
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+
+ "github.com/posener/complete"
+)
+
+// envComplete is the env var that the complete library sets to specify
+// it should be calculating an auto-completion. This isn't exported so we
+// reproduce it here. If it changes then we'll have to update this.
+const envComplete = "COMP_LINE"
+
+func TestCLIIsHelp(t *testing.T) {
+ testCases := []struct {
+ args []string
+ isHelp bool
+ }{
+ {[]string{"-h"}, true},
+ {[]string{"-help"}, true},
+ {[]string{"--help"}, true},
+ {[]string{"-h", "foo"}, true},
+ {[]string{"foo", "bar"}, false},
+ {[]string{"-v", "bar"}, false},
+ {[]string{"foo", "-h"}, true},
+ {[]string{"foo", "-help"}, true},
+ {[]string{"foo", "--help"}, true},
+ {[]string{"foo", "bar", "-h"}, true},
+ {[]string{"foo", "bar", "-help"}, true},
+ {[]string{"foo", "bar", "--help"}, true},
+ {[]string{"foo", "bar", "--", "zip", "-h"}, false},
+ {[]string{"foo", "bar", "--", "zip", "-help"}, false},
+ {[]string{"foo", "bar", "--", "zip", "--help"}, false},
+ }
+
+ for _, testCase := range testCases {
+ cli := &CLI{Args: testCase.args}
+ result := cli.IsHelp()
+
+ if result != testCase.isHelp {
+ t.Errorf("Expected '%#v'. Args: %#v", testCase.isHelp, testCase.args)
+ }
+ }
+}
+
+func TestCLIIsVersion(t *testing.T) {
+ testCases := []struct {
+ args []string
+ isVersion bool
+ }{
+ {[]string{"--", "-v"}, false},
+ {[]string{"--", "-version"}, false},
+ {[]string{"--", "--version"}, false},
+ {[]string{"-v"}, true},
+ {[]string{"-version"}, true},
+ {[]string{"--version"}, true},
+ {[]string{"-v", "foo"}, true},
+ {[]string{"foo", "bar"}, false},
+ {[]string{"-h", "bar"}, false},
+ {[]string{"foo", "-v"}, false},
+ {[]string{"foo", "-version"}, false},
+ {[]string{"foo", "--version"}, false},
+ {[]string{"foo", "--", "zip", "-v"}, false},
+ {[]string{"foo", "--", "zip", "-version"}, false},
+ {[]string{"foo", "--", "zip", "--version"}, false},
+ }
+
+ for _, testCase := range testCases {
+ cli := &CLI{Args: testCase.args}
+ result := cli.IsVersion()
+
+ if result != testCase.isVersion {
+ t.Errorf("Expected '%#v'. Args: %#v", testCase.isVersion, testCase.args)
+ }
+ }
+}
+
+func TestCLIRun(t *testing.T) {
+ command := new(MockCommand)
+ cli := &CLI{
+ Args: []string{"foo", "-bar", "-baz"},
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != command.RunResult {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if !command.RunCalled {
+ t.Fatalf("run should be called")
+ }
+
+ if !reflect.DeepEqual(command.RunArgs, []string{"-bar", "-baz"}) {
+ t.Fatalf("bad args: %#v", command.RunArgs)
+ }
+}
+
+func TestCLIRun_blank(t *testing.T) {
+ command := new(MockCommand)
+ cli := &CLI{
+ Args: []string{"", "foo", "-bar", "-baz"},
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != command.RunResult {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if !command.RunCalled {
+ t.Fatalf("run should be called")
+ }
+
+ if !reflect.DeepEqual(command.RunArgs, []string{"-bar", "-baz"}) {
+ t.Fatalf("bad args: %#v", command.RunArgs)
+ }
+}
+
+func TestCLIRun_prefix(t *testing.T) {
+ buf := new(bytes.Buffer)
+ command := new(MockCommand)
+ cli := &CLI{
+ Args: []string{"foobar"},
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+
+ "foo bar": func() (Command, error) {
+ return command, nil
+ },
+ },
+ HelpWriter: buf,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 127 {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if command.RunCalled {
+ t.Fatalf("run should not be called")
+ }
+}
+
+func TestCLIRun_default(t *testing.T) {
+ commandBar := new(MockCommand)
+ commandBar.RunResult = 42
+
+ cli := &CLI{
+ Args: []string{"-bar", "-baz"},
+ Commands: map[string]CommandFactory{
+ "": func() (Command, error) {
+ return commandBar, nil
+ },
+ "foo": func() (Command, error) {
+ return new(MockCommand), nil
+ },
+ },
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != commandBar.RunResult {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if !commandBar.RunCalled {
+ t.Fatalf("run should be called")
+ }
+
+ if !reflect.DeepEqual(commandBar.RunArgs, []string{"-bar", "-baz"}) {
+ t.Fatalf("bad args: %#v", commandBar.RunArgs)
+ }
+}
+
+func TestCLIRun_helpNested(t *testing.T) {
+ helpCalled := false
+ buf := new(bytes.Buffer)
+ cli := &CLI{
+ Args: []string{"--help"},
+ Commands: map[string]CommandFactory{
+ "foo sub42": func() (Command, error) {
+ return new(MockCommand), nil
+ },
+ },
+ HelpFunc: func(m map[string]CommandFactory) string {
+ helpCalled = true
+
+ var keys []string
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ expected := []string{"foo"}
+ if !reflect.DeepEqual(keys, expected) {
+ return fmt.Sprintf("error: contained sub: %#v", keys)
+ }
+
+ return ""
+ },
+ HelpWriter: buf,
+ }
+
+ code, err := cli.Run()
+ if err != nil {
+ t.Fatalf("Error: %s", err)
+ }
+
+ if code != 0 {
+ t.Fatalf("Code: %d", code)
+ }
+
+ if !helpCalled {
+ t.Fatal("help not called")
+ }
+}
+
+func TestCLIRun_nested(t *testing.T) {
+ command := new(MockCommand)
+ cli := &CLI{
+ Args: []string{"foo", "bar", "-bar", "-baz"},
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return new(MockCommand), nil
+ },
+ "foo bar": func() (Command, error) {
+ return command, nil
+ },
+ },
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != command.RunResult {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if !command.RunCalled {
+ t.Fatalf("run should be called")
+ }
+
+ if !reflect.DeepEqual(command.RunArgs, []string{"-bar", "-baz"}) {
+ t.Fatalf("bad args: %#v", command.RunArgs)
+ }
+}
+
+func TestCLIRun_nestedMissingParent(t *testing.T) {
+ buf := new(bytes.Buffer)
+ cli := &CLI{
+ Args: []string{"foo"},
+ Commands: map[string]CommandFactory{
+ "foo bar": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ },
+ HelpWriter: buf,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 1 {
+ t.Fatalf("bad exit code: %d", exitCode)
+ }
+
+ if buf.String() != testCommandNestedMissingParent {
+ t.Fatalf("bad: %#v", buf.String())
+ }
+}
+
+func TestCLIRun_printHelp(t *testing.T) {
+ testCases := [][]string{
+ {"-h"},
+ {"--help"},
+ }
+
+ for _, testCase := range testCases {
+ buf := new(bytes.Buffer)
+ helpText := "foo"
+
+ cli := &CLI{
+ Args: testCase,
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return new(MockCommand), nil
+ },
+ },
+ HelpFunc: func(map[string]CommandFactory) string {
+ return helpText
+ },
+ HelpWriter: buf,
+ }
+
+ code, err := cli.Run()
+ if err != nil {
+ t.Errorf("Args: %#v. Error: %s", testCase, err)
+ continue
+ }
+
+ if code != 0 {
+ t.Errorf("Args: %#v. Code: %d", testCase, code)
+ continue
+ }
+
+ if !strings.Contains(buf.String(), helpText) {
+ t.Errorf("Args: %#v. Text: %v", testCase, buf.String())
+ }
+ }
+}
+
+func TestCLIRun_printHelpIllegal(t *testing.T) {
+ testCases := []struct {
+ args []string
+ exit int
+ }{
+ {nil, 127},
+ {[]string{"i-dont-exist"}, 127},
+ {[]string{"-bad-flag", "foo"}, 1},
+ }
+
+ for _, testCase := range testCases {
+ buf := new(bytes.Buffer)
+ helpText := "foo"
+
+ cli := &CLI{
+ Args: testCase.args,
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return &MockCommand{HelpText: helpText}, nil
+ },
+ "foo sub42": func() (Command, error) {
+ return new(MockCommand), nil
+ },
+ },
+ HelpFunc: func(m map[string]CommandFactory) string {
+ var keys []string
+ for k := range m {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ expected := []string{"foo"}
+ if !reflect.DeepEqual(keys, expected) {
+ return fmt.Sprintf("error: contained sub: %#v", keys)
+ }
+
+ return helpText
+ },
+ HelpWriter: buf,
+ }
+
+ code, err := cli.Run()
+ if err != nil {
+ t.Errorf("Args: %#v. Error: %s", testCase, err)
+ continue
+ }
+
+ if code != testCase.exit {
+ t.Errorf("Args: %#v. Code: %d", testCase, code)
+ continue
+ }
+
+ if strings.Contains(buf.String(), "error") {
+ t.Errorf("Args: %#v. Text: %v", testCase, buf.String())
+ }
+
+ if !strings.Contains(buf.String(), helpText) {
+ t.Errorf("Args: %#v. Text: %v", testCase, buf.String())
+ }
+ }
+}
+
+func TestCLIRun_printCommandHelp(t *testing.T) {
+ testCases := [][]string{
+ {"--help", "foo"},
+ {"-h", "foo"},
+ }
+
+ for _, args := range testCases {
+ command := &MockCommand{
+ HelpText: "donuts",
+ }
+
+ buf := new(bytes.Buffer)
+ cli := &CLI{
+ Args: args,
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+ HelpWriter: buf,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad exit code: %d", exitCode)
+ }
+
+ if buf.String() != (command.HelpText + "\n") {
+ t.Fatalf("bad: %#v", buf.String())
+ }
+ }
+}
+
+func TestCLIRun_printCommandHelpNested(t *testing.T) {
+ testCases := [][]string{
+ {"--help", "foo", "bar"},
+ {"-h", "foo", "bar"},
+ }
+
+ for _, args := range testCases {
+ command := &MockCommand{
+ HelpText: "donuts",
+ }
+
+ buf := new(bytes.Buffer)
+ cli := &CLI{
+ Args: args,
+ Commands: map[string]CommandFactory{
+ "foo bar": func() (Command, error) {
+ return command, nil
+ },
+ },
+ HelpWriter: buf,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad exit code: %d", exitCode)
+ }
+
+ if buf.String() != (command.HelpText + "\n") {
+ t.Fatalf("bad: %#v", buf.String())
+ }
+ }
+}
+
+func TestCLIRun_printCommandHelpSubcommands(t *testing.T) {
+ testCases := [][]string{
+ {"--help", "foo"},
+ {"-h", "foo"},
+ }
+
+ for _, args := range testCases {
+ command := &MockCommand{
+ HelpText: "donuts",
+ }
+
+ buf := new(bytes.Buffer)
+ cli := &CLI{
+ Args: args,
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ "foo bar": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo zip": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo zap": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo banana": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo longer": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo longer longest": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ },
+ HelpWriter: buf,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad exit code: %d", exitCode)
+ }
+
+ if buf.String() != testCommandHelpSubcommandsOutput {
+ t.Fatalf("bad: %#v\n\n'%#v'\n\n'%#v'", args, buf.String(), testCommandHelpSubcommandsOutput)
+ }
+ }
+}
+
+func TestCLIRun_printCommandHelpSubcommandsNestedTwoLevel(t *testing.T) {
+ testCases := [][]string{
+ {"--help", "L1"},
+ {"-h", "L1"},
+ }
+
+ for _, args := range testCases {
+ command := &MockCommand{
+ HelpText: "donuts",
+ }
+
+ buf := new(bytes.Buffer)
+ cli := &CLI{
+ Args: args,
+ Commands: map[string]CommandFactory{
+ "L1": func() (Command, error) {
+ return command, nil
+ },
+ "L1 L2A": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "L1 L2B": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "L1 L2A L3A": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "L1 L2A L3B": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ },
+ HelpWriter: buf,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad exit code: %d", exitCode)
+ }
+
+ if buf.String() != testCommandHelpSubcommandsTwoLevelOutput {
+ t.Fatalf("bad: %#v\n\n%s\n\n%s", args, buf.String(), testCommandHelpSubcommandsOutput)
+ }
+ }
+}
+
+// Test that the root help only prints the root level.
+func TestCLIRun_printHelpRootSubcommands(t *testing.T) {
+ testCases := [][]string{
+ {"--help"},
+ {"-h"},
+ }
+
+ for _, args := range testCases {
+ buf := new(bytes.Buffer)
+ cli := &CLI{
+ Args: args,
+ Commands: map[string]CommandFactory{
+ "bar": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo bar": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo zip": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ },
+ HelpWriter: buf,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad exit code: %d", exitCode)
+ }
+
+ expected := `Usage: app [--version] [--help] <command> [<args>]
+
+Available commands are:
+ bar hi!
+ foo hi!
+
+`
+ if buf.String() != expected {
+ t.Fatalf("bad: %#v\n\n'%#v'\n\n'%#v'", args, buf.String(), expected)
+ }
+ }
+}
+
+func TestCLIRun_printCommandHelpTemplate(t *testing.T) {
+ testCases := [][]string{
+ {"--help", "foo"},
+ {"-h", "foo"},
+ }
+
+ for _, args := range testCases {
+ command := &MockCommandHelpTemplate{
+ MockCommand: MockCommand{
+ HelpText: "donuts",
+ },
+
+ HelpTemplateText: "hello {{.Help}}",
+ }
+
+ buf := new(bytes.Buffer)
+ cli := &CLI{
+ Args: args,
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+ HelpWriter: buf,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad exit code: %d", exitCode)
+ }
+
+ if buf.String() != "hello "+command.HelpText+"\n" {
+ t.Fatalf("bad: %#v", buf.String())
+ }
+ }
+}
+
+func TestCLIRun_helpHiddenRoot(t *testing.T) {
+ helpCalled := false
+ buf := new(bytes.Buffer)
+ cli := &CLI{
+ Args: []string{"--help"},
+ HiddenCommands: []string{"bar"},
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return &MockCommand{}, nil
+ },
+ "bar": func() (Command, error) {
+ return &MockCommand{}, nil
+ },
+ },
+ HelpFunc: func(m map[string]CommandFactory) string {
+ helpCalled = true
+
+ if _, ok := m["foo"]; !ok {
+ t.Fatal("should have foo")
+ }
+ if _, ok := m["bar"]; ok {
+ t.Fatal("should not have bar")
+ }
+
+ return ""
+ },
+ HelpWriter: buf,
+ }
+
+ code, err := cli.Run()
+ if err != nil {
+ t.Fatalf("Error: %s", err)
+ }
+
+ if code != 0 {
+ t.Fatalf("Code: %d", code)
+ }
+
+ if !helpCalled {
+ t.Fatal("help not called")
+ }
+}
+
+func TestCLIRun_helpHiddenNested(t *testing.T) {
+ command := &MockCommand{
+ HelpText: "donuts",
+ }
+
+ buf := new(bytes.Buffer)
+ cli := &CLI{
+ Args: []string{"foo", "--help"},
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ "foo bar": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo zip": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo longer": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ "foo longer longest": func() (Command, error) {
+ return &MockCommand{SynopsisText: "hi!"}, nil
+ },
+ },
+ HiddenCommands: []string{"foo zip", "foo longer longest"},
+ HelpWriter: buf,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad exit code: %d", exitCode)
+ }
+
+ if buf.String() != testCommandHelpSubcommandsHiddenOutput {
+ t.Fatalf("bad: '%#v'\n\n'%#v'", buf.String(), testCommandHelpSubcommandsOutput)
+ }
+}
+
+func TestCLIRun_autocompleteBoth(t *testing.T) {
+ command := new(MockCommand)
+ cli := &CLI{
+ Args: []string{
+ "-" + defaultAutocompleteInstall,
+ "-" + defaultAutocompleteUninstall,
+ },
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+
+ Name: "foo",
+ Autocomplete: true,
+ autocompleteInstaller: &mockAutocompleteInstaller{},
+ }
+
+ exitCode, err := cli.Run()
+ if err == nil {
+ t.Fatal("should error")
+ }
+
+ if exitCode != 1 {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if command.RunCalled {
+ t.Fatalf("run should not be called")
+ }
+}
+
+func TestCLIRun_autocompleteInstall(t *testing.T) {
+ command := new(MockCommand)
+ installer := new(mockAutocompleteInstaller)
+ cli := &CLI{
+ Args: []string{
+ "-" + defaultAutocompleteInstall,
+ },
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+
+ Name: "foo",
+ Autocomplete: true,
+ autocompleteInstaller: installer,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if command.RunCalled {
+ t.Fatalf("run should not be called")
+ }
+
+ if !installer.InstallCalled {
+ t.Fatal("should call install")
+ }
+}
+
+func TestCLIRun_autocompleteUninstall(t *testing.T) {
+ command := new(MockCommand)
+ installer := new(mockAutocompleteInstaller)
+ cli := &CLI{
+ Args: []string{
+ "-" + defaultAutocompleteUninstall,
+ },
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+
+ Name: "foo",
+ Autocomplete: true,
+ autocompleteInstaller: installer,
+ }
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if command.RunCalled {
+ t.Fatalf("run should not be called")
+ }
+
+ if !installer.UninstallCalled {
+ t.Fatal("should call uninstall")
+ }
+}
+
+func TestCLIRun_autocompleteNoName(t *testing.T) {
+ command := new(MockCommand)
+ installer := new(mockAutocompleteInstaller)
+ cli := &CLI{
+ Args: []string{"foo"},
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+
+ Autocomplete: true,
+ autocompleteInstaller: installer,
+ }
+
+ exitCode, err := cli.Run()
+ if err == nil {
+ t.Fatal("should error")
+ }
+
+ if exitCode != 1 {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if command.RunCalled {
+ t.Fatalf("run should not be called")
+ }
+}
+
+// Test that running `-autocomplete-install<tab>` doesn't execute
+// the autocomplete installer. This was a bug reported by Nomad.
+func TestCLIRun_autocompleteInstallTab(t *testing.T) {
+ command := new(MockCommand)
+ installer := new(mockAutocompleteInstaller)
+ cli := &CLI{
+ Args: []string{
+ "-" + defaultAutocompleteInstall,
+ },
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+
+ Name: "foo",
+ Autocomplete: true,
+ autocompleteInstaller: installer,
+ }
+
+ defer testAutocomplete(t, "foo -autocomplete-install")()
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if command.RunCalled {
+ t.Fatalf("run should not be called")
+ }
+
+ if installer.InstallCalled {
+ t.Fatal("should not call install")
+ }
+}
+
+func TestCLIRun_autocompleteHelpTab(t *testing.T) {
+ buf := new(bytes.Buffer)
+ command := new(MockCommand)
+ installer := new(mockAutocompleteInstaller)
+ cli := &CLI{
+ Args: []string{
+ "-help",
+ },
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+
+ Name: "foo",
+ HelpWriter: buf,
+ Autocomplete: true,
+ autocompleteInstaller: installer,
+ }
+
+ defer testAutocomplete(t, "foo -help")()
+
+ exitCode, err := cli.Run()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ if exitCode != 0 {
+ t.Fatalf("bad: %d", exitCode)
+ }
+
+ if command.RunCalled {
+ t.Fatalf("run should not be called")
+ }
+
+ if buf.String() != "" {
+ t.Fatal("help should be empty")
+ }
+}
+
+func TestCLIAutocomplete_root(t *testing.T) {
+ cases := []struct {
+ Completed []string
+ Last string
+ Expected []string
+ }{
+ {nil, "-v", []string{"-version"}},
+ {nil, "-h", []string{"-help"}},
+ {nil, "-a", []string{
+ "-" + defaultAutocompleteInstall,
+ "-" + defaultAutocompleteUninstall,
+ }},
+
+ {nil, "f", []string{"foo"}},
+ {nil, "n", []string{"nodes", "noodles"}},
+ {nil, "noo", []string{"noodles"}},
+ {nil, "su", []string{"sub"}},
+ {nil, "h", nil},
+
+ // Make sure global flags work on subcommands
+ {[]string{"sub"}, "-v", nil},
+ {[]string{"sub"}, "o", []string{"one"}},
+ {[]string{"sub"}, "su", []string{"sub2"}},
+ {[]string{"sub", "sub2"}, "o", []string{"one"}},
+ {[]string{"deep", "deep2"}, "a", []string{"a1"}},
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.Last, func(t *testing.T) {
+ command := new(MockCommand)
+ cli := &CLI{
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) { return command, nil },
+ "nodes": func() (Command, error) { return command, nil },
+ "noodles": func() (Command, error) { return command, nil },
+ "hidden": func() (Command, error) { return command, nil },
+ "sub one": func() (Command, error) { return command, nil },
+ "sub two": func() (Command, error) { return command, nil },
+ "sub sub2 one": func() (Command, error) { return command, nil },
+ "sub sub2 two": func() (Command, error) { return command, nil },
+ "deep deep2 a1": func() (Command, error) { return command, nil },
+ "deep deep2 b2": func() (Command, error) { return command, nil },
+ },
+ HiddenCommands: []string{"hidden"},
+
+ Autocomplete: true,
+ }
+
+ // Initialize
+ cli.init()
+
+ // Build All value
+ var all []string
+ all = append(all, tc.Completed...)
+ all = append(all, tc.Last)
+
+ // Test the autocompleter
+ actual := cli.autocomplete.Command.Predict(complete.Args{
+ All: all,
+ Completed: tc.Completed,
+ Last: tc.Last,
+ })
+ sort.Strings(actual)
+
+ if !reflect.DeepEqual(actual, tc.Expected) {
+ t.Fatalf("bad prediction: %#v", actual)
+ }
+ })
+ }
+}
+
+func TestCLIAutocomplete_rootGlobalFlags(t *testing.T) {
+ cases := []struct {
+ Completed []string
+ Last string
+ Expected []string
+ }{
+ {nil, "-v", []string{"-version"}},
+ {nil, "-t", []string{"-tubes"}},
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.Last, func(t *testing.T) {
+ command := new(MockCommand)
+ cli := &CLI{
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) { return command, nil },
+ },
+
+ Autocomplete: true,
+ AutocompleteGlobalFlags: map[string]complete.Predictor{
+ "-tubes": complete.PredictNothing,
+ },
+ }
+
+ // Initialize
+ cli.init()
+
+ // Test the autocompleter
+ actual := cli.autocomplete.Command.Predict(complete.Args{
+ Completed: tc.Completed,
+ Last: tc.Last,
+ })
+ sort.Strings(actual)
+
+ if !reflect.DeepEqual(actual, tc.Expected) {
+ t.Fatalf("bad prediction: %#v", actual)
+ }
+ })
+ }
+}
+
+func TestCLIAutocomplete_rootDisableDefaultFlags(t *testing.T) {
+ cases := []struct {
+ Completed []string
+ Last string
+ Expected []string
+ }{
+ {nil, "-v", nil},
+ {nil, "-h", nil},
+ {nil, "-auto", nil},
+ {nil, "-t", []string{"-tubes"}},
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.Last, func(t *testing.T) {
+ command := new(MockCommand)
+ cli := &CLI{
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) { return command, nil },
+ },
+
+ Autocomplete: true,
+ AutocompleteNoDefaultFlags: true,
+ AutocompleteGlobalFlags: map[string]complete.Predictor{
+ "-tubes": complete.PredictNothing,
+ },
+ }
+
+ // Initialize
+ cli.init()
+
+ // Test the autocompleter
+ actual := cli.autocomplete.Command.Predict(complete.Args{
+ Completed: tc.Completed,
+ Last: tc.Last,
+ })
+ sort.Strings(actual)
+
+ if !reflect.DeepEqual(actual, tc.Expected) {
+ t.Fatalf("bad prediction: %#v", actual)
+ }
+ })
+ }
+}
+
+func TestCLIAutocomplete_subcommandArgs(t *testing.T) {
+ cases := []struct {
+ Completed []string
+ Last string
+ Expected []string
+ }{
+ {[]string{"foo"}, "RE", []string{"README.md"}},
+ {[]string{"foo", "-go"}, "asdf", []string{"yo"}},
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.Last, func(t *testing.T) {
+ command := new(MockCommandAutocomplete)
+ command.AutocompleteArgsValue = complete.PredictFiles("*")
+ command.AutocompleteFlagsValue = map[string]complete.Predictor{
+ "-go": complete.PredictFunc(func(complete.Args) []string {
+ return []string{"yo"}
+ }),
+ }
+
+ cli := &CLI{
+ Commands: map[string]CommandFactory{
+ "foo": func() (Command, error) {
+ return command, nil
+ },
+ },
+
+ Autocomplete: true,
+ }
+
+ // Initialize
+ cli.init()
+
+ // Test the autocompleter
+ actual := cli.autocomplete.Command.Predict(complete.Args{
+ Completed: tc.Completed,
+ Last: tc.Last,
+ LastCompleted: tc.Completed[len(tc.Completed)-1],
+ })
+ sort.Strings(actual)
+
+ if !reflect.DeepEqual(actual, tc.Expected) {
+ t.Fatalf("bad prediction: %#v", actual)
+ }
+ })
+ }
+}
+
+func TestCLISubcommand(t *testing.T) {
+ testCases := []struct {
+ args []string
+ subcommand string
+ }{
+ {[]string{"bar"}, "bar"},
+ {[]string{"foo", "-h"}, "foo"},
+ {[]string{"-h", "bar"}, "bar"},
+ {[]string{"foo", "bar", "-h"}, "foo"},
+ }
+
+ for _, testCase := range testCases {
+ cli := &CLI{Args: testCase.args}
+ result := cli.Subcommand()
+
+ if result != testCase.subcommand {
+ t.Errorf("Expected %#v, got %#v. Args: %#v",
+ testCase.subcommand, result, testCase.args)
+ }
+ }
+}
+
+func TestCLISubcommand_nested(t *testing.T) {
+ testCases := []struct {
+ args []string
+ subcommand string
+ }{
+ {[]string{"bar"}, "bar"},
+ {[]string{"foo", "-h"}, "foo"},
+ {[]string{"-h", "bar"}, "bar"},
+ {[]string{"foo", "bar", "-h"}, "foo bar"},
+ {[]string{"foo", "bar", "baz", "-h"}, "foo bar"},
+ {[]string{"foo", "bar", "-h", "baz"}, "foo bar"},
+ {[]string{"-h", "foo", "bar"}, "foo bar"},
+ }
+
+ for _, testCase := range testCases {
+ cli := &CLI{
+ Args: testCase.args,
+ Commands: map[string]CommandFactory{
+ "foo bar": func() (Command, error) {
+ return new(MockCommand), nil
+ },
+ },
+ }
+ result := cli.Subcommand()
+
+ if result != testCase.subcommand {
+ t.Errorf("Expected %#v, got %#v. Args: %#v",
+ testCase.subcommand, result, testCase.args)
+ }
+ }
+}
+
+// testAutocomplete sets up the environment to behave like a <tab> was
+// pressed in a shell to autocomplete a command.
+func testAutocomplete(t *testing.T, input string) func() {
+ // This env var is used to trigger autocomplete
+ os.Setenv(envComplete, input)
+
+ // Change stdout/stderr since the autocompleter writes directly to them.
+ oldStdout := os.Stdout
+ oldStderr := os.Stderr
+
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+
+ os.Stdout = w
+ os.Stderr = w
+
+ return func() {
+ // Reset our env
+ os.Unsetenv(envComplete)
+
+ // Reset stdout, stderr
+ os.Stdout = oldStdout
+ os.Stderr = oldStderr
+
+ // Close our pipe
+ r.Close()
+ w.Close()
+ }
+}
+
+const testCommandNestedMissingParent = `This command is accessed by using one of the subcommands below.
+
+Subcommands:
+ bar hi!
+`
+
+const testCommandHelpSubcommandsOutput = `donuts
+
+Subcommands:
+ banana hi!
+ bar hi!
+ longer hi!
+ zap hi!
+ zip hi!
+`
+
+const testCommandHelpSubcommandsHiddenOutput = `donuts
+
+Subcommands:
+ bar hi!
+ longer hi!
+`
+
+const testCommandHelpSubcommandsTwoLevelOutput = `donuts
+
+Subcommands:
+ L2A hi!
+ L2B hi!
+`