diff options
author | Thomas Hipp <thipp@suse.de> | 2017-09-15 09:37:45 +0200 |
---|---|---|
committer | Thomas Hipp <thipp@suse.de> | 2017-09-15 10:01:36 +0200 |
commit | 651fe263818d1c4ad5ba8adf8c8485ce647dc40b (patch) | |
tree | fc73bb74bdcce03c48169ce624698bf240b55e6b /vendor/github.com/mitchellh/cli/cli_test.go | |
parent | 23635373aed10aab286a2436459670b686b581bb (diff) | |
download | terraform-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.go | 1296 |
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! +` |