2015-03-16 19:31:03 +00:00
|
|
|
package cobra
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-05-14 12:57:11 +00:00
|
|
|
"io/ioutil"
|
2015-03-16 19:31:03 +00:00
|
|
|
"os"
|
2016-04-02 20:45:01 +00:00
|
|
|
"os/exec"
|
2015-03-16 19:31:03 +00:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2015-04-07 21:38:22 +00:00
|
|
|
func checkOmit(t *testing.T, found, unexpected string) {
|
|
|
|
if strings.Contains(found, unexpected) {
|
|
|
|
t.Errorf("Unexpected response.\nGot: %q\nBut should not have!\n", unexpected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-16 19:31:03 +00:00
|
|
|
func check(t *testing.T, found, expected string) {
|
|
|
|
if !strings.Contains(found, expected) {
|
|
|
|
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-02 20:45:01 +00:00
|
|
|
func runShellCheck(s string) error {
|
|
|
|
excluded := []string{
|
|
|
|
"SC2034", // PREFIX appears unused. Verify it or export it.
|
|
|
|
}
|
|
|
|
cmd := exec.Command("shellcheck", "-s", "bash", "-", "-e", strings.Join(excluded, ","))
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
|
|
|
|
stdin, err := cmd.StdinPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
defer stdin.Close()
|
|
|
|
stdin.Write([]byte(s))
|
|
|
|
}()
|
|
|
|
|
|
|
|
return cmd.Run()
|
|
|
|
}
|
|
|
|
|
2015-03-16 19:31:03 +00:00
|
|
|
// World worst custom function, just keep telling you to enter hello!
|
|
|
|
const (
|
2016-03-31 13:53:34 +00:00
|
|
|
bashCompletionFunc = `__custom_func() {
|
2015-03-16 19:31:03 +00:00
|
|
|
COMPREPLY=( "hello" )
|
|
|
|
}
|
|
|
|
`
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestBashCompletions(t *testing.T) {
|
|
|
|
c := initializeWithRootCmd()
|
|
|
|
cmdEcho.AddCommand(cmdTimes)
|
2015-12-10 04:57:45 +00:00
|
|
|
c.AddCommand(cmdEcho, cmdPrint, cmdDeprecated, cmdColon)
|
2015-03-16 19:31:03 +00:00
|
|
|
|
|
|
|
// custom completion function
|
2016-03-31 13:53:34 +00:00
|
|
|
c.BashCompletionFunction = bashCompletionFunc
|
2015-03-16 19:31:03 +00:00
|
|
|
|
|
|
|
// required flag
|
|
|
|
c.MarkFlagRequired("introot")
|
|
|
|
|
2015-06-22 18:28:16 +00:00
|
|
|
// valid nouns
|
2016-03-25 15:05:56 +00:00
|
|
|
validArgs := []string{"pod", "node", "service", "replicationcontroller"}
|
2015-03-16 19:31:03 +00:00
|
|
|
c.ValidArgs = validArgs
|
|
|
|
|
2016-03-25 15:05:56 +00:00
|
|
|
// noun aliases
|
|
|
|
argAliases := []string{"pods", "nodes", "services", "replicationcontrollers", "po", "no", "svc", "rc"}
|
|
|
|
c.ArgAliases = argAliases
|
|
|
|
|
2015-06-22 18:28:16 +00:00
|
|
|
// filename
|
2015-03-16 19:31:03 +00:00
|
|
|
var flagval string
|
|
|
|
c.Flags().StringVar(&flagval, "filename", "", "Enter a filename")
|
2015-06-22 18:28:16 +00:00
|
|
|
c.MarkFlagFilename("filename", "json", "yaml", "yml")
|
|
|
|
|
2015-11-04 19:42:43 +00:00
|
|
|
// persistent filename
|
|
|
|
var flagvalPersistent string
|
|
|
|
c.PersistentFlags().StringVar(&flagvalPersistent, "persistent-filename", "", "Enter a filename")
|
|
|
|
c.MarkPersistentFlagFilename("persistent-filename")
|
|
|
|
c.MarkPersistentFlagRequired("persistent-filename")
|
|
|
|
|
2015-06-22 18:28:16 +00:00
|
|
|
// filename extensions
|
|
|
|
var flagvalExt string
|
|
|
|
c.Flags().StringVar(&flagvalExt, "filename-ext", "", "Enter a filename (extension limited)")
|
|
|
|
c.MarkFlagFilename("filename-ext")
|
2015-03-16 19:31:03 +00:00
|
|
|
|
2016-03-20 20:05:18 +00:00
|
|
|
// filename extensions
|
|
|
|
var flagvalCustom string
|
|
|
|
c.Flags().StringVar(&flagvalCustom, "custom", "", "Enter a filename (extension limited)")
|
|
|
|
c.MarkFlagCustom("custom", "__complete_custom")
|
|
|
|
|
2015-08-09 19:30:58 +00:00
|
|
|
// subdirectories in a given directory
|
|
|
|
var flagvalTheme string
|
|
|
|
c.Flags().StringVar(&flagvalTheme, "theme", "", "theme to use (located in /themes/THEMENAME/)")
|
|
|
|
c.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"})
|
|
|
|
|
2015-03-16 19:31:03 +00:00
|
|
|
out := new(bytes.Buffer)
|
|
|
|
c.GenBashCompletion(out)
|
|
|
|
str := out.String()
|
|
|
|
|
|
|
|
check(t, str, "_cobra-test")
|
|
|
|
check(t, str, "_cobra-test_echo")
|
|
|
|
check(t, str, "_cobra-test_echo_times")
|
|
|
|
check(t, str, "_cobra-test_print")
|
2015-12-10 04:57:45 +00:00
|
|
|
check(t, str, "_cobra-test_cmd__colon")
|
2015-03-16 19:31:03 +00:00
|
|
|
|
|
|
|
// check for required flags
|
|
|
|
check(t, str, `must_have_one_flag+=("--introot=")`)
|
2015-11-04 19:42:43 +00:00
|
|
|
check(t, str, `must_have_one_flag+=("--persistent-filename=")`)
|
2015-03-16 19:31:03 +00:00
|
|
|
// check for custom completion function
|
|
|
|
check(t, str, `COMPREPLY=( "hello" )`)
|
|
|
|
// check for required nouns
|
2016-03-25 15:05:56 +00:00
|
|
|
check(t, str, `must_have_one_noun+=("pod")`)
|
|
|
|
// check for noun aliases
|
|
|
|
check(t, str, `noun_aliases+=("pods")`)
|
|
|
|
check(t, str, `noun_aliases+=("rc")`)
|
|
|
|
checkOmit(t, str, `must_have_one_noun+=("pods")`)
|
2015-06-22 18:28:16 +00:00
|
|
|
// check for filename extension flags
|
|
|
|
check(t, str, `flags_completion+=("_filedir")`)
|
|
|
|
// check for filename extension flags
|
2015-05-04 19:44:07 +00:00
|
|
|
check(t, str, `flags_completion+=("__handle_filename_extension_flag json|yaml|yml")`)
|
2016-03-20 20:05:18 +00:00
|
|
|
// check for custom flags
|
|
|
|
check(t, str, `flags_completion+=("__complete_custom")`)
|
2015-08-09 19:30:58 +00:00
|
|
|
// check for subdirs_in_dir flags
|
|
|
|
check(t, str, `flags_completion+=("__handle_subdirs_in_dir_flag themes")`)
|
2015-04-07 21:38:22 +00:00
|
|
|
|
|
|
|
checkOmit(t, str, cmdDeprecated.Name())
|
2016-04-02 20:45:01 +00:00
|
|
|
|
|
|
|
// if available, run shellcheck against the script
|
|
|
|
if err := exec.Command("which", "shellcheck").Run(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err := runShellCheck(str)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("shellcheck failed: %v", err)
|
|
|
|
}
|
2015-03-16 19:31:03 +00:00
|
|
|
}
|
2016-08-02 21:49:33 +00:00
|
|
|
|
|
|
|
func TestBashCompletionHiddenFlag(t *testing.T) {
|
|
|
|
var cmdTrue = &Command{
|
|
|
|
Use: "does nothing",
|
|
|
|
Run: func(cmd *Command, args []string) {},
|
|
|
|
}
|
|
|
|
|
|
|
|
const flagName = "hidden-foo-bar-baz"
|
|
|
|
|
|
|
|
var flagValue bool
|
|
|
|
cmdTrue.Flags().BoolVar(&flagValue, flagName, false, "hidden flag")
|
|
|
|
cmdTrue.Flags().MarkHidden(flagName)
|
|
|
|
|
|
|
|
out := new(bytes.Buffer)
|
|
|
|
cmdTrue.GenBashCompletion(out)
|
|
|
|
bashCompletion := out.String()
|
|
|
|
if strings.Contains(bashCompletion, flagName) {
|
2016-08-20 07:09:46 +00:00
|
|
|
t.Errorf("expected completion to not include %q flag: Got %v", flagName, bashCompletion)
|
2016-08-02 21:49:33 +00:00
|
|
|
}
|
|
|
|
}
|
2016-08-02 22:01:33 +00:00
|
|
|
|
|
|
|
func TestBashCompletionDeprecatedFlag(t *testing.T) {
|
|
|
|
var cmdTrue = &Command{
|
|
|
|
Use: "does nothing",
|
|
|
|
Run: func(cmd *Command, args []string) {},
|
|
|
|
}
|
|
|
|
|
|
|
|
const flagName = "deprecated-foo-bar-baz"
|
|
|
|
|
|
|
|
var flagValue bool
|
|
|
|
cmdTrue.Flags().BoolVar(&flagValue, flagName, false, "hidden flag")
|
|
|
|
cmdTrue.Flags().MarkDeprecated(flagName, "use --does-not-exist instead")
|
|
|
|
|
|
|
|
out := new(bytes.Buffer)
|
|
|
|
cmdTrue.GenBashCompletion(out)
|
|
|
|
bashCompletion := out.String()
|
|
|
|
if strings.Contains(bashCompletion, flagName) {
|
|
|
|
t.Errorf("expected completion to not include %q flag: Got %v", flagName, bashCompletion)
|
|
|
|
}
|
|
|
|
}
|
2017-05-14 12:57:11 +00:00
|
|
|
|
|
|
|
func BenchmarkBashCompletion(b *testing.B) {
|
|
|
|
c := initializeWithRootCmd()
|
|
|
|
cmdEcho.AddCommand(cmdTimes)
|
|
|
|
c.AddCommand(cmdEcho, cmdPrint, cmdDeprecated, cmdColon)
|
|
|
|
|
|
|
|
file, err := ioutil.TempFile("", "")
|
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.Remove(file.Name())
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
if err := c.GenBashCompletion(file); err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|