summaryrefslogtreecommitdiffstats
path: root/completions_test.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 17:15:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 17:15:09 +0000
commitea9f089f6ad153f432b8265a99bce7d261b9c820 (patch)
tree8590b318bb54874bf1f0c597ff503f8c3b9ace75 /completions_test.go
parentInitial commit. (diff)
downloadgolang-github-spf13-cobra-ea9f089f6ad153f432b8265a99bce7d261b9c820.tar.xz
golang-github-spf13-cobra-ea9f089f6ad153f432b8265a99bce7d261b9c820.zip
Adding upstream version 1.8.0.upstream/1.8.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'completions_test.go')
-rw-r--r--completions_test.go3519
1 files changed, 3519 insertions, 0 deletions
diff --git a/completions_test.go b/completions_test.go
new file mode 100644
index 0000000..d5aee25
--- /dev/null
+++ b/completions_test.go
@@ -0,0 +1,3519 @@
+// Copyright 2013-2023 The Cobra Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cobra
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "strings"
+ "sync"
+ "testing"
+)
+
+func validArgsFunc(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ if len(args) != 0 {
+ return nil, ShellCompDirectiveNoFileComp
+ }
+
+ var completions []string
+ for _, comp := range []string{"one\tThe first", "two\tThe second"} {
+ if strings.HasPrefix(comp, toComplete) {
+ completions = append(completions, comp)
+ }
+ }
+ return completions, ShellCompDirectiveDefault
+}
+
+func validArgsFunc2(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ if len(args) != 0 {
+ return nil, ShellCompDirectiveNoFileComp
+ }
+
+ var completions []string
+ for _, comp := range []string{"three\tThe third", "four\tThe fourth"} {
+ if strings.HasPrefix(comp, toComplete) {
+ completions = append(completions, comp)
+ }
+ }
+ return completions, ShellCompDirectiveDefault
+}
+
+func TestCmdNameCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ childCmd1 := &Command{
+ Use: "firstChild",
+ Short: "First command",
+ Run: emptyRun,
+ }
+ childCmd2 := &Command{
+ Use: "secondChild",
+ Run: emptyRun,
+ }
+ hiddenCmd := &Command{
+ Use: "testHidden",
+ Hidden: true, // Not completed
+ Run: emptyRun,
+ }
+ deprecatedCmd := &Command{
+ Use: "testDeprecated",
+ Deprecated: "deprecated", // Not completed
+ Run: emptyRun,
+ }
+ aliasedCmd := &Command{
+ Use: "aliased",
+ Short: "A command with aliases",
+ Aliases: []string{"testAlias", "testSynonym"}, // Not completed
+ Run: emptyRun,
+ }
+
+ rootCmd.AddCommand(childCmd1, childCmd2, hiddenCmd, deprecatedCmd, aliasedCmd)
+
+ // Test that sub-command names are completed
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "aliased",
+ "completion",
+ "firstChild",
+ "help",
+ "secondChild",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that sub-command names are completed with prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "s")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "secondChild",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that even with no valid sub-command matches, hidden, deprecated and
+ // aliases are not completed
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "test")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that sub-command names are completed with description
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "aliased\tA command with aliases",
+ "completion\tGenerate the autocompletion script for the specified shell",
+ "firstChild\tFirst command",
+ "help\tHelp about any command",
+ "secondChild",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestNoCmdNameCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ rootCmd.Flags().String("localroot", "", "local root flag")
+
+ childCmd1 := &Command{
+ Use: "childCmd1",
+ Short: "First command",
+ Args: MinimumNArgs(0),
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(childCmd1)
+ childCmd1.PersistentFlags().StringP("persistent", "p", "", "persistent flag")
+ persistentFlag := childCmd1.PersistentFlags().Lookup("persistent")
+ childCmd1.Flags().StringP("nonPersistent", "n", "", "non-persistent flag")
+ nonPersistentFlag := childCmd1.Flags().Lookup("nonPersistent")
+
+ childCmd2 := &Command{
+ Use: "childCmd2",
+ Run: emptyRun,
+ }
+ childCmd1.AddCommand(childCmd2)
+
+ // Test that sub-command names are not completed if there is an argument already
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "arg1", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that sub-command names are not completed if a local non-persistent flag is present
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--nonPersistent", "value", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ nonPersistentFlag.Changed = false
+
+ expected = strings.Join([]string{
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that sub-command names are completed if a local non-persistent flag is present and TraverseChildren is set to true
+ // set TraverseChildren to true on the root cmd
+ rootCmd.TraverseChildren = true
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset TraverseChildren for next command
+ rootCmd.TraverseChildren = false
+
+ expected = strings.Join([]string{
+ "childCmd1",
+ "completion",
+ "help",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that sub-command names from a child cmd are completed if a local non-persistent flag is present
+ // and TraverseChildren is set to true on the root cmd
+ rootCmd.TraverseChildren = true
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "--nonPersistent", "value", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset TraverseChildren for next command
+ rootCmd.TraverseChildren = false
+ // Reset the flag for the next command
+ nonPersistentFlag.Changed = false
+
+ expected = strings.Join([]string{
+ "childCmd2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that we don't use Traverse when we shouldn't.
+ // This command should not return a completion since the command line is invalid without TraverseChildren.
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that sub-command names are not completed if a local non-persistent short flag is present
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-n", "value", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ nonPersistentFlag.Changed = false
+
+ expected = strings.Join([]string{
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that sub-command names are completed with a persistent flag
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--persistent", "value", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ persistentFlag.Changed = false
+
+ expected = strings.Join([]string{
+ "childCmd2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that sub-command names are completed with a persistent short flag
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-p", "value", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ persistentFlag.Changed = false
+
+ expected = strings.Join([]string{
+ "childCmd2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ ValidArgs: []string{"one", "two", "three"},
+ Args: MinimumNArgs(1),
+ }
+
+ // Test that validArgs are completed
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "one",
+ "two",
+ "three",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that validArgs are completed with prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "o")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "one",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that validArgs don't repeat
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "one", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsAndCmdCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ ValidArgs: []string{"one", "two"},
+ Run: emptyRun,
+ }
+
+ childCmd := &Command{
+ Use: "thechild",
+ Run: emptyRun,
+ }
+
+ rootCmd.AddCommand(childCmd)
+
+ // Test that both sub-commands and validArgs are completed
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "completion",
+ "help",
+ "thechild",
+ "one",
+ "two",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that both sub-commands and validArgs are completed with prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "thechild",
+ "two",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+
+ childCmd := &Command{
+ Use: "thechild",
+ Short: "The child command",
+ Run: emptyRun,
+ }
+
+ rootCmd.AddCommand(childCmd)
+
+ // Test that both sub-commands and validArgsFunction are completed
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "completion",
+ "help",
+ "thechild",
+ "one",
+ "two",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that both sub-commands and validArgs are completed with prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "thechild",
+ "two",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that both sub-commands and validArgs are completed with description
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "thechild\tThe child command",
+ "two\tThe second",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestFlagNameCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ childCmd := &Command{
+ Use: "childCmd",
+ Version: "1.2.3",
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(childCmd)
+
+ rootCmd.Flags().IntP("first", "f", -1, "first flag")
+ rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag")
+ childCmd.Flags().String("subFlag", "", "sub flag")
+
+ // Test that flag names are not shown if the user has not given the '-' prefix
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "childCmd",
+ "completion",
+ "help",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that flag names are completed
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--first",
+ "-f",
+ "--help",
+ "-h",
+ "--second",
+ "-s",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that flag names are completed when a prefix is given
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--f")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--first",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that flag names are completed in a sub-cmd
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--second",
+ "-s",
+ "--help",
+ "-h",
+ "--subFlag",
+ "--version",
+ "-v",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestFlagNameCompletionInGoWithDesc(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ childCmd := &Command{
+ Use: "childCmd",
+ Short: "first command",
+ Version: "1.2.3",
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(childCmd)
+
+ rootCmd.Flags().IntP("first", "f", -1, "first flag\nlonger description for flag")
+ rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag")
+ childCmd.Flags().String("subFlag", "", "sub flag")
+
+ // Test that flag names are not shown if the user has not given the '-' prefix
+ output, err := executeCommand(rootCmd, ShellCompRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "childCmd\tfirst command",
+ "completion\tGenerate the autocompletion script for the specified shell",
+ "help\tHelp about any command",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that flag names are completed
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--first\tfirst flag",
+ "-f\tfirst flag",
+ "--help\thelp for root",
+ "-h\thelp for root",
+ "--second\tsecond flag",
+ "-s\tsecond flag",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that flag names are completed when a prefix is given
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--f")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--first\tfirst flag",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that flag names are completed in a sub-cmd
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "childCmd", "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--second\tsecond flag",
+ "-s\tsecond flag",
+ "--help\thelp for childCmd",
+ "-h\thelp for childCmd",
+ "--subFlag\tsub flag",
+ "--version\tversion for childCmd",
+ "-v\tversion for childCmd",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestFlagNameCompletionRepeat(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ childCmd := &Command{
+ Use: "childCmd",
+ Short: "first command",
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(childCmd)
+
+ rootCmd.Flags().IntP("first", "f", -1, "first flag")
+ firstFlag := rootCmd.Flags().Lookup("first")
+ rootCmd.Flags().BoolP("second", "s", false, "second flag")
+ secondFlag := rootCmd.Flags().Lookup("second")
+ rootCmd.Flags().StringArrayP("array", "a", nil, "array flag")
+ arrayFlag := rootCmd.Flags().Lookup("array")
+ rootCmd.Flags().IntSliceP("slice", "l", nil, "slice flag")
+ sliceFlag := rootCmd.Flags().Lookup("slice")
+ rootCmd.Flags().BoolSliceP("bslice", "b", nil, "bool slice flag")
+ bsliceFlag := rootCmd.Flags().Lookup("bslice")
+
+ // Test that flag names are not repeated unless they are an array or slice
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ firstFlag.Changed = false
+
+ expected := strings.Join([]string{
+ "--array",
+ "--bslice",
+ "--help",
+ "--second",
+ "--slice",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that flag names are not repeated unless they are an array or slice
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--second=false", "--")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ firstFlag.Changed = false
+ secondFlag.Changed = false
+
+ expected = strings.Join([]string{
+ "--array",
+ "--bslice",
+ "--help",
+ "--slice",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that flag names are not repeated unless they are an array or slice
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--slice", "1", "--slice=2", "--array", "val", "--bslice", "true", "--")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ sliceFlag.Changed = false
+ arrayFlag.Changed = false
+ bsliceFlag.Changed = false
+
+ expected = strings.Join([]string{
+ "--array",
+ "--bslice",
+ "--first",
+ "--help",
+ "--second",
+ "--slice",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that flag names are not repeated unless they are an array or slice, using shortname
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ sliceFlag.Changed = false
+ arrayFlag.Changed = false
+
+ expected = strings.Join([]string{
+ "--array",
+ "-a",
+ "--bslice",
+ "-b",
+ "--first",
+ "-f",
+ "--help",
+ "-h",
+ "--second",
+ "-s",
+ "--slice",
+ "-l",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that flag names are not repeated unless they are an array or slice, using shortname with prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-a")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ sliceFlag.Changed = false
+ arrayFlag.Changed = false
+
+ expected = strings.Join([]string{
+ "-a",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestRequiredFlagNameCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ ValidArgs: []string{"realArg"},
+ Run: emptyRun,
+ }
+ childCmd := &Command{
+ Use: "childCmd",
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"subArg"}, ShellCompDirectiveNoFileComp
+ },
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(childCmd)
+
+ rootCmd.Flags().IntP("requiredFlag", "r", -1, "required flag")
+ assertNoErr(t, rootCmd.MarkFlagRequired("requiredFlag"))
+ requiredFlag := rootCmd.Flags().Lookup("requiredFlag")
+
+ rootCmd.PersistentFlags().IntP("requiredPersistent", "p", -1, "required persistent")
+ assertNoErr(t, rootCmd.MarkPersistentFlagRequired("requiredPersistent"))
+ requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent")
+
+ rootCmd.Flags().StringP("release", "R", "", "Release name")
+
+ childCmd.Flags().BoolP("subRequired", "s", false, "sub required flag")
+ assertNoErr(t, childCmd.MarkFlagRequired("subRequired"))
+ childCmd.Flags().BoolP("subNotRequired", "n", false, "sub not required flag")
+
+ // Test that a required flag is suggested even without the - prefix
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "childCmd",
+ "completion",
+ "help",
+ "--requiredFlag",
+ "-r",
+ "--requiredPersistent",
+ "-p",
+ "realArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that a required flag is suggested without other flags when using the '-' prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--requiredFlag",
+ "-r",
+ "--requiredPersistent",
+ "-p",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that if no required flag matches, the normal flags are suggested
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--relea")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--release",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test required flags for sub-commands
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--requiredPersistent",
+ "-p",
+ "--subRequired",
+ "-s",
+ "subArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--requiredPersistent",
+ "-p",
+ "--subRequired",
+ "-s",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "--subNot")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--subNotRequired",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that when a required flag is present, it is not suggested anymore
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ requiredFlag.Changed = false
+
+ expected = strings.Join([]string{
+ "--requiredPersistent",
+ "-p",
+ "realArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that when a persistent required flag is present, it is not suggested anymore
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredPersistent", "1", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flag for the next command
+ requiredPersistent.Changed = false
+
+ expected = strings.Join([]string{
+ "childCmd",
+ "completion",
+ "help",
+ "--requiredFlag",
+ "-r",
+ "realArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that when all required flags are present, normal completion is done
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "--requiredPersistent", "1", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ // Reset the flags for the next command
+ requiredFlag.Changed = false
+ requiredPersistent.Changed = false
+
+ expected = strings.Join([]string{
+ "realArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestFlagFileExtFilterCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+
+ // No extensions. Should be ignored.
+ rootCmd.Flags().StringP("file", "f", "", "file flag")
+ assertNoErr(t, rootCmd.MarkFlagFilename("file"))
+
+ // Single extension
+ rootCmd.Flags().StringP("log", "l", "", "log flag")
+ assertNoErr(t, rootCmd.MarkFlagFilename("log", "log"))
+
+ // Multiple extensions
+ rootCmd.Flags().StringP("yaml", "y", "", "yaml flag")
+ assertNoErr(t, rootCmd.MarkFlagFilename("yaml", "yaml", "yml"))
+
+ // Directly using annotation
+ rootCmd.Flags().StringP("text", "t", "", "text flag")
+ assertNoErr(t, rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"}))
+
+ // Test that the completion logic returns the proper info for the completion
+ // script to handle the file filtering
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--file", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--log", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "log",
+ ":8",
+ "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "yaml", "yml",
+ ":8",
+ "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml=")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "yaml", "yml",
+ ":8",
+ "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "yaml", "yml",
+ ":8",
+ "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y=")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "yaml", "yml",
+ ":8",
+ "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--text", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "txt",
+ ":8",
+ "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestFlagDirFilterCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+
+ // Filter directories
+ rootCmd.Flags().StringP("dir", "d", "", "dir flag")
+ assertNoErr(t, rootCmd.MarkFlagDirname("dir"))
+
+ // Filter directories within a directory
+ rootCmd.Flags().StringP("subdir", "s", "", "subdir")
+ assertNoErr(t, rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"}))
+
+ // Multiple directory specification get ignored
+ rootCmd.Flags().StringP("manydir", "m", "", "manydir")
+ assertNoErr(t, rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"}))
+
+ // Test that the completion logic returns the proper info for the completion
+ // script to handle the directory filtering
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--dir", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ ":16",
+ "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-d", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":16",
+ "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "themes",
+ ":16",
+ "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir=")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "themes",
+ ":16",
+ "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "themes",
+ ":16",
+ "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s=")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "themes",
+ ":16",
+ "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--manydir", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":16",
+ "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsFuncCmdContext(t *testing.T) {
+ validArgsFunc := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ ctx := cmd.Context()
+
+ if ctx == nil {
+ t.Error("Received nil context in completion func")
+ } else if ctx.Value("testKey") != "123" {
+ t.Error("Received invalid context")
+ }
+
+ return nil, ShellCompDirectiveDefault
+ }
+
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ childCmd := &Command{
+ Use: "childCmd",
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(childCmd)
+
+ //nolint:golint,staticcheck // We can safely use a basic type as key in tests.
+ ctx := context.WithValue(context.Background(), "testKey", "123")
+
+ // Test completing an empty string on the childCmd
+ _, output, err := executeCommandWithContextC(ctx, rootCmd, ShellCompNoDescRequestCmd, "childCmd", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsFuncSingleCmd(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+
+ // Test completing an empty string
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "one",
+ "two",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with a prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "two",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsFuncSingleCmdInvalidArg(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ // If we don't specify a value for Args, this test fails.
+ // This is only true for a root command without any subcommands, and is caused
+ // by the fact that the __complete command becomes a subcommand when there should not be one.
+ // The problem is in the implementation of legacyArgs().
+ Args: MinimumNArgs(1),
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+
+ // Check completing with wrong number of args
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "unexpectedArg", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsFuncChildCmds(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ child1Cmd := &Command{
+ Use: "child1",
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+ child2Cmd := &Command{
+ Use: "child2",
+ ValidArgsFunction: validArgsFunc2,
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(child1Cmd, child2Cmd)
+
+ // Test completion of first sub-command with empty argument
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "one",
+ "two",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test completion of first sub-command with a prefix to complete
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "two",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with wrong number of args
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "unexpectedArg", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test completion of second sub-command with empty argument
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "three",
+ "four",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "three",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with wrong number of args
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "unexpectedArg", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsFuncAliases(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ child := &Command{
+ Use: "child",
+ Aliases: []string{"son", "daughter"},
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(child)
+
+ // Test completion of first sub-command with empty argument
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "one",
+ "two",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test completion of first sub-command with a prefix to complete
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "daughter", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "two",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with wrong number of args
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "unexpectedArg", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsFuncInBashScript(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ child := &Command{
+ Use: "child",
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(child)
+
+ buf := new(bytes.Buffer)
+ assertNoErr(t, rootCmd.GenBashCompletion(buf))
+ output := buf.String()
+
+ check(t, output, "has_completion_function=1")
+}
+
+func TestNoValidArgsFuncInBashScript(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ child := &Command{
+ Use: "child",
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(child)
+
+ buf := new(bytes.Buffer)
+ assertNoErr(t, rootCmd.GenBashCompletion(buf))
+ output := buf.String()
+
+ checkOmit(t, output, "has_completion_function=1")
+}
+
+func TestCompleteCmdInBashScript(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ child := &Command{
+ Use: "child",
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(child)
+
+ buf := new(bytes.Buffer)
+ assertNoErr(t, rootCmd.GenBashCompletion(buf))
+ output := buf.String()
+
+ check(t, output, ShellCompNoDescRequestCmd)
+}
+
+func TestCompleteNoDesCmdInZshScript(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ child := &Command{
+ Use: "child",
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(child)
+
+ buf := new(bytes.Buffer)
+ assertNoErr(t, rootCmd.GenZshCompletionNoDesc(buf))
+ output := buf.String()
+
+ check(t, output, ShellCompNoDescRequestCmd)
+}
+
+func TestCompleteCmdInZshScript(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ child := &Command{
+ Use: "child",
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(child)
+
+ buf := new(bytes.Buffer)
+ assertNoErr(t, rootCmd.GenZshCompletion(buf))
+ output := buf.String()
+
+ check(t, output, ShellCompRequestCmd)
+ checkOmit(t, output, ShellCompNoDescRequestCmd)
+}
+
+func TestFlagCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
+ assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ completions := []string{}
+ for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
+ if strings.HasPrefix(comp, toComplete) {
+ completions = append(completions, comp)
+ }
+ }
+ return completions, ShellCompDirectiveDefault
+ }))
+ rootCmd.Flags().String("filename", "", "Enter a filename")
+ assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ completions := []string{}
+ for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
+ if strings.HasPrefix(comp, toComplete) {
+ completions = append(completions, comp)
+ }
+ }
+ return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
+ }))
+
+ // Test completing an empty string
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "1",
+ "2",
+ "10",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with a prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "1")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "1",
+ "10",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test completing an empty string
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "file.yaml",
+ "myfile.json",
+ "file.xml",
+ ":6",
+ "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with a prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "f")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "file.yaml",
+ "file.xml",
+ ":6",
+ "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ child1Cmd := &Command{
+ Use: "child1",
+ ValidArgsFunction: validArgsFunc,
+ Run: emptyRun,
+ }
+ child2Cmd := &Command{
+ Use: "child2",
+ ValidArgsFunction: validArgsFunc2,
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(child1Cmd, child2Cmd)
+
+ // Test completion of first sub-command with empty argument
+ output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child1", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "one\tThe first",
+ "two\tThe second",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test completion of first sub-command with a prefix to complete
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "two\tThe second",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with wrong number of args
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "unexpectedArg", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test completion of second sub-command with empty argument
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "three\tThe third",
+ "four\tThe fourth",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "three\tThe third",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with wrong number of args
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "unexpectedArg", "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) {
+ rootCmd := &Command{Use: "root", Run: emptyRun}
+ childCmd := &Command{
+ Use: "child",
+ Run: emptyRun,
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"--validarg", "test"}, ShellCompDirectiveDefault
+ },
+ }
+ childCmd2 := &Command{
+ Use: "child2",
+ Run: emptyRun,
+ ValidArgs: []string{"arg1", "arg2"},
+ }
+ rootCmd.AddCommand(childCmd, childCmd2)
+ childCmd.Flags().Bool("bool", false, "test bool flag")
+ childCmd.Flags().String("string", "", "test string flag")
+ _ = childCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"myval"}, ShellCompDirectiveDefault
+ })
+
+ // Test flag completion with no argument
+ output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "--bool\ttest bool flag",
+ "--help\thelp for child",
+ "--string\ttest string flag",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that no flags are completed after the -- arg
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--validarg",
+ "test",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that no flags are completed after the -- arg with a flag set
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--bool", "--", "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--validarg",
+ "test",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // set Interspersed to false which means that no flags should be completed after the first arg
+ childCmd.Flags().SetInterspersed(false)
+
+ // Test that no flags are completed after the first arg
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "arg", "--")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--validarg",
+ "test",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that no flags are completed after the fist arg with a flag set
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "t", "arg", "--")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--validarg",
+ "test",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check that args are still completed after --
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--validarg",
+ "test",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check that args are still completed even if flagname with ValidArgsFunction exists
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--string", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--validarg",
+ "test",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check that args are still completed even if flagname with ValidArgsFunction exists
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "--", "a")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "arg1",
+ "arg2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check that --validarg is not parsed as flag after --
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--validarg",
+ "test",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check that --validarg is not parsed as flag after an arg
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "arg", "--validarg", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--validarg",
+ "test",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check that --validarg is added to args for the ValidArgsFunction
+ childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return args, ShellCompDirectiveDefault
+ }
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--validarg",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check that --validarg is added to args for the ValidArgsFunction and toComplete is also set correctly
+ childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return append(args, toComplete), ShellCompDirectiveDefault
+ }
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "--toComp=ab")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "--validarg",
+ "--toComp=ab",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) {
+ rootCmd := &Command{Use: "root", Run: emptyRun}
+ childCmd := &Command{
+ Use: "child",
+ Run: emptyRun,
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"--validarg", "test"}, ShellCompDirectiveDefault
+ },
+ }
+ childCmd.Flags().Bool("bool", false, "test bool flag")
+ childCmd.Flags().String("string", "", "test string flag")
+ _ = childCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"myval"}, ShellCompDirectiveDefault
+ })
+
+ // Important: This is a test for https://github.com/spf13/cobra/issues/1437
+ // Only add the subcommand after RegisterFlagCompletionFunc was called, do not change this order!
+ rootCmd.AddCommand(childCmd)
+
+ // Test that flag completion works for the subcmd
+ output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "myval",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestFlagCompletionForPersistentFlagsCalledFromSubCmd(t *testing.T) {
+ rootCmd := &Command{Use: "root", Run: emptyRun}
+ rootCmd.PersistentFlags().String("string", "", "test string flag")
+ _ = rootCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"myval"}, ShellCompDirectiveDefault
+ })
+
+ childCmd := &Command{
+ Use: "child",
+ Run: emptyRun,
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"--validarg", "test"}, ShellCompDirectiveDefault
+ },
+ }
+ childCmd.Flags().Bool("bool", false, "test bool flag")
+ rootCmd.AddCommand(childCmd)
+
+ // Test that persistent flag completion works for the subcmd
+ output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "myval",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+// This test tries to register flag completion concurrently to make sure the
+// code handles concurrency properly.
+// This was reported as a problem when tests are run concurrently:
+// https://github.com/spf13/cobra/issues/1320
+//
+// NOTE: this test can sometimes pass even if the code were to not handle
+// concurrency properly. This is not great but the important part is that
+// it should never fail. Therefore, if the tests fails sometimes, we will
+// still be able to know there is a problem.
+func TestFlagCompletionConcurrentRegistration(t *testing.T) {
+ rootCmd := &Command{Use: "root", Run: emptyRun}
+ const maxFlags = 50
+ for i := 1; i < maxFlags; i += 2 {
+ flagName := fmt.Sprintf("flag%d", i)
+ rootCmd.Flags().String(flagName, "", fmt.Sprintf("test %s flag on root", flagName))
+ }
+
+ childCmd := &Command{
+ Use: "child",
+ Run: emptyRun,
+ }
+ for i := 2; i <= maxFlags; i += 2 {
+ flagName := fmt.Sprintf("flag%d", i)
+ childCmd.Flags().String(flagName, "", fmt.Sprintf("test %s flag on child", flagName))
+ }
+
+ rootCmd.AddCommand(childCmd)
+
+ // Register completion in different threads to test concurrency.
+ var wg sync.WaitGroup
+ for i := 1; i <= maxFlags; i++ {
+ index := i
+ flagName := fmt.Sprintf("flag%d", i)
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ cmd := rootCmd
+ if index%2 == 0 {
+ cmd = childCmd
+ }
+ _ = cmd.RegisterFlagCompletionFunc(flagName, func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{fmt.Sprintf("flag%d", index)}, ShellCompDirectiveDefault
+ })
+ }()
+ }
+
+ wg.Wait()
+
+ // Test that flag completion works for each flag
+ for i := 1; i <= 6; i++ {
+ var output string
+ var err error
+ flagName := fmt.Sprintf("flag%d", i)
+
+ if i%2 == 1 {
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--"+flagName, "")
+ } else {
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--"+flagName, "")
+ }
+
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ flagName,
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+ }
+}
+
+func TestFlagCompletionInGoWithDesc(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
+ assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ completions := []string{}
+ for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
+ if strings.HasPrefix(comp, toComplete) {
+ completions = append(completions, comp)
+ }
+ }
+ return completions, ShellCompDirectiveDefault
+ }))
+ rootCmd.Flags().String("filename", "", "Enter a filename")
+ assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ completions := []string{}
+ for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
+ if strings.HasPrefix(comp, toComplete) {
+ completions = append(completions, comp)
+ }
+ }
+ return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
+ }))
+
+ // Test completing an empty string
+ output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "1\tThe first",
+ "2\tThe second",
+ "10\tThe tenth",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with a prefix
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "1")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "1\tThe first",
+ "10\tThe tenth",
+ ":0",
+ "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test completing an empty string
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "file.yaml\tYAML format",
+ "myfile.json\tJSON format",
+ "file.xml\tXML format",
+ ":6",
+ "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with a prefix
+ output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "f")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "file.yaml\tYAML format",
+ "file.xml\tXML format",
+ ":6",
+ "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestValidArgsNotValidArgsFunc(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ ValidArgs: []string{"one", "two"},
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"three", "four"}, ShellCompDirectiveNoFileComp
+ },
+ Run: emptyRun,
+ }
+
+ // Test that if both ValidArgs and ValidArgsFunction are present
+ // only ValidArgs is considered
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "one",
+ "two",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Check completing with a prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "two",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestArgAliasesCompletionInGo(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Args: OnlyValidArgs,
+ ValidArgs: []string{"one", "two", "three"},
+ ArgAliases: []string{"un", "deux", "trois"},
+ Run: emptyRun,
+ }
+
+ // Test that argaliases are not completed when there are validargs that match
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "one",
+ "two",
+ "three",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that argaliases are not completed when there are validargs that match using a prefix
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "two",
+ "three",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that argaliases are completed when there are no validargs that match
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "tr")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "trois",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestCompleteHelp(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ child1Cmd := &Command{
+ Use: "child1",
+ Run: emptyRun,
+ }
+ child2Cmd := &Command{
+ Use: "child2",
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(child1Cmd, child2Cmd)
+
+ child3Cmd := &Command{
+ Use: "child3",
+ Run: emptyRun,
+ }
+ child1Cmd.AddCommand(child3Cmd)
+
+ // Test that completion includes the help command
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "child1",
+ "child2",
+ "completion",
+ "help",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test sub-commands are completed on first level of help command
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "child1",
+ "child2",
+ "completion",
+ "help", // "<program> help help" is a valid command, so should be completed
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test sub-commands are completed on first level of help command
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "child1", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "child3",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func removeCompCmd(rootCmd *Command) {
+ // Remove completion command for the next test
+ for _, cmd := range rootCmd.commands {
+ if cmd.Name() == compCmdName {
+ rootCmd.RemoveCommand(cmd)
+ return
+ }
+ }
+}
+
+func TestDefaultCompletionCmd(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ Args: NoArgs,
+ Run: emptyRun,
+ }
+
+ // Test that no completion command is created if there are not other sub-commands
+ assertNoErr(t, rootCmd.Execute())
+ for _, cmd := range rootCmd.commands {
+ if cmd.Name() == compCmdName {
+ t.Errorf("Should not have a 'completion' command when there are no other sub-commands of root")
+ break
+ }
+ }
+
+ subCmd := &Command{
+ Use: "sub",
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(subCmd)
+
+ // Test that a completion command is created if there are other sub-commands
+ found := false
+ assertNoErr(t, rootCmd.Execute())
+ for _, cmd := range rootCmd.commands {
+ if cmd.Name() == compCmdName {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("Should have a 'completion' command when there are other sub-commands of root")
+ }
+ // Remove completion command for the next test
+ removeCompCmd(rootCmd)
+
+ // Test that the default completion command can be disabled
+ rootCmd.CompletionOptions.DisableDefaultCmd = true
+ assertNoErr(t, rootCmd.Execute())
+ for _, cmd := range rootCmd.commands {
+ if cmd.Name() == compCmdName {
+ t.Errorf("Should not have a 'completion' command when the feature is disabled")
+ break
+ }
+ }
+ // Re-enable for next test
+ rootCmd.CompletionOptions.DisableDefaultCmd = false
+
+ // Test that completion descriptions are enabled by default
+ output, err := executeCommand(rootCmd, compCmdName, "zsh")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ check(t, output, ShellCompRequestCmd)
+ checkOmit(t, output, ShellCompNoDescRequestCmd)
+ // Remove completion command for the next test
+ removeCompCmd(rootCmd)
+
+ // Test that completion descriptions can be disabled completely
+ rootCmd.CompletionOptions.DisableDescriptions = true
+ output, err = executeCommand(rootCmd, compCmdName, "zsh")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ check(t, output, ShellCompNoDescRequestCmd)
+ // Re-enable for next test
+ rootCmd.CompletionOptions.DisableDescriptions = false
+ // Remove completion command for the next test
+ removeCompCmd(rootCmd)
+
+ var compCmd *Command
+ // Test that the --no-descriptions flag is present on all shells
+ assertNoErr(t, rootCmd.Execute())
+ for _, shell := range []string{"bash", "fish", "powershell", "zsh"} {
+ if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag == nil {
+ t.Errorf("Missing --%s flag for %s shell", compCmdNoDescFlagName, shell)
+ }
+ }
+ // Remove completion command for the next test
+ removeCompCmd(rootCmd)
+
+ // Test that the '--no-descriptions' flag can be disabled
+ rootCmd.CompletionOptions.DisableNoDescFlag = true
+ assertNoErr(t, rootCmd.Execute())
+ for _, shell := range []string{"fish", "zsh", "bash", "powershell"} {
+ if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil {
+ t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell)
+ }
+ }
+ // Re-enable for next test
+ rootCmd.CompletionOptions.DisableNoDescFlag = false
+ // Remove completion command for the next test
+ removeCompCmd(rootCmd)
+
+ // Test that the '--no-descriptions' flag is disabled when descriptions are disabled
+ rootCmd.CompletionOptions.DisableDescriptions = true
+ assertNoErr(t, rootCmd.Execute())
+ for _, shell := range []string{"fish", "zsh", "bash", "powershell"} {
+ if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil {
+ t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell)
+ }
+ }
+ // Re-enable for next test
+ rootCmd.CompletionOptions.DisableDescriptions = false
+ // Remove completion command for the next test
+ removeCompCmd(rootCmd)
+
+ // Test that the 'completion' command can be hidden
+ rootCmd.CompletionOptions.HiddenDefaultCmd = true
+ assertNoErr(t, rootCmd.Execute())
+ compCmd, _, err = rootCmd.Find([]string{compCmdName})
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if compCmd.Hidden == false {
+ t.Error("Default 'completion' command should be hidden but it is not")
+ }
+ // Re-enable for next test
+ rootCmd.CompletionOptions.HiddenDefaultCmd = false
+ // Remove completion command for the next test
+ removeCompCmd(rootCmd)
+}
+
+func TestCompleteCompletion(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ subCmd := &Command{
+ Use: "sub",
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(subCmd)
+
+ // Test sub-commands of the completion command
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "completion", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "bash",
+ "fish",
+ "powershell",
+ "zsh",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test there are no completions for the sub-commands of the completion command
+ var compCmd *Command
+ for _, cmd := range rootCmd.Commands() {
+ if cmd.Name() == compCmdName {
+ compCmd = cmd
+ break
+ }
+ }
+
+ for _, shell := range compCmd.Commands() {
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, compCmdName, shell.Name(), "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+ }
+}
+
+func TestMultipleShorthandFlagCompletion(t *testing.T) {
+ rootCmd := &Command{
+ Use: "root",
+ ValidArgs: []string{"foo", "bar"},
+ Run: emptyRun,
+ }
+ f := rootCmd.Flags()
+ f.BoolP("short", "s", false, "short flag 1")
+ f.BoolP("short2", "d", false, "short flag 2")
+ f.StringP("short3", "f", "", "short flag 3")
+ _ = rootCmd.RegisterFlagCompletionFunc("short3", func(*Command, []string, string) ([]string, ShellCompDirective) {
+ return []string{"works"}, ShellCompDirectiveNoFileComp
+ })
+
+ // Test that a single shorthand flag works
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "foo",
+ "bar",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that multiple boolean shorthand flags work
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sd", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "foo",
+ "bar",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that multiple boolean + string shorthand flags work
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "works",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that multiple boolean + string with equal sign shorthand flags work
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "works",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that multiple boolean + string with equal sign with value shorthand flags work
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=abc", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "foo",
+ "bar",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestCompleteWithDisableFlagParsing(t *testing.T) {
+
+ flagValidArgs := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"--flag", "-f"}, ShellCompDirectiveNoFileComp
+ }
+
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ childCmd := &Command{
+ Use: "child",
+ Run: emptyRun,
+ DisableFlagParsing: true,
+ ValidArgsFunction: flagValidArgs,
+ }
+ rootCmd.AddCommand(childCmd)
+
+ rootCmd.PersistentFlags().StringP("persistent", "p", "", "persistent flag")
+ childCmd.Flags().StringP("nonPersistent", "n", "", "non-persistent flag")
+
+ // Test that when DisableFlagParsing==true, ValidArgsFunction is called to complete flag names,
+ // after Cobra tried to complete the flags it knows about.
+ childCmd.DisableFlagParsing = true
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "--persistent",
+ "-p",
+ "--nonPersistent",
+ "-n",
+ "--flag",
+ "-f",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Test that when DisableFlagParsing==false, Cobra completes the flags itself and ValidArgsFunction is not called
+ childCmd.DisableFlagParsing = false
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "-")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ // Cobra was not told of any flags, so it returns nothing
+ expected = strings.Join([]string{
+ "--persistent",
+ "-p",
+ "--help",
+ "-h",
+ "--nonPersistent",
+ "-n",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestCompleteWithRootAndLegacyArgs(t *testing.T) {
+ // Test a lonely root command which uses legacyArgs(). In such a case, the root
+ // command should accept any number of arguments and completion should behave accordingly.
+ rootCmd := &Command{
+ Use: "root",
+ Args: nil, // Args must be nil to trigger the legacyArgs() function
+ Run: emptyRun,
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"arg1", "arg2"}, ShellCompDirectiveNoFileComp
+ },
+ }
+
+ // Make sure the first arg is completed
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "arg1",
+ "arg2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+
+ // Make sure the completion of arguments continues
+ output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "arg1", "")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected = strings.Join([]string{
+ "arg1",
+ "arg2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestFixedCompletions(t *testing.T) {
+ rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+ choices := []string{"apple", "banana", "orange"}
+ childCmd := &Command{
+ Use: "child",
+ ValidArgsFunction: FixedCompletions(choices, ShellCompDirectiveNoFileComp),
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(childCmd)
+
+ output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "a")
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ expected := strings.Join([]string{
+ "apple",
+ "banana",
+ "orange",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+ if output != expected {
+ t.Errorf("expected: %q, got: %q", expected, output)
+ }
+}
+
+func TestCompletionForGroupedFlags(t *testing.T) {
+ getCmd := func() *Command {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ childCmd := &Command{
+ Use: "child",
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"subArg"}, ShellCompDirectiveNoFileComp
+ },
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(childCmd)
+
+ rootCmd.PersistentFlags().Int("ingroup1", -1, "ingroup1")
+ rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2")
+
+ childCmd.Flags().Bool("ingroup3", false, "ingroup3")
+ childCmd.Flags().Bool("nogroup", false, "nogroup")
+
+ // Add flags to a group
+ childCmd.MarkFlagsRequiredTogether("ingroup1", "ingroup2", "ingroup3")
+
+ return rootCmd
+ }
+
+ // Each test case uses a unique command from the function above.
+ testcases := []struct {
+ desc string
+ args []string
+ expectedOutput string
+ }{
+ {
+ desc: "flags in group not suggested without - prefix",
+ args: []string{"child", ""},
+ expectedOutput: strings.Join([]string{
+ "subArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "flags in group suggested with - prefix",
+ args: []string{"child", "-"},
+ expectedOutput: strings.Join([]string{
+ "--ingroup1",
+ "--ingroup2",
+ "--help",
+ "-h",
+ "--ingroup3",
+ "--nogroup",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "when flag in group present, other flags in group suggested even without - prefix",
+ args: []string{"child", "--ingroup2", "value", ""},
+ expectedOutput: strings.Join([]string{
+ "--ingroup1",
+ "--ingroup3",
+ "subArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "when all flags in group present, flags not suggested without - prefix",
+ args: []string{"child", "--ingroup1", "8", "--ingroup2", "value2", "--ingroup3", ""},
+ expectedOutput: strings.Join([]string{
+ "subArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "group ignored if some flags not applicable",
+ args: []string{"--ingroup2", "value", ""},
+ expectedOutput: strings.Join([]string{
+ "child",
+ "completion",
+ "help",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.desc, func(t *testing.T) {
+ c := getCmd()
+ args := []string{ShellCompNoDescRequestCmd}
+ args = append(args, tc.args...)
+ output, err := executeCommand(c, args...)
+ switch {
+ case err == nil && output != tc.expectedOutput:
+ t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
+ case err != nil:
+ t.Errorf("Unexpected error %q", err)
+ }
+ })
+ }
+}
+
+func TestCompletionForOneRequiredGroupFlags(t *testing.T) {
+ getCmd := func() *Command {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ childCmd := &Command{
+ Use: "child",
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"subArg"}, ShellCompDirectiveNoFileComp
+ },
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(childCmd)
+
+ rootCmd.PersistentFlags().Int("ingroup1", -1, "ingroup1")
+ rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2")
+
+ childCmd.Flags().Bool("ingroup3", false, "ingroup3")
+ childCmd.Flags().Bool("nogroup", false, "nogroup")
+
+ // Add flags to a group
+ childCmd.MarkFlagsOneRequired("ingroup1", "ingroup2", "ingroup3")
+
+ return rootCmd
+ }
+
+ // Each test case uses a unique command from the function above.
+ testcases := []struct {
+ desc string
+ args []string
+ expectedOutput string
+ }{
+ {
+ desc: "flags in group suggested without - prefix",
+ args: []string{"child", ""},
+ expectedOutput: strings.Join([]string{
+ "--ingroup1",
+ "--ingroup2",
+ "--ingroup3",
+ "subArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "flags in group suggested with - prefix",
+ args: []string{"child", "-"},
+ expectedOutput: strings.Join([]string{
+ "--ingroup1",
+ "--ingroup2",
+ "--ingroup3",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "when any flag in group present, other flags in group not suggested without - prefix",
+ args: []string{"child", "--ingroup2", "value", ""},
+ expectedOutput: strings.Join([]string{
+ "subArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "when all flags in group present, flags not suggested without - prefix",
+ args: []string{"child", "--ingroup1", "8", "--ingroup2", "value2", "--ingroup3", ""},
+ expectedOutput: strings.Join([]string{
+ "subArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "group ignored if some flags not applicable",
+ args: []string{"--ingroup2", "value", ""},
+ expectedOutput: strings.Join([]string{
+ "child",
+ "completion",
+ "help",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.desc, func(t *testing.T) {
+ c := getCmd()
+ args := []string{ShellCompNoDescRequestCmd}
+ args = append(args, tc.args...)
+ output, err := executeCommand(c, args...)
+ switch {
+ case err == nil && output != tc.expectedOutput:
+ t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
+ case err != nil:
+ t.Errorf("Unexpected error %q", err)
+ }
+ })
+ }
+}
+
+func TestCompletionForMutuallyExclusiveFlags(t *testing.T) {
+ getCmd := func() *Command {
+ rootCmd := &Command{
+ Use: "root",
+ Run: emptyRun,
+ }
+ childCmd := &Command{
+ Use: "child",
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"subArg"}, ShellCompDirectiveNoFileComp
+ },
+ Run: emptyRun,
+ }
+ rootCmd.AddCommand(childCmd)
+
+ rootCmd.PersistentFlags().IntSlice("ingroup1", []int{1}, "ingroup1")
+ rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2")
+
+ childCmd.Flags().Bool("ingroup3", false, "ingroup3")
+ childCmd.Flags().Bool("nogroup", false, "nogroup")
+
+ // Add flags to a group
+ childCmd.MarkFlagsMutuallyExclusive("ingroup1", "ingroup2", "ingroup3")
+
+ return rootCmd
+ }
+
+ // Each test case uses a unique command from the function above.
+ testcases := []struct {
+ desc string
+ args []string
+ expectedOutput string
+ }{
+ {
+ desc: "flags in mutually exclusive group not suggested without the - prefix",
+ args: []string{"child", ""},
+ expectedOutput: strings.Join([]string{
+ "subArg",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "flags in mutually exclusive group suggested with the - prefix",
+ args: []string{"child", "-"},
+ expectedOutput: strings.Join([]string{
+ "--ingroup1",
+ "--ingroup2",
+ "--help",
+ "-h",
+ "--ingroup3",
+ "--nogroup",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "when flag in mutually exclusive group present, other flags in group not suggested even with the - prefix",
+ args: []string{"child", "--ingroup1", "8", "-"},
+ expectedOutput: strings.Join([]string{
+ "--ingroup1", // Should be suggested again since it is a slice
+ "--help",
+ "-h",
+ "--nogroup",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "group ignored if some flags not applicable",
+ args: []string{"--ingroup1", "8", "-"},
+ expectedOutput: strings.Join([]string{
+ "--help",
+ "-h",
+ "--ingroup1",
+ "--ingroup2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.desc, func(t *testing.T) {
+ c := getCmd()
+ args := []string{ShellCompNoDescRequestCmd}
+ args = append(args, tc.args...)
+ output, err := executeCommand(c, args...)
+ switch {
+ case err == nil && output != tc.expectedOutput:
+ t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
+ case err != nil:
+ t.Errorf("Unexpected error %q", err)
+ }
+ })
+ }
+}
+
+func TestCompletionCobraFlags(t *testing.T) {
+ getCmd := func() *Command {
+ rootCmd := &Command{
+ Use: "root",
+ Version: "1.1.1",
+ Run: emptyRun,
+ }
+ childCmd := &Command{
+ Use: "child",
+ Version: "1.1.1",
+ Run: emptyRun,
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"extra"}, ShellCompDirectiveNoFileComp
+ },
+ }
+ childCmd2 := &Command{
+ Use: "child2",
+ Version: "1.1.1",
+ Run: emptyRun,
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"extra2"}, ShellCompDirectiveNoFileComp
+ },
+ }
+ childCmd3 := &Command{
+ Use: "child3",
+ Version: "1.1.1",
+ Run: emptyRun,
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"extra3"}, ShellCompDirectiveNoFileComp
+ },
+ }
+ childCmd4 := &Command{
+ Use: "child4",
+ Version: "1.1.1",
+ Run: emptyRun,
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"extra4"}, ShellCompDirectiveNoFileComp
+ },
+ DisableFlagParsing: true,
+ }
+ childCmd5 := &Command{
+ Use: "child5",
+ Version: "1.1.1",
+ Run: emptyRun,
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"extra5"}, ShellCompDirectiveNoFileComp
+ },
+ DisableFlagParsing: true,
+ }
+
+ rootCmd.AddCommand(childCmd, childCmd2, childCmd3, childCmd4, childCmd5)
+
+ _ = childCmd.Flags().Bool("bool", false, "A bool flag")
+ _ = childCmd.MarkFlagRequired("bool")
+
+ // Have a command that adds its own help and version flag
+ _ = childCmd2.Flags().BoolP("help", "h", false, "My own help")
+ _ = childCmd2.Flags().BoolP("version", "v", false, "My own version")
+
+ // Have a command that only adds its own -v flag
+ _ = childCmd3.Flags().BoolP("verbose", "v", false, "Not a version flag")
+
+ // Have a command that DisablesFlagParsing but that also adds its own help and version flags
+ _ = childCmd5.Flags().BoolP("help", "h", false, "My own help")
+ _ = childCmd5.Flags().BoolP("version", "v", false, "My own version")
+
+ return rootCmd
+ }
+
+ // Each test case uses a unique command from the function above.
+ testcases := []struct {
+ desc string
+ args []string
+ expectedOutput string
+ }{
+ {
+ desc: "completion of help and version flags",
+ args: []string{"-"},
+ expectedOutput: strings.Join([]string{
+ "--help",
+ "-h",
+ "--version",
+ "-v",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "no completion after --help flag",
+ args: []string{"--help", ""},
+ expectedOutput: strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "no completion after -h flag",
+ args: []string{"-h", ""},
+ expectedOutput: strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "no completion after --version flag",
+ args: []string{"--version", ""},
+ expectedOutput: strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "no completion after -v flag",
+ args: []string{"-v", ""},
+ expectedOutput: strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "no completion after --help flag even with other completions",
+ args: []string{"child", "--help", ""},
+ expectedOutput: strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "no completion after -h flag even with other completions",
+ args: []string{"child", "-h", ""},
+ expectedOutput: strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "no completion after --version flag even with other completions",
+ args: []string{"child", "--version", ""},
+ expectedOutput: strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "no completion after -v flag even with other completions",
+ args: []string{"child", "-v", ""},
+ expectedOutput: strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "no completion after -v flag even with other flag completions",
+ args: []string{"child", "-v", "-"},
+ expectedOutput: strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "completion after --help flag when created by program",
+ args: []string{"child2", "--help", ""},
+ expectedOutput: strings.Join([]string{
+ "extra2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "completion after -h flag when created by program",
+ args: []string{"child2", "-h", ""},
+ expectedOutput: strings.Join([]string{
+ "extra2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "completion after --version flag when created by program",
+ args: []string{"child2", "--version", ""},
+ expectedOutput: strings.Join([]string{
+ "extra2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "completion after -v flag when created by program",
+ args: []string{"child2", "-v", ""},
+ expectedOutput: strings.Join([]string{
+ "extra2",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "completion after --version when only -v flag was created by program",
+ args: []string{"child3", "--version", ""},
+ expectedOutput: strings.Join([]string{
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "completion after -v flag when only -v flag was created by program",
+ args: []string{"child3", "-v", ""},
+ expectedOutput: strings.Join([]string{
+ "extra3",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "no completion for --help/-h and --version/-v flags when DisableFlagParsing=true",
+ args: []string{"child4", "-"},
+ expectedOutput: strings.Join([]string{
+ "extra4",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ {
+ desc: "completions for program-defined --help/-h and --version/-v flags even when DisableFlagParsing=true",
+ args: []string{"child5", "-"},
+ expectedOutput: strings.Join([]string{
+ "--help",
+ "-h",
+ "--version",
+ "-v",
+ "extra5",
+ ":4",
+ "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.desc, func(t *testing.T) {
+ c := getCmd()
+ args := []string{ShellCompNoDescRequestCmd}
+ args = append(args, tc.args...)
+ output, err := executeCommand(c, args...)
+ switch {
+ case err == nil && output != tc.expectedOutput:
+ t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
+ case err != nil:
+ t.Errorf("Unexpected error %q", err)
+ }
+ })
+ }
+}
+
+func TestArgsNotDetectedAsFlagsCompletionInGo(t *testing.T) {
+ // Regression test that ensures the bug described in
+ // https://github.com/spf13/cobra/issues/1816 does not occur anymore.
+
+ root := Command{
+ Use: "root",
+ ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"service", "1-123", "11-123"}, ShellCompDirectiveNoFileComp
+ },
+ }
+
+ completion := `service
+1-123
+11-123
+:4
+Completion ended with directive: ShellCompDirectiveNoFileComp
+`
+
+ testcases := []struct {
+ desc string
+ args []string
+ expectedOutput string
+ }{
+ {
+ desc: "empty",
+ args: []string{""},
+ expectedOutput: completion,
+ },
+ {
+ desc: "service only",
+ args: []string{"service", ""},
+ expectedOutput: completion,
+ },
+ {
+ desc: "service last",
+ args: []string{"1-123", "service", ""},
+ expectedOutput: completion,
+ },
+ {
+ desc: "two digit prefixed dash last",
+ args: []string{"service", "11-123", ""},
+ expectedOutput: completion,
+ },
+ {
+ desc: "one digit prefixed dash last",
+ args: []string{"service", "1-123", ""},
+ expectedOutput: completion,
+ },
+ }
+ for _, tc := range testcases {
+ t.Run(tc.desc, func(t *testing.T) {
+ args := []string{ShellCompNoDescRequestCmd}
+ args = append(args, tc.args...)
+ output, err := executeCommand(&root, args...)
+ switch {
+ case err == nil && output != tc.expectedOutput:
+ t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
+ case err != nil:
+ t.Errorf("Unexpected error %q", err)
+ }
+ })
+ }
+}
+
+func TestGetFlagCompletion(t *testing.T) {
+ rootCmd := &Command{Use: "root", Run: emptyRun}
+
+ rootCmd.Flags().String("rootflag", "", "root flag")
+ _ = rootCmd.RegisterFlagCompletionFunc("rootflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"rootvalue"}, ShellCompDirectiveKeepOrder
+ })
+
+ rootCmd.PersistentFlags().String("persistentflag", "", "persistent flag")
+ _ = rootCmd.RegisterFlagCompletionFunc("persistentflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"persistentvalue"}, ShellCompDirectiveDefault
+ })
+
+ childCmd := &Command{Use: "child", Run: emptyRun}
+
+ childCmd.Flags().String("childflag", "", "child flag")
+ _ = childCmd.RegisterFlagCompletionFunc("childflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+ return []string{"childvalue"}, ShellCompDirectiveNoFileComp | ShellCompDirectiveNoSpace
+ })
+
+ rootCmd.AddCommand(childCmd)
+
+ testcases := []struct {
+ desc string
+ cmd *Command
+ flagName string
+ exists bool
+ comps []string
+ directive ShellCompDirective
+ }{
+ {
+ desc: "get flag completion function for command",
+ cmd: rootCmd,
+ flagName: "rootflag",
+ exists: true,
+ comps: []string{"rootvalue"},
+ directive: ShellCompDirectiveKeepOrder,
+ },
+ {
+ desc: "get persistent flag completion function for command",
+ cmd: rootCmd,
+ flagName: "persistentflag",
+ exists: true,
+ comps: []string{"persistentvalue"},
+ directive: ShellCompDirectiveDefault,
+ },
+ {
+ desc: "get flag completion function for child command",
+ cmd: childCmd,
+ flagName: "childflag",
+ exists: true,
+ comps: []string{"childvalue"},
+ directive: ShellCompDirectiveNoFileComp | ShellCompDirectiveNoSpace,
+ },
+ {
+ desc: "get persistent flag completion function for child command",
+ cmd: childCmd,
+ flagName: "persistentflag",
+ exists: true,
+ comps: []string{"persistentvalue"},
+ directive: ShellCompDirectiveDefault,
+ },
+ {
+ desc: "cannot get flag completion function for local parent flag",
+ cmd: childCmd,
+ flagName: "rootflag",
+ exists: false,
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.desc, func(t *testing.T) {
+ compFunc, exists := tc.cmd.GetFlagCompletionFunc(tc.flagName)
+ if tc.exists != exists {
+ t.Errorf("Unexpected result looking for flag completion function")
+ }
+
+ if exists {
+ comps, directive := compFunc(tc.cmd, []string{}, "")
+ if strings.Join(tc.comps, " ") != strings.Join(comps, " ") {
+ t.Errorf("Unexpected completions %q", comps)
+ }
+ if tc.directive != directive {
+ t.Errorf("Unexpected directive %q", directive)
+ }
+ }
+ })
+ }
+}