...

Source file src/github.com/spf13/cobra/completions_test.go

Documentation: github.com/spf13/cobra

     1  // Copyright 2013-2023 The Cobra Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cobra
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"fmt"
    21  	"strings"
    22  	"sync"
    23  	"testing"
    24  )
    25  
    26  func validArgsFunc(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
    27  	if len(args) != 0 {
    28  		return nil, ShellCompDirectiveNoFileComp
    29  	}
    30  
    31  	var completions []string
    32  	for _, comp := range []string{"one\tThe first", "two\tThe second"} {
    33  		if strings.HasPrefix(comp, toComplete) {
    34  			completions = append(completions, comp)
    35  		}
    36  	}
    37  	return completions, ShellCompDirectiveDefault
    38  }
    39  
    40  func validArgsFunc2(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
    41  	if len(args) != 0 {
    42  		return nil, ShellCompDirectiveNoFileComp
    43  	}
    44  
    45  	var completions []string
    46  	for _, comp := range []string{"three\tThe third", "four\tThe fourth"} {
    47  		if strings.HasPrefix(comp, toComplete) {
    48  			completions = append(completions, comp)
    49  		}
    50  	}
    51  	return completions, ShellCompDirectiveDefault
    52  }
    53  
    54  func TestCmdNameCompletionInGo(t *testing.T) {
    55  	rootCmd := &Command{
    56  		Use: "root",
    57  		Run: emptyRun,
    58  	}
    59  	childCmd1 := &Command{
    60  		Use:   "firstChild",
    61  		Short: "First command",
    62  		Run:   emptyRun,
    63  	}
    64  	childCmd2 := &Command{
    65  		Use: "secondChild",
    66  		Run: emptyRun,
    67  	}
    68  	hiddenCmd := &Command{
    69  		Use:    "testHidden",
    70  		Hidden: true, // Not completed
    71  		Run:    emptyRun,
    72  	}
    73  	deprecatedCmd := &Command{
    74  		Use:        "testDeprecated",
    75  		Deprecated: "deprecated", // Not completed
    76  		Run:        emptyRun,
    77  	}
    78  	aliasedCmd := &Command{
    79  		Use:     "aliased",
    80  		Short:   "A command with aliases",
    81  		Aliases: []string{"testAlias", "testSynonym"}, // Not completed
    82  		Run:     emptyRun,
    83  	}
    84  
    85  	rootCmd.AddCommand(childCmd1, childCmd2, hiddenCmd, deprecatedCmd, aliasedCmd)
    86  
    87  	// Test that sub-command names are completed
    88  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
    89  	if err != nil {
    90  		t.Errorf("Unexpected error: %v", err)
    91  	}
    92  
    93  	expected := strings.Join([]string{
    94  		"aliased",
    95  		"completion",
    96  		"firstChild",
    97  		"help",
    98  		"secondChild",
    99  		":4",
   100  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   101  
   102  	if output != expected {
   103  		t.Errorf("expected: %q, got: %q", expected, output)
   104  	}
   105  
   106  	// Test that sub-command names are completed with prefix
   107  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "s")
   108  	if err != nil {
   109  		t.Errorf("Unexpected error: %v", err)
   110  	}
   111  
   112  	expected = strings.Join([]string{
   113  		"secondChild",
   114  		":4",
   115  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   116  
   117  	if output != expected {
   118  		t.Errorf("expected: %q, got: %q", expected, output)
   119  	}
   120  
   121  	// Test that even with no valid sub-command matches, hidden, deprecated and
   122  	// aliases are not completed
   123  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "test")
   124  	if err != nil {
   125  		t.Errorf("Unexpected error: %v", err)
   126  	}
   127  
   128  	expected = strings.Join([]string{
   129  		":4",
   130  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   131  
   132  	if output != expected {
   133  		t.Errorf("expected: %q, got: %q", expected, output)
   134  	}
   135  
   136  	// Test that sub-command names are completed with description
   137  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "")
   138  	if err != nil {
   139  		t.Errorf("Unexpected error: %v", err)
   140  	}
   141  
   142  	expected = strings.Join([]string{
   143  		"aliased\tA command with aliases",
   144  		"completion\tGenerate the autocompletion script for the specified shell",
   145  		"firstChild\tFirst command",
   146  		"help\tHelp about any command",
   147  		"secondChild",
   148  		":4",
   149  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   150  
   151  	if output != expected {
   152  		t.Errorf("expected: %q, got: %q", expected, output)
   153  	}
   154  }
   155  
   156  func TestNoCmdNameCompletionInGo(t *testing.T) {
   157  	rootCmd := &Command{
   158  		Use: "root",
   159  		Run: emptyRun,
   160  	}
   161  	rootCmd.Flags().String("localroot", "", "local root flag")
   162  
   163  	childCmd1 := &Command{
   164  		Use:   "childCmd1",
   165  		Short: "First command",
   166  		Args:  MinimumNArgs(0),
   167  		Run:   emptyRun,
   168  	}
   169  	rootCmd.AddCommand(childCmd1)
   170  	childCmd1.PersistentFlags().StringP("persistent", "p", "", "persistent flag")
   171  	persistentFlag := childCmd1.PersistentFlags().Lookup("persistent")
   172  	childCmd1.Flags().StringP("nonPersistent", "n", "", "non-persistent flag")
   173  	nonPersistentFlag := childCmd1.Flags().Lookup("nonPersistent")
   174  
   175  	childCmd2 := &Command{
   176  		Use: "childCmd2",
   177  		Run: emptyRun,
   178  	}
   179  	childCmd1.AddCommand(childCmd2)
   180  
   181  	// Test that sub-command names are not completed if there is an argument already
   182  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "arg1", "")
   183  	if err != nil {
   184  		t.Errorf("Unexpected error: %v", err)
   185  	}
   186  
   187  	expected := strings.Join([]string{
   188  		":0",
   189  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
   190  
   191  	if output != expected {
   192  		t.Errorf("expected: %q, got: %q", expected, output)
   193  	}
   194  
   195  	// Test that sub-command names are not completed if a local non-persistent flag is present
   196  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--nonPersistent", "value", "")
   197  	if err != nil {
   198  		t.Errorf("Unexpected error: %v", err)
   199  	}
   200  	// Reset the flag for the next command
   201  	nonPersistentFlag.Changed = false
   202  
   203  	expected = strings.Join([]string{
   204  		":0",
   205  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
   206  
   207  	if output != expected {
   208  		t.Errorf("expected: %q, got: %q", expected, output)
   209  	}
   210  
   211  	// Test that sub-command names are completed if a local non-persistent flag is present and TraverseChildren is set to true
   212  	// set TraverseChildren to true on the root cmd
   213  	rootCmd.TraverseChildren = true
   214  
   215  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "")
   216  	if err != nil {
   217  		t.Errorf("Unexpected error: %v", err)
   218  	}
   219  	// Reset TraverseChildren for next command
   220  	rootCmd.TraverseChildren = false
   221  
   222  	expected = strings.Join([]string{
   223  		"childCmd1",
   224  		"completion",
   225  		"help",
   226  		":4",
   227  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   228  
   229  	if output != expected {
   230  		t.Errorf("expected: %q, got: %q", expected, output)
   231  	}
   232  
   233  	// Test that sub-command names from a child cmd are completed if a local non-persistent flag is present
   234  	// and TraverseChildren is set to true on the root cmd
   235  	rootCmd.TraverseChildren = true
   236  
   237  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "--nonPersistent", "value", "")
   238  	if err != nil {
   239  		t.Errorf("Unexpected error: %v", err)
   240  	}
   241  	// Reset TraverseChildren for next command
   242  	rootCmd.TraverseChildren = false
   243  	// Reset the flag for the next command
   244  	nonPersistentFlag.Changed = false
   245  
   246  	expected = strings.Join([]string{
   247  		"childCmd2",
   248  		":4",
   249  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   250  
   251  	if output != expected {
   252  		t.Errorf("expected: %q, got: %q", expected, output)
   253  	}
   254  
   255  	// Test that we don't use Traverse when we shouldn't.
   256  	// This command should not return a completion since the command line is invalid without TraverseChildren.
   257  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "")
   258  	if err != nil {
   259  		t.Errorf("Unexpected error: %v", err)
   260  	}
   261  
   262  	expected = strings.Join([]string{
   263  		":0",
   264  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
   265  
   266  	if output != expected {
   267  		t.Errorf("expected: %q, got: %q", expected, output)
   268  	}
   269  
   270  	// Test that sub-command names are not completed if a local non-persistent short flag is present
   271  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-n", "value", "")
   272  	if err != nil {
   273  		t.Errorf("Unexpected error: %v", err)
   274  	}
   275  	// Reset the flag for the next command
   276  	nonPersistentFlag.Changed = false
   277  
   278  	expected = strings.Join([]string{
   279  		":0",
   280  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
   281  
   282  	if output != expected {
   283  		t.Errorf("expected: %q, got: %q", expected, output)
   284  	}
   285  
   286  	// Test that sub-command names are completed with a persistent flag
   287  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--persistent", "value", "")
   288  	if err != nil {
   289  		t.Errorf("Unexpected error: %v", err)
   290  	}
   291  	// Reset the flag for the next command
   292  	persistentFlag.Changed = false
   293  
   294  	expected = strings.Join([]string{
   295  		"childCmd2",
   296  		":4",
   297  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   298  
   299  	if output != expected {
   300  		t.Errorf("expected: %q, got: %q", expected, output)
   301  	}
   302  
   303  	// Test that sub-command names are completed with a persistent short flag
   304  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-p", "value", "")
   305  	if err != nil {
   306  		t.Errorf("Unexpected error: %v", err)
   307  	}
   308  	// Reset the flag for the next command
   309  	persistentFlag.Changed = false
   310  
   311  	expected = strings.Join([]string{
   312  		"childCmd2",
   313  		":4",
   314  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   315  
   316  	if output != expected {
   317  		t.Errorf("expected: %q, got: %q", expected, output)
   318  	}
   319  }
   320  
   321  func TestValidArgsCompletionInGo(t *testing.T) {
   322  	rootCmd := &Command{
   323  		Use:       "root",
   324  		ValidArgs: []string{"one", "two", "three"},
   325  		Args:      MinimumNArgs(1),
   326  	}
   327  
   328  	// Test that validArgs are completed
   329  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
   330  	if err != nil {
   331  		t.Errorf("Unexpected error: %v", err)
   332  	}
   333  
   334  	expected := strings.Join([]string{
   335  		"one",
   336  		"two",
   337  		"three",
   338  		":4",
   339  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   340  
   341  	if output != expected {
   342  		t.Errorf("expected: %q, got: %q", expected, output)
   343  	}
   344  
   345  	// Test that validArgs are completed with prefix
   346  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "o")
   347  	if err != nil {
   348  		t.Errorf("Unexpected error: %v", err)
   349  	}
   350  
   351  	expected = strings.Join([]string{
   352  		"one",
   353  		":4",
   354  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   355  
   356  	if output != expected {
   357  		t.Errorf("expected: %q, got: %q", expected, output)
   358  	}
   359  
   360  	// Test that validArgs don't repeat
   361  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "one", "")
   362  	if err != nil {
   363  		t.Errorf("Unexpected error: %v", err)
   364  	}
   365  
   366  	expected = strings.Join([]string{
   367  		":0",
   368  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
   369  
   370  	if output != expected {
   371  		t.Errorf("expected: %q, got: %q", expected, output)
   372  	}
   373  }
   374  
   375  func TestValidArgsAndCmdCompletionInGo(t *testing.T) {
   376  	rootCmd := &Command{
   377  		Use:       "root",
   378  		ValidArgs: []string{"one", "two"},
   379  		Run:       emptyRun,
   380  	}
   381  
   382  	childCmd := &Command{
   383  		Use: "thechild",
   384  		Run: emptyRun,
   385  	}
   386  
   387  	rootCmd.AddCommand(childCmd)
   388  
   389  	// Test that both sub-commands and validArgs are completed
   390  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
   391  	if err != nil {
   392  		t.Errorf("Unexpected error: %v", err)
   393  	}
   394  
   395  	expected := strings.Join([]string{
   396  		"completion",
   397  		"help",
   398  		"thechild",
   399  		"one",
   400  		"two",
   401  		":4",
   402  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   403  
   404  	if output != expected {
   405  		t.Errorf("expected: %q, got: %q", expected, output)
   406  	}
   407  
   408  	// Test that both sub-commands and validArgs are completed with prefix
   409  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
   410  	if err != nil {
   411  		t.Errorf("Unexpected error: %v", err)
   412  	}
   413  
   414  	expected = strings.Join([]string{
   415  		"thechild",
   416  		"two",
   417  		":4",
   418  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   419  
   420  	if output != expected {
   421  		t.Errorf("expected: %q, got: %q", expected, output)
   422  	}
   423  }
   424  
   425  func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) {
   426  	rootCmd := &Command{
   427  		Use:               "root",
   428  		ValidArgsFunction: validArgsFunc,
   429  		Run:               emptyRun,
   430  	}
   431  
   432  	childCmd := &Command{
   433  		Use:   "thechild",
   434  		Short: "The child command",
   435  		Run:   emptyRun,
   436  	}
   437  
   438  	rootCmd.AddCommand(childCmd)
   439  
   440  	// Test that both sub-commands and validArgsFunction are completed
   441  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
   442  	if err != nil {
   443  		t.Errorf("Unexpected error: %v", err)
   444  	}
   445  
   446  	expected := strings.Join([]string{
   447  		"completion",
   448  		"help",
   449  		"thechild",
   450  		"one",
   451  		"two",
   452  		":0",
   453  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
   454  
   455  	if output != expected {
   456  		t.Errorf("expected: %q, got: %q", expected, output)
   457  	}
   458  
   459  	// Test that both sub-commands and validArgs are completed with prefix
   460  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
   461  	if err != nil {
   462  		t.Errorf("Unexpected error: %v", err)
   463  	}
   464  
   465  	expected = strings.Join([]string{
   466  		"thechild",
   467  		"two",
   468  		":0",
   469  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
   470  
   471  	if output != expected {
   472  		t.Errorf("expected: %q, got: %q", expected, output)
   473  	}
   474  
   475  	// Test that both sub-commands and validArgs are completed with description
   476  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "t")
   477  	if err != nil {
   478  		t.Errorf("Unexpected error: %v", err)
   479  	}
   480  
   481  	expected = strings.Join([]string{
   482  		"thechild\tThe child command",
   483  		"two\tThe second",
   484  		":0",
   485  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
   486  
   487  	if output != expected {
   488  		t.Errorf("expected: %q, got: %q", expected, output)
   489  	}
   490  }
   491  
   492  func TestFlagNameCompletionInGo(t *testing.T) {
   493  	rootCmd := &Command{
   494  		Use: "root",
   495  		Run: emptyRun,
   496  	}
   497  	childCmd := &Command{
   498  		Use:     "childCmd",
   499  		Version: "1.2.3",
   500  		Run:     emptyRun,
   501  	}
   502  	rootCmd.AddCommand(childCmd)
   503  
   504  	rootCmd.Flags().IntP("first", "f", -1, "first flag")
   505  	rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag")
   506  	childCmd.Flags().String("subFlag", "", "sub flag")
   507  
   508  	// Test that flag names are not shown if the user has not given the '-' prefix
   509  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
   510  	if err != nil {
   511  		t.Errorf("Unexpected error: %v", err)
   512  	}
   513  
   514  	expected := strings.Join([]string{
   515  		"childCmd",
   516  		"completion",
   517  		"help",
   518  		":4",
   519  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   520  
   521  	if output != expected {
   522  		t.Errorf("expected: %q, got: %q", expected, output)
   523  	}
   524  
   525  	// Test that flag names are completed
   526  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-")
   527  	if err != nil {
   528  		t.Errorf("Unexpected error: %v", err)
   529  	}
   530  
   531  	expected = strings.Join([]string{
   532  		"--first",
   533  		"-f",
   534  		"--help",
   535  		"-h",
   536  		"--second",
   537  		"-s",
   538  		":4",
   539  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   540  
   541  	if output != expected {
   542  		t.Errorf("expected: %q, got: %q", expected, output)
   543  	}
   544  
   545  	// Test that flag names are completed when a prefix is given
   546  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--f")
   547  	if err != nil {
   548  		t.Errorf("Unexpected error: %v", err)
   549  	}
   550  
   551  	expected = strings.Join([]string{
   552  		"--first",
   553  		":4",
   554  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   555  
   556  	if output != expected {
   557  		t.Errorf("expected: %q, got: %q", expected, output)
   558  	}
   559  
   560  	// Test that flag names are completed in a sub-cmd
   561  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-")
   562  	if err != nil {
   563  		t.Errorf("Unexpected error: %v", err)
   564  	}
   565  
   566  	expected = strings.Join([]string{
   567  		"--second",
   568  		"-s",
   569  		"--help",
   570  		"-h",
   571  		"--subFlag",
   572  		"--version",
   573  		"-v",
   574  		":4",
   575  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   576  
   577  	if output != expected {
   578  		t.Errorf("expected: %q, got: %q", expected, output)
   579  	}
   580  }
   581  
   582  func TestFlagNameCompletionInGoWithDesc(t *testing.T) {
   583  	rootCmd := &Command{
   584  		Use: "root",
   585  		Run: emptyRun,
   586  	}
   587  	childCmd := &Command{
   588  		Use:     "childCmd",
   589  		Short:   "first command",
   590  		Version: "1.2.3",
   591  		Run:     emptyRun,
   592  	}
   593  	rootCmd.AddCommand(childCmd)
   594  
   595  	rootCmd.Flags().IntP("first", "f", -1, "first flag\nlonger description for flag")
   596  	rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag")
   597  	childCmd.Flags().String("subFlag", "", "sub flag")
   598  
   599  	// Test that flag names are not shown if the user has not given the '-' prefix
   600  	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "")
   601  	if err != nil {
   602  		t.Errorf("Unexpected error: %v", err)
   603  	}
   604  
   605  	expected := strings.Join([]string{
   606  		"childCmd\tfirst command",
   607  		"completion\tGenerate the autocompletion script for the specified shell",
   608  		"help\tHelp about any command",
   609  		":4",
   610  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   611  
   612  	if output != expected {
   613  		t.Errorf("expected: %q, got: %q", expected, output)
   614  	}
   615  
   616  	// Test that flag names are completed
   617  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "-")
   618  	if err != nil {
   619  		t.Errorf("Unexpected error: %v", err)
   620  	}
   621  
   622  	expected = strings.Join([]string{
   623  		"--first\tfirst flag",
   624  		"-f\tfirst flag",
   625  		"--help\thelp for root",
   626  		"-h\thelp for root",
   627  		"--second\tsecond flag",
   628  		"-s\tsecond flag",
   629  		":4",
   630  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   631  
   632  	if output != expected {
   633  		t.Errorf("expected: %q, got: %q", expected, output)
   634  	}
   635  
   636  	// Test that flag names are completed when a prefix is given
   637  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--f")
   638  	if err != nil {
   639  		t.Errorf("Unexpected error: %v", err)
   640  	}
   641  
   642  	expected = strings.Join([]string{
   643  		"--first\tfirst flag",
   644  		":4",
   645  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   646  
   647  	if output != expected {
   648  		t.Errorf("expected: %q, got: %q", expected, output)
   649  	}
   650  
   651  	// Test that flag names are completed in a sub-cmd
   652  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "childCmd", "-")
   653  	if err != nil {
   654  		t.Errorf("Unexpected error: %v", err)
   655  	}
   656  
   657  	expected = strings.Join([]string{
   658  		"--second\tsecond flag",
   659  		"-s\tsecond flag",
   660  		"--help\thelp for childCmd",
   661  		"-h\thelp for childCmd",
   662  		"--subFlag\tsub flag",
   663  		"--version\tversion for childCmd",
   664  		"-v\tversion for childCmd",
   665  		":4",
   666  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   667  
   668  	if output != expected {
   669  		t.Errorf("expected: %q, got: %q", expected, output)
   670  	}
   671  }
   672  
   673  func TestFlagNameCompletionRepeat(t *testing.T) {
   674  	rootCmd := &Command{
   675  		Use: "root",
   676  		Run: emptyRun,
   677  	}
   678  	childCmd := &Command{
   679  		Use:   "childCmd",
   680  		Short: "first command",
   681  		Run:   emptyRun,
   682  	}
   683  	rootCmd.AddCommand(childCmd)
   684  
   685  	rootCmd.Flags().IntP("first", "f", -1, "first flag")
   686  	firstFlag := rootCmd.Flags().Lookup("first")
   687  	rootCmd.Flags().BoolP("second", "s", false, "second flag")
   688  	secondFlag := rootCmd.Flags().Lookup("second")
   689  	rootCmd.Flags().StringArrayP("array", "a", nil, "array flag")
   690  	arrayFlag := rootCmd.Flags().Lookup("array")
   691  	rootCmd.Flags().IntSliceP("slice", "l", nil, "slice flag")
   692  	sliceFlag := rootCmd.Flags().Lookup("slice")
   693  	rootCmd.Flags().BoolSliceP("bslice", "b", nil, "bool slice flag")
   694  	bsliceFlag := rootCmd.Flags().Lookup("bslice")
   695  
   696  	// Test that flag names are not repeated unless they are an array or slice
   697  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--")
   698  	if err != nil {
   699  		t.Errorf("Unexpected error: %v", err)
   700  	}
   701  	// Reset the flag for the next command
   702  	firstFlag.Changed = false
   703  
   704  	expected := strings.Join([]string{
   705  		"--array",
   706  		"--bslice",
   707  		"--help",
   708  		"--second",
   709  		"--slice",
   710  		":4",
   711  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   712  
   713  	if output != expected {
   714  		t.Errorf("expected: %q, got: %q", expected, output)
   715  	}
   716  
   717  	// Test that flag names are not repeated unless they are an array or slice
   718  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--second=false", "--")
   719  	if err != nil {
   720  		t.Errorf("Unexpected error: %v", err)
   721  	}
   722  	// Reset the flag for the next command
   723  	firstFlag.Changed = false
   724  	secondFlag.Changed = false
   725  
   726  	expected = strings.Join([]string{
   727  		"--array",
   728  		"--bslice",
   729  		"--help",
   730  		"--slice",
   731  		":4",
   732  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   733  
   734  	if output != expected {
   735  		t.Errorf("expected: %q, got: %q", expected, output)
   736  	}
   737  
   738  	// Test that flag names are not repeated unless they are an array or slice
   739  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--slice", "1", "--slice=2", "--array", "val", "--bslice", "true", "--")
   740  	if err != nil {
   741  		t.Errorf("Unexpected error: %v", err)
   742  	}
   743  	// Reset the flag for the next command
   744  	sliceFlag.Changed = false
   745  	arrayFlag.Changed = false
   746  	bsliceFlag.Changed = false
   747  
   748  	expected = strings.Join([]string{
   749  		"--array",
   750  		"--bslice",
   751  		"--first",
   752  		"--help",
   753  		"--second",
   754  		"--slice",
   755  		":4",
   756  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   757  
   758  	if output != expected {
   759  		t.Errorf("expected: %q, got: %q", expected, output)
   760  	}
   761  
   762  	// Test that flag names are not repeated unless they are an array or slice, using shortname
   763  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-")
   764  	if err != nil {
   765  		t.Errorf("Unexpected error: %v", err)
   766  	}
   767  	// Reset the flag for the next command
   768  	sliceFlag.Changed = false
   769  	arrayFlag.Changed = false
   770  
   771  	expected = strings.Join([]string{
   772  		"--array",
   773  		"-a",
   774  		"--bslice",
   775  		"-b",
   776  		"--first",
   777  		"-f",
   778  		"--help",
   779  		"-h",
   780  		"--second",
   781  		"-s",
   782  		"--slice",
   783  		"-l",
   784  		":4",
   785  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   786  
   787  	if output != expected {
   788  		t.Errorf("expected: %q, got: %q", expected, output)
   789  	}
   790  
   791  	// Test that flag names are not repeated unless they are an array or slice, using shortname with prefix
   792  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-a")
   793  	if err != nil {
   794  		t.Errorf("Unexpected error: %v", err)
   795  	}
   796  	// Reset the flag for the next command
   797  	sliceFlag.Changed = false
   798  	arrayFlag.Changed = false
   799  
   800  	expected = strings.Join([]string{
   801  		"-a",
   802  		":4",
   803  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   804  
   805  	if output != expected {
   806  		t.Errorf("expected: %q, got: %q", expected, output)
   807  	}
   808  }
   809  
   810  func TestRequiredFlagNameCompletionInGo(t *testing.T) {
   811  	rootCmd := &Command{
   812  		Use:       "root",
   813  		ValidArgs: []string{"realArg"},
   814  		Run:       emptyRun,
   815  	}
   816  	childCmd := &Command{
   817  		Use: "childCmd",
   818  		ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
   819  			return []string{"subArg"}, ShellCompDirectiveNoFileComp
   820  		},
   821  		Run: emptyRun,
   822  	}
   823  	rootCmd.AddCommand(childCmd)
   824  
   825  	rootCmd.Flags().IntP("requiredFlag", "r", -1, "required flag")
   826  	assertNoErr(t, rootCmd.MarkFlagRequired("requiredFlag"))
   827  	requiredFlag := rootCmd.Flags().Lookup("requiredFlag")
   828  
   829  	rootCmd.PersistentFlags().IntP("requiredPersistent", "p", -1, "required persistent")
   830  	assertNoErr(t, rootCmd.MarkPersistentFlagRequired("requiredPersistent"))
   831  	requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent")
   832  
   833  	rootCmd.Flags().StringP("release", "R", "", "Release name")
   834  
   835  	childCmd.Flags().BoolP("subRequired", "s", false, "sub required flag")
   836  	assertNoErr(t, childCmd.MarkFlagRequired("subRequired"))
   837  	childCmd.Flags().BoolP("subNotRequired", "n", false, "sub not required flag")
   838  
   839  	// Test that a required flag is suggested even without the - prefix
   840  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
   841  	if err != nil {
   842  		t.Errorf("Unexpected error: %v", err)
   843  	}
   844  
   845  	expected := strings.Join([]string{
   846  		"childCmd",
   847  		"completion",
   848  		"help",
   849  		"--requiredFlag",
   850  		"-r",
   851  		"--requiredPersistent",
   852  		"-p",
   853  		"realArg",
   854  		":4",
   855  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   856  
   857  	if output != expected {
   858  		t.Errorf("expected: %q, got: %q", expected, output)
   859  	}
   860  
   861  	// Test that a required flag is suggested without other flags when using the '-' prefix
   862  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-")
   863  	if err != nil {
   864  		t.Errorf("Unexpected error: %v", err)
   865  	}
   866  
   867  	expected = strings.Join([]string{
   868  		"--requiredFlag",
   869  		"-r",
   870  		"--requiredPersistent",
   871  		"-p",
   872  		":4",
   873  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   874  
   875  	if output != expected {
   876  		t.Errorf("expected: %q, got: %q", expected, output)
   877  	}
   878  
   879  	// Test that if no required flag matches, the normal flags are suggested
   880  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--relea")
   881  	if err != nil {
   882  		t.Errorf("Unexpected error: %v", err)
   883  	}
   884  
   885  	expected = strings.Join([]string{
   886  		"--release",
   887  		":4",
   888  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   889  
   890  	if output != expected {
   891  		t.Errorf("expected: %q, got: %q", expected, output)
   892  	}
   893  
   894  	// Test required flags for sub-commands
   895  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "")
   896  	if err != nil {
   897  		t.Errorf("Unexpected error: %v", err)
   898  	}
   899  
   900  	expected = strings.Join([]string{
   901  		"--requiredPersistent",
   902  		"-p",
   903  		"--subRequired",
   904  		"-s",
   905  		"subArg",
   906  		":4",
   907  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   908  
   909  	if output != expected {
   910  		t.Errorf("expected: %q, got: %q", expected, output)
   911  	}
   912  
   913  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-")
   914  	if err != nil {
   915  		t.Errorf("Unexpected error: %v", err)
   916  	}
   917  
   918  	expected = strings.Join([]string{
   919  		"--requiredPersistent",
   920  		"-p",
   921  		"--subRequired",
   922  		"-s",
   923  		":4",
   924  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   925  
   926  	if output != expected {
   927  		t.Errorf("expected: %q, got: %q", expected, output)
   928  	}
   929  
   930  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "--subNot")
   931  	if err != nil {
   932  		t.Errorf("Unexpected error: %v", err)
   933  	}
   934  
   935  	expected = strings.Join([]string{
   936  		"--subNotRequired",
   937  		":4",
   938  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   939  
   940  	if output != expected {
   941  		t.Errorf("expected: %q, got: %q", expected, output)
   942  	}
   943  
   944  	// Test that when a required flag is present, it is not suggested anymore
   945  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "")
   946  	if err != nil {
   947  		t.Errorf("Unexpected error: %v", err)
   948  	}
   949  	// Reset the flag for the next command
   950  	requiredFlag.Changed = false
   951  
   952  	expected = strings.Join([]string{
   953  		"--requiredPersistent",
   954  		"-p",
   955  		"realArg",
   956  		":4",
   957  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   958  
   959  	if output != expected {
   960  		t.Errorf("expected: %q, got: %q", expected, output)
   961  	}
   962  
   963  	// Test that when a persistent required flag is present, it is not suggested anymore
   964  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredPersistent", "1", "")
   965  	if err != nil {
   966  		t.Errorf("Unexpected error: %v", err)
   967  	}
   968  	// Reset the flag for the next command
   969  	requiredPersistent.Changed = false
   970  
   971  	expected = strings.Join([]string{
   972  		"childCmd",
   973  		"completion",
   974  		"help",
   975  		"--requiredFlag",
   976  		"-r",
   977  		"realArg",
   978  		":4",
   979  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   980  
   981  	if output != expected {
   982  		t.Errorf("expected: %q, got: %q", expected, output)
   983  	}
   984  
   985  	// Test that when all required flags are present, normal completion is done
   986  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "--requiredPersistent", "1", "")
   987  	if err != nil {
   988  		t.Errorf("Unexpected error: %v", err)
   989  	}
   990  	// Reset the flags for the next command
   991  	requiredFlag.Changed = false
   992  	requiredPersistent.Changed = false
   993  
   994  	expected = strings.Join([]string{
   995  		"realArg",
   996  		":4",
   997  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
   998  
   999  	if output != expected {
  1000  		t.Errorf("expected: %q, got: %q", expected, output)
  1001  	}
  1002  }
  1003  
  1004  func TestFlagFileExtFilterCompletionInGo(t *testing.T) {
  1005  	rootCmd := &Command{
  1006  		Use: "root",
  1007  		Run: emptyRun,
  1008  	}
  1009  
  1010  	// No extensions.  Should be ignored.
  1011  	rootCmd.Flags().StringP("file", "f", "", "file flag")
  1012  	assertNoErr(t, rootCmd.MarkFlagFilename("file"))
  1013  
  1014  	// Single extension
  1015  	rootCmd.Flags().StringP("log", "l", "", "log flag")
  1016  	assertNoErr(t, rootCmd.MarkFlagFilename("log", "log"))
  1017  
  1018  	// Multiple extensions
  1019  	rootCmd.Flags().StringP("yaml", "y", "", "yaml flag")
  1020  	assertNoErr(t, rootCmd.MarkFlagFilename("yaml", "yaml", "yml"))
  1021  
  1022  	// Directly using annotation
  1023  	rootCmd.Flags().StringP("text", "t", "", "text flag")
  1024  	assertNoErr(t, rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"}))
  1025  
  1026  	// Test that the completion logic returns the proper info for the completion
  1027  	// script to handle the file filtering
  1028  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--file", "")
  1029  	if err != nil {
  1030  		t.Errorf("Unexpected error: %v", err)
  1031  	}
  1032  
  1033  	expected := strings.Join([]string{
  1034  		":0",
  1035  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1036  
  1037  	if output != expected {
  1038  		t.Errorf("expected: %q, got: %q", expected, output)
  1039  	}
  1040  
  1041  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--log", "")
  1042  	if err != nil {
  1043  		t.Errorf("Unexpected error: %v", err)
  1044  	}
  1045  
  1046  	expected = strings.Join([]string{
  1047  		"log",
  1048  		":8",
  1049  		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
  1050  
  1051  	if output != expected {
  1052  		t.Errorf("expected: %q, got: %q", expected, output)
  1053  	}
  1054  
  1055  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml", "")
  1056  	if err != nil {
  1057  		t.Errorf("Unexpected error: %v", err)
  1058  	}
  1059  
  1060  	expected = strings.Join([]string{
  1061  		"yaml", "yml",
  1062  		":8",
  1063  		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
  1064  
  1065  	if output != expected {
  1066  		t.Errorf("expected: %q, got: %q", expected, output)
  1067  	}
  1068  
  1069  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml=")
  1070  	if err != nil {
  1071  		t.Errorf("Unexpected error: %v", err)
  1072  	}
  1073  
  1074  	expected = strings.Join([]string{
  1075  		"yaml", "yml",
  1076  		":8",
  1077  		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
  1078  
  1079  	if output != expected {
  1080  		t.Errorf("expected: %q, got: %q", expected, output)
  1081  	}
  1082  
  1083  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y", "")
  1084  	if err != nil {
  1085  		t.Errorf("Unexpected error: %v", err)
  1086  	}
  1087  
  1088  	expected = strings.Join([]string{
  1089  		"yaml", "yml",
  1090  		":8",
  1091  		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
  1092  
  1093  	if output != expected {
  1094  		t.Errorf("expected: %q, got: %q", expected, output)
  1095  	}
  1096  
  1097  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y=")
  1098  	if err != nil {
  1099  		t.Errorf("Unexpected error: %v", err)
  1100  	}
  1101  
  1102  	expected = strings.Join([]string{
  1103  		"yaml", "yml",
  1104  		":8",
  1105  		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
  1106  
  1107  	if output != expected {
  1108  		t.Errorf("expected: %q, got: %q", expected, output)
  1109  	}
  1110  
  1111  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--text", "")
  1112  	if err != nil {
  1113  		t.Errorf("Unexpected error: %v", err)
  1114  	}
  1115  
  1116  	expected = strings.Join([]string{
  1117  		"txt",
  1118  		":8",
  1119  		"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
  1120  
  1121  	if output != expected {
  1122  		t.Errorf("expected: %q, got: %q", expected, output)
  1123  	}
  1124  }
  1125  
  1126  func TestFlagDirFilterCompletionInGo(t *testing.T) {
  1127  	rootCmd := &Command{
  1128  		Use: "root",
  1129  		Run: emptyRun,
  1130  	}
  1131  
  1132  	// Filter directories
  1133  	rootCmd.Flags().StringP("dir", "d", "", "dir flag")
  1134  	assertNoErr(t, rootCmd.MarkFlagDirname("dir"))
  1135  
  1136  	// Filter directories within a directory
  1137  	rootCmd.Flags().StringP("subdir", "s", "", "subdir")
  1138  	assertNoErr(t, rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"}))
  1139  
  1140  	// Multiple directory specification get ignored
  1141  	rootCmd.Flags().StringP("manydir", "m", "", "manydir")
  1142  	assertNoErr(t, rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"}))
  1143  
  1144  	// Test that the completion logic returns the proper info for the completion
  1145  	// script to handle the directory filtering
  1146  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--dir", "")
  1147  	if err != nil {
  1148  		t.Errorf("Unexpected error: %v", err)
  1149  	}
  1150  
  1151  	expected := strings.Join([]string{
  1152  		":16",
  1153  		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
  1154  
  1155  	if output != expected {
  1156  		t.Errorf("expected: %q, got: %q", expected, output)
  1157  	}
  1158  
  1159  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-d", "")
  1160  	if err != nil {
  1161  		t.Errorf("Unexpected error: %v", err)
  1162  	}
  1163  
  1164  	expected = strings.Join([]string{
  1165  		":16",
  1166  		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
  1167  
  1168  	if output != expected {
  1169  		t.Errorf("expected: %q, got: %q", expected, output)
  1170  	}
  1171  
  1172  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir", "")
  1173  	if err != nil {
  1174  		t.Errorf("Unexpected error: %v", err)
  1175  	}
  1176  
  1177  	expected = strings.Join([]string{
  1178  		"themes",
  1179  		":16",
  1180  		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
  1181  
  1182  	if output != expected {
  1183  		t.Errorf("expected: %q, got: %q", expected, output)
  1184  	}
  1185  
  1186  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir=")
  1187  	if err != nil {
  1188  		t.Errorf("Unexpected error: %v", err)
  1189  	}
  1190  
  1191  	expected = strings.Join([]string{
  1192  		"themes",
  1193  		":16",
  1194  		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
  1195  
  1196  	if output != expected {
  1197  		t.Errorf("expected: %q, got: %q", expected, output)
  1198  	}
  1199  
  1200  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "")
  1201  	if err != nil {
  1202  		t.Errorf("Unexpected error: %v", err)
  1203  	}
  1204  
  1205  	expected = strings.Join([]string{
  1206  		"themes",
  1207  		":16",
  1208  		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
  1209  
  1210  	if output != expected {
  1211  		t.Errorf("expected: %q, got: %q", expected, output)
  1212  	}
  1213  
  1214  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s=")
  1215  	if err != nil {
  1216  		t.Errorf("Unexpected error: %v", err)
  1217  	}
  1218  
  1219  	expected = strings.Join([]string{
  1220  		"themes",
  1221  		":16",
  1222  		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
  1223  
  1224  	if output != expected {
  1225  		t.Errorf("expected: %q, got: %q", expected, output)
  1226  	}
  1227  
  1228  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--manydir", "")
  1229  	if err != nil {
  1230  		t.Errorf("Unexpected error: %v", err)
  1231  	}
  1232  
  1233  	expected = strings.Join([]string{
  1234  		":16",
  1235  		"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
  1236  
  1237  	if output != expected {
  1238  		t.Errorf("expected: %q, got: %q", expected, output)
  1239  	}
  1240  }
  1241  
  1242  func TestValidArgsFuncCmdContext(t *testing.T) {
  1243  	validArgsFunc := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  1244  		ctx := cmd.Context()
  1245  
  1246  		if ctx == nil {
  1247  			t.Error("Received nil context in completion func")
  1248  		} else if ctx.Value("testKey") != "123" {
  1249  			t.Error("Received invalid context")
  1250  		}
  1251  
  1252  		return nil, ShellCompDirectiveDefault
  1253  	}
  1254  
  1255  	rootCmd := &Command{
  1256  		Use: "root",
  1257  		Run: emptyRun,
  1258  	}
  1259  	childCmd := &Command{
  1260  		Use:               "childCmd",
  1261  		ValidArgsFunction: validArgsFunc,
  1262  		Run:               emptyRun,
  1263  	}
  1264  	rootCmd.AddCommand(childCmd)
  1265  
  1266  	//nolint:golint,staticcheck // We can safely use a basic type as key in tests.
  1267  	ctx := context.WithValue(context.Background(), "testKey", "123")
  1268  
  1269  	// Test completing an empty string on the childCmd
  1270  	_, output, err := executeCommandWithContextC(ctx, rootCmd, ShellCompNoDescRequestCmd, "childCmd", "")
  1271  	if err != nil {
  1272  		t.Errorf("Unexpected error: %v", err)
  1273  	}
  1274  
  1275  	expected := strings.Join([]string{
  1276  		":0",
  1277  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1278  
  1279  	if output != expected {
  1280  		t.Errorf("expected: %q, got: %q", expected, output)
  1281  	}
  1282  }
  1283  
  1284  func TestValidArgsFuncSingleCmd(t *testing.T) {
  1285  	rootCmd := &Command{
  1286  		Use:               "root",
  1287  		ValidArgsFunction: validArgsFunc,
  1288  		Run:               emptyRun,
  1289  	}
  1290  
  1291  	// Test completing an empty string
  1292  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
  1293  	if err != nil {
  1294  		t.Errorf("Unexpected error: %v", err)
  1295  	}
  1296  
  1297  	expected := strings.Join([]string{
  1298  		"one",
  1299  		"two",
  1300  		":0",
  1301  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1302  
  1303  	if output != expected {
  1304  		t.Errorf("expected: %q, got: %q", expected, output)
  1305  	}
  1306  
  1307  	// Check completing with a prefix
  1308  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
  1309  	if err != nil {
  1310  		t.Errorf("Unexpected error: %v", err)
  1311  	}
  1312  
  1313  	expected = strings.Join([]string{
  1314  		"two",
  1315  		":0",
  1316  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1317  
  1318  	if output != expected {
  1319  		t.Errorf("expected: %q, got: %q", expected, output)
  1320  	}
  1321  }
  1322  
  1323  func TestValidArgsFuncSingleCmdInvalidArg(t *testing.T) {
  1324  	rootCmd := &Command{
  1325  		Use: "root",
  1326  		// If we don't specify a value for Args, this test fails.
  1327  		// This is only true for a root command without any subcommands, and is caused
  1328  		// by the fact that the __complete command becomes a subcommand when there should not be one.
  1329  		// The problem is in the implementation of legacyArgs().
  1330  		Args:              MinimumNArgs(1),
  1331  		ValidArgsFunction: validArgsFunc,
  1332  		Run:               emptyRun,
  1333  	}
  1334  
  1335  	// Check completing with wrong number of args
  1336  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "unexpectedArg", "t")
  1337  	if err != nil {
  1338  		t.Errorf("Unexpected error: %v", err)
  1339  	}
  1340  
  1341  	expected := strings.Join([]string{
  1342  		":4",
  1343  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  1344  
  1345  	if output != expected {
  1346  		t.Errorf("expected: %q, got: %q", expected, output)
  1347  	}
  1348  }
  1349  
  1350  func TestValidArgsFuncChildCmds(t *testing.T) {
  1351  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  1352  	child1Cmd := &Command{
  1353  		Use:               "child1",
  1354  		ValidArgsFunction: validArgsFunc,
  1355  		Run:               emptyRun,
  1356  	}
  1357  	child2Cmd := &Command{
  1358  		Use:               "child2",
  1359  		ValidArgsFunction: validArgsFunc2,
  1360  		Run:               emptyRun,
  1361  	}
  1362  	rootCmd.AddCommand(child1Cmd, child2Cmd)
  1363  
  1364  	// Test completion of first sub-command with empty argument
  1365  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "")
  1366  	if err != nil {
  1367  		t.Errorf("Unexpected error: %v", err)
  1368  	}
  1369  
  1370  	expected := strings.Join([]string{
  1371  		"one",
  1372  		"two",
  1373  		":0",
  1374  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1375  
  1376  	if output != expected {
  1377  		t.Errorf("expected: %q, got: %q", expected, output)
  1378  	}
  1379  
  1380  	// Test completion of first sub-command with a prefix to complete
  1381  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "t")
  1382  	if err != nil {
  1383  		t.Errorf("Unexpected error: %v", err)
  1384  	}
  1385  
  1386  	expected = strings.Join([]string{
  1387  		"two",
  1388  		":0",
  1389  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1390  
  1391  	if output != expected {
  1392  		t.Errorf("expected: %q, got: %q", expected, output)
  1393  	}
  1394  
  1395  	// Check completing with wrong number of args
  1396  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "unexpectedArg", "t")
  1397  	if err != nil {
  1398  		t.Errorf("Unexpected error: %v", err)
  1399  	}
  1400  
  1401  	expected = strings.Join([]string{
  1402  		":4",
  1403  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  1404  
  1405  	if output != expected {
  1406  		t.Errorf("expected: %q, got: %q", expected, output)
  1407  	}
  1408  
  1409  	// Test completion of second sub-command with empty argument
  1410  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "")
  1411  	if err != nil {
  1412  		t.Errorf("Unexpected error: %v", err)
  1413  	}
  1414  
  1415  	expected = strings.Join([]string{
  1416  		"three",
  1417  		"four",
  1418  		":0",
  1419  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1420  
  1421  	if output != expected {
  1422  		t.Errorf("expected: %q, got: %q", expected, output)
  1423  	}
  1424  
  1425  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "t")
  1426  	if err != nil {
  1427  		t.Errorf("Unexpected error: %v", err)
  1428  	}
  1429  
  1430  	expected = strings.Join([]string{
  1431  		"three",
  1432  		":0",
  1433  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1434  
  1435  	if output != expected {
  1436  		t.Errorf("expected: %q, got: %q", expected, output)
  1437  	}
  1438  
  1439  	// Check completing with wrong number of args
  1440  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "unexpectedArg", "t")
  1441  	if err != nil {
  1442  		t.Errorf("Unexpected error: %v", err)
  1443  	}
  1444  
  1445  	expected = strings.Join([]string{
  1446  		":4",
  1447  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  1448  
  1449  	if output != expected {
  1450  		t.Errorf("expected: %q, got: %q", expected, output)
  1451  	}
  1452  }
  1453  
  1454  func TestValidArgsFuncAliases(t *testing.T) {
  1455  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  1456  	child := &Command{
  1457  		Use:               "child",
  1458  		Aliases:           []string{"son", "daughter"},
  1459  		ValidArgsFunction: validArgsFunc,
  1460  		Run:               emptyRun,
  1461  	}
  1462  	rootCmd.AddCommand(child)
  1463  
  1464  	// Test completion of first sub-command with empty argument
  1465  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "")
  1466  	if err != nil {
  1467  		t.Errorf("Unexpected error: %v", err)
  1468  	}
  1469  
  1470  	expected := strings.Join([]string{
  1471  		"one",
  1472  		"two",
  1473  		":0",
  1474  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1475  
  1476  	if output != expected {
  1477  		t.Errorf("expected: %q, got: %q", expected, output)
  1478  	}
  1479  
  1480  	// Test completion of first sub-command with a prefix to complete
  1481  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "daughter", "t")
  1482  	if err != nil {
  1483  		t.Errorf("Unexpected error: %v", err)
  1484  	}
  1485  
  1486  	expected = strings.Join([]string{
  1487  		"two",
  1488  		":0",
  1489  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1490  
  1491  	if output != expected {
  1492  		t.Errorf("expected: %q, got: %q", expected, output)
  1493  	}
  1494  
  1495  	// Check completing with wrong number of args
  1496  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "unexpectedArg", "t")
  1497  	if err != nil {
  1498  		t.Errorf("Unexpected error: %v", err)
  1499  	}
  1500  
  1501  	expected = strings.Join([]string{
  1502  		":4",
  1503  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  1504  
  1505  	if output != expected {
  1506  		t.Errorf("expected: %q, got: %q", expected, output)
  1507  	}
  1508  }
  1509  
  1510  func TestValidArgsFuncInBashScript(t *testing.T) {
  1511  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  1512  	child := &Command{
  1513  		Use:               "child",
  1514  		ValidArgsFunction: validArgsFunc,
  1515  		Run:               emptyRun,
  1516  	}
  1517  	rootCmd.AddCommand(child)
  1518  
  1519  	buf := new(bytes.Buffer)
  1520  	assertNoErr(t, rootCmd.GenBashCompletion(buf))
  1521  	output := buf.String()
  1522  
  1523  	check(t, output, "has_completion_function=1")
  1524  }
  1525  
  1526  func TestNoValidArgsFuncInBashScript(t *testing.T) {
  1527  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  1528  	child := &Command{
  1529  		Use: "child",
  1530  		Run: emptyRun,
  1531  	}
  1532  	rootCmd.AddCommand(child)
  1533  
  1534  	buf := new(bytes.Buffer)
  1535  	assertNoErr(t, rootCmd.GenBashCompletion(buf))
  1536  	output := buf.String()
  1537  
  1538  	checkOmit(t, output, "has_completion_function=1")
  1539  }
  1540  
  1541  func TestCompleteCmdInBashScript(t *testing.T) {
  1542  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  1543  	child := &Command{
  1544  		Use:               "child",
  1545  		ValidArgsFunction: validArgsFunc,
  1546  		Run:               emptyRun,
  1547  	}
  1548  	rootCmd.AddCommand(child)
  1549  
  1550  	buf := new(bytes.Buffer)
  1551  	assertNoErr(t, rootCmd.GenBashCompletion(buf))
  1552  	output := buf.String()
  1553  
  1554  	check(t, output, ShellCompNoDescRequestCmd)
  1555  }
  1556  
  1557  func TestCompleteNoDesCmdInZshScript(t *testing.T) {
  1558  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  1559  	child := &Command{
  1560  		Use:               "child",
  1561  		ValidArgsFunction: validArgsFunc,
  1562  		Run:               emptyRun,
  1563  	}
  1564  	rootCmd.AddCommand(child)
  1565  
  1566  	buf := new(bytes.Buffer)
  1567  	assertNoErr(t, rootCmd.GenZshCompletionNoDesc(buf))
  1568  	output := buf.String()
  1569  
  1570  	check(t, output, ShellCompNoDescRequestCmd)
  1571  }
  1572  
  1573  func TestCompleteCmdInZshScript(t *testing.T) {
  1574  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  1575  	child := &Command{
  1576  		Use:               "child",
  1577  		ValidArgsFunction: validArgsFunc,
  1578  		Run:               emptyRun,
  1579  	}
  1580  	rootCmd.AddCommand(child)
  1581  
  1582  	buf := new(bytes.Buffer)
  1583  	assertNoErr(t, rootCmd.GenZshCompletion(buf))
  1584  	output := buf.String()
  1585  
  1586  	check(t, output, ShellCompRequestCmd)
  1587  	checkOmit(t, output, ShellCompNoDescRequestCmd)
  1588  }
  1589  
  1590  func TestFlagCompletionInGo(t *testing.T) {
  1591  	rootCmd := &Command{
  1592  		Use: "root",
  1593  		Run: emptyRun,
  1594  	}
  1595  	rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
  1596  	assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  1597  		completions := []string{}
  1598  		for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
  1599  			if strings.HasPrefix(comp, toComplete) {
  1600  				completions = append(completions, comp)
  1601  			}
  1602  		}
  1603  		return completions, ShellCompDirectiveDefault
  1604  	}))
  1605  	rootCmd.Flags().String("filename", "", "Enter a filename")
  1606  	assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  1607  		completions := []string{}
  1608  		for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
  1609  			if strings.HasPrefix(comp, toComplete) {
  1610  				completions = append(completions, comp)
  1611  			}
  1612  		}
  1613  		return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
  1614  	}))
  1615  
  1616  	// Test completing an empty string
  1617  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "")
  1618  	if err != nil {
  1619  		t.Errorf("Unexpected error: %v", err)
  1620  	}
  1621  
  1622  	expected := strings.Join([]string{
  1623  		"1",
  1624  		"2",
  1625  		"10",
  1626  		":0",
  1627  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1628  
  1629  	if output != expected {
  1630  		t.Errorf("expected: %q, got: %q", expected, output)
  1631  	}
  1632  
  1633  	// Check completing with a prefix
  1634  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "1")
  1635  	if err != nil {
  1636  		t.Errorf("Unexpected error: %v", err)
  1637  	}
  1638  
  1639  	expected = strings.Join([]string{
  1640  		"1",
  1641  		"10",
  1642  		":0",
  1643  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1644  
  1645  	if output != expected {
  1646  		t.Errorf("expected: %q, got: %q", expected, output)
  1647  	}
  1648  
  1649  	// Test completing an empty string
  1650  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "")
  1651  	if err != nil {
  1652  		t.Errorf("Unexpected error: %v", err)
  1653  	}
  1654  
  1655  	expected = strings.Join([]string{
  1656  		"file.yaml",
  1657  		"myfile.json",
  1658  		"file.xml",
  1659  		":6",
  1660  		"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
  1661  
  1662  	if output != expected {
  1663  		t.Errorf("expected: %q, got: %q", expected, output)
  1664  	}
  1665  
  1666  	// Check completing with a prefix
  1667  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "f")
  1668  	if err != nil {
  1669  		t.Errorf("Unexpected error: %v", err)
  1670  	}
  1671  
  1672  	expected = strings.Join([]string{
  1673  		"file.yaml",
  1674  		"file.xml",
  1675  		":6",
  1676  		"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
  1677  
  1678  	if output != expected {
  1679  		t.Errorf("expected: %q, got: %q", expected, output)
  1680  	}
  1681  }
  1682  
  1683  func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) {
  1684  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  1685  	child1Cmd := &Command{
  1686  		Use:               "child1",
  1687  		ValidArgsFunction: validArgsFunc,
  1688  		Run:               emptyRun,
  1689  	}
  1690  	child2Cmd := &Command{
  1691  		Use:               "child2",
  1692  		ValidArgsFunction: validArgsFunc2,
  1693  		Run:               emptyRun,
  1694  	}
  1695  	rootCmd.AddCommand(child1Cmd, child2Cmd)
  1696  
  1697  	// Test completion of first sub-command with empty argument
  1698  	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child1", "")
  1699  	if err != nil {
  1700  		t.Errorf("Unexpected error: %v", err)
  1701  	}
  1702  
  1703  	expected := strings.Join([]string{
  1704  		"one\tThe first",
  1705  		"two\tThe second",
  1706  		":0",
  1707  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1708  
  1709  	if output != expected {
  1710  		t.Errorf("expected: %q, got: %q", expected, output)
  1711  	}
  1712  
  1713  	// Test completion of first sub-command with a prefix to complete
  1714  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "t")
  1715  	if err != nil {
  1716  		t.Errorf("Unexpected error: %v", err)
  1717  	}
  1718  
  1719  	expected = strings.Join([]string{
  1720  		"two\tThe second",
  1721  		":0",
  1722  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1723  
  1724  	if output != expected {
  1725  		t.Errorf("expected: %q, got: %q", expected, output)
  1726  	}
  1727  
  1728  	// Check completing with wrong number of args
  1729  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "unexpectedArg", "t")
  1730  	if err != nil {
  1731  		t.Errorf("Unexpected error: %v", err)
  1732  	}
  1733  
  1734  	expected = strings.Join([]string{
  1735  		":4",
  1736  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  1737  
  1738  	if output != expected {
  1739  		t.Errorf("expected: %q, got: %q", expected, output)
  1740  	}
  1741  
  1742  	// Test completion of second sub-command with empty argument
  1743  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "")
  1744  	if err != nil {
  1745  		t.Errorf("Unexpected error: %v", err)
  1746  	}
  1747  
  1748  	expected = strings.Join([]string{
  1749  		"three\tThe third",
  1750  		"four\tThe fourth",
  1751  		":0",
  1752  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1753  
  1754  	if output != expected {
  1755  		t.Errorf("expected: %q, got: %q", expected, output)
  1756  	}
  1757  
  1758  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "t")
  1759  	if err != nil {
  1760  		t.Errorf("Unexpected error: %v", err)
  1761  	}
  1762  
  1763  	expected = strings.Join([]string{
  1764  		"three\tThe third",
  1765  		":0",
  1766  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1767  
  1768  	if output != expected {
  1769  		t.Errorf("expected: %q, got: %q", expected, output)
  1770  	}
  1771  
  1772  	// Check completing with wrong number of args
  1773  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "unexpectedArg", "t")
  1774  	if err != nil {
  1775  		t.Errorf("Unexpected error: %v", err)
  1776  	}
  1777  
  1778  	expected = strings.Join([]string{
  1779  		":4",
  1780  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  1781  
  1782  	if output != expected {
  1783  		t.Errorf("expected: %q, got: %q", expected, output)
  1784  	}
  1785  }
  1786  
  1787  func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) {
  1788  	rootCmd := &Command{Use: "root", Run: emptyRun}
  1789  	childCmd := &Command{
  1790  		Use: "child",
  1791  		Run: emptyRun,
  1792  		ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  1793  			return []string{"--validarg", "test"}, ShellCompDirectiveDefault
  1794  		},
  1795  	}
  1796  	childCmd2 := &Command{
  1797  		Use:       "child2",
  1798  		Run:       emptyRun,
  1799  		ValidArgs: []string{"arg1", "arg2"},
  1800  	}
  1801  	rootCmd.AddCommand(childCmd, childCmd2)
  1802  	childCmd.Flags().Bool("bool", false, "test bool flag")
  1803  	childCmd.Flags().String("string", "", "test string flag")
  1804  	_ = childCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  1805  		return []string{"myval"}, ShellCompDirectiveDefault
  1806  	})
  1807  
  1808  	// Test flag completion with no argument
  1809  	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--")
  1810  	if err != nil {
  1811  		t.Errorf("Unexpected error: %v", err)
  1812  	}
  1813  
  1814  	expected := strings.Join([]string{
  1815  		"--bool\ttest bool flag",
  1816  		"--help\thelp for child",
  1817  		"--string\ttest string flag",
  1818  		":4",
  1819  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  1820  
  1821  	if output != expected {
  1822  		t.Errorf("expected: %q, got: %q", expected, output)
  1823  	}
  1824  
  1825  	// Test that no flags are completed after the -- arg
  1826  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "-")
  1827  	if err != nil {
  1828  		t.Errorf("Unexpected error: %v", err)
  1829  	}
  1830  
  1831  	expected = strings.Join([]string{
  1832  		"--validarg",
  1833  		"test",
  1834  		":0",
  1835  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1836  
  1837  	if output != expected {
  1838  		t.Errorf("expected: %q, got: %q", expected, output)
  1839  	}
  1840  
  1841  	// Test that no flags are completed after the -- arg with a flag set
  1842  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--bool", "--", "-")
  1843  	if err != nil {
  1844  		t.Errorf("Unexpected error: %v", err)
  1845  	}
  1846  
  1847  	expected = strings.Join([]string{
  1848  		"--validarg",
  1849  		"test",
  1850  		":0",
  1851  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1852  
  1853  	if output != expected {
  1854  		t.Errorf("expected: %q, got: %q", expected, output)
  1855  	}
  1856  
  1857  	// set Interspersed to false which means that no flags should be completed after the first arg
  1858  	childCmd.Flags().SetInterspersed(false)
  1859  
  1860  	// Test that no flags are completed after the first arg
  1861  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "arg", "--")
  1862  	if err != nil {
  1863  		t.Errorf("Unexpected error: %v", err)
  1864  	}
  1865  
  1866  	expected = strings.Join([]string{
  1867  		"--validarg",
  1868  		"test",
  1869  		":0",
  1870  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1871  
  1872  	if output != expected {
  1873  		t.Errorf("expected: %q, got: %q", expected, output)
  1874  	}
  1875  
  1876  	// Test that no flags are completed after the fist arg with a flag set
  1877  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "t", "arg", "--")
  1878  	if err != nil {
  1879  		t.Errorf("Unexpected error: %v", err)
  1880  	}
  1881  
  1882  	expected = strings.Join([]string{
  1883  		"--validarg",
  1884  		"test",
  1885  		":0",
  1886  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1887  
  1888  	if output != expected {
  1889  		t.Errorf("expected: %q, got: %q", expected, output)
  1890  	}
  1891  
  1892  	// Check that args are still completed after --
  1893  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "")
  1894  	if err != nil {
  1895  		t.Errorf("Unexpected error: %v", err)
  1896  	}
  1897  
  1898  	expected = strings.Join([]string{
  1899  		"--validarg",
  1900  		"test",
  1901  		":0",
  1902  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1903  
  1904  	if output != expected {
  1905  		t.Errorf("expected: %q, got: %q", expected, output)
  1906  	}
  1907  
  1908  	// Check that args are still completed even if flagname with ValidArgsFunction exists
  1909  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--string", "")
  1910  	if err != nil {
  1911  		t.Errorf("Unexpected error: %v", err)
  1912  	}
  1913  
  1914  	expected = strings.Join([]string{
  1915  		"--validarg",
  1916  		"test",
  1917  		":0",
  1918  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1919  
  1920  	if output != expected {
  1921  		t.Errorf("expected: %q, got: %q", expected, output)
  1922  	}
  1923  
  1924  	// Check that args are still completed even if flagname with ValidArgsFunction exists
  1925  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "--", "a")
  1926  	if err != nil {
  1927  		t.Errorf("Unexpected error: %v", err)
  1928  	}
  1929  
  1930  	expected = strings.Join([]string{
  1931  		"arg1",
  1932  		"arg2",
  1933  		":4",
  1934  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  1935  
  1936  	if output != expected {
  1937  		t.Errorf("expected: %q, got: %q", expected, output)
  1938  	}
  1939  
  1940  	// Check that --validarg is not parsed as flag after --
  1941  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "")
  1942  	if err != nil {
  1943  		t.Errorf("Unexpected error: %v", err)
  1944  	}
  1945  
  1946  	expected = strings.Join([]string{
  1947  		"--validarg",
  1948  		"test",
  1949  		":0",
  1950  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1951  
  1952  	if output != expected {
  1953  		t.Errorf("expected: %q, got: %q", expected, output)
  1954  	}
  1955  
  1956  	// Check that --validarg is not parsed as flag after an arg
  1957  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "arg", "--validarg", "")
  1958  	if err != nil {
  1959  		t.Errorf("Unexpected error: %v", err)
  1960  	}
  1961  
  1962  	expected = strings.Join([]string{
  1963  		"--validarg",
  1964  		"test",
  1965  		":0",
  1966  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1967  
  1968  	if output != expected {
  1969  		t.Errorf("expected: %q, got: %q", expected, output)
  1970  	}
  1971  
  1972  	// Check that --validarg is added to args for the ValidArgsFunction
  1973  	childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  1974  		return args, ShellCompDirectiveDefault
  1975  	}
  1976  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "")
  1977  	if err != nil {
  1978  		t.Errorf("Unexpected error: %v", err)
  1979  	}
  1980  
  1981  	expected = strings.Join([]string{
  1982  		"--validarg",
  1983  		":0",
  1984  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  1985  
  1986  	if output != expected {
  1987  		t.Errorf("expected: %q, got: %q", expected, output)
  1988  	}
  1989  
  1990  	// Check that --validarg is added to args for the ValidArgsFunction and toComplete is also set correctly
  1991  	childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  1992  		return append(args, toComplete), ShellCompDirectiveDefault
  1993  	}
  1994  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "--toComp=ab")
  1995  	if err != nil {
  1996  		t.Errorf("Unexpected error: %v", err)
  1997  	}
  1998  
  1999  	expected = strings.Join([]string{
  2000  		"--validarg",
  2001  		"--toComp=ab",
  2002  		":0",
  2003  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  2004  
  2005  	if output != expected {
  2006  		t.Errorf("expected: %q, got: %q", expected, output)
  2007  	}
  2008  }
  2009  
  2010  func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) {
  2011  	rootCmd := &Command{Use: "root", Run: emptyRun}
  2012  	childCmd := &Command{
  2013  		Use: "child",
  2014  		Run: emptyRun,
  2015  		ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2016  			return []string{"--validarg", "test"}, ShellCompDirectiveDefault
  2017  		},
  2018  	}
  2019  	childCmd.Flags().Bool("bool", false, "test bool flag")
  2020  	childCmd.Flags().String("string", "", "test string flag")
  2021  	_ = childCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2022  		return []string{"myval"}, ShellCompDirectiveDefault
  2023  	})
  2024  
  2025  	// Important: This is a test for https://github.com/spf13/cobra/issues/1437
  2026  	// Only add the subcommand after RegisterFlagCompletionFunc was called, do not change this order!
  2027  	rootCmd.AddCommand(childCmd)
  2028  
  2029  	// Test that flag completion works for the subcmd
  2030  	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "")
  2031  	if err != nil {
  2032  		t.Errorf("Unexpected error: %v", err)
  2033  	}
  2034  
  2035  	expected := strings.Join([]string{
  2036  		"myval",
  2037  		":0",
  2038  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  2039  
  2040  	if output != expected {
  2041  		t.Errorf("expected: %q, got: %q", expected, output)
  2042  	}
  2043  }
  2044  
  2045  func TestFlagCompletionForPersistentFlagsCalledFromSubCmd(t *testing.T) {
  2046  	rootCmd := &Command{Use: "root", Run: emptyRun}
  2047  	rootCmd.PersistentFlags().String("string", "", "test string flag")
  2048  	_ = rootCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2049  		return []string{"myval"}, ShellCompDirectiveDefault
  2050  	})
  2051  
  2052  	childCmd := &Command{
  2053  		Use: "child",
  2054  		Run: emptyRun,
  2055  		ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2056  			return []string{"--validarg", "test"}, ShellCompDirectiveDefault
  2057  		},
  2058  	}
  2059  	childCmd.Flags().Bool("bool", false, "test bool flag")
  2060  	rootCmd.AddCommand(childCmd)
  2061  
  2062  	// Test that persistent flag completion works for the subcmd
  2063  	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "")
  2064  	if err != nil {
  2065  		t.Errorf("Unexpected error: %v", err)
  2066  	}
  2067  
  2068  	expected := strings.Join([]string{
  2069  		"myval",
  2070  		":0",
  2071  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  2072  
  2073  	if output != expected {
  2074  		t.Errorf("expected: %q, got: %q", expected, output)
  2075  	}
  2076  }
  2077  
  2078  // This test tries to register flag completion concurrently to make sure the
  2079  // code handles concurrency properly.
  2080  // This was reported as a problem when tests are run concurrently:
  2081  // https://github.com/spf13/cobra/issues/1320
  2082  //
  2083  // NOTE: this test can sometimes pass even if the code were to not handle
  2084  // concurrency properly. This is not great but the important part is that
  2085  // it should never fail.  Therefore, if the tests fails sometimes, we will
  2086  // still be able to know there is a problem.
  2087  func TestFlagCompletionConcurrentRegistration(t *testing.T) {
  2088  	rootCmd := &Command{Use: "root", Run: emptyRun}
  2089  	const maxFlags = 50
  2090  	for i := 1; i < maxFlags; i += 2 {
  2091  		flagName := fmt.Sprintf("flag%d", i)
  2092  		rootCmd.Flags().String(flagName, "", fmt.Sprintf("test %s flag on root", flagName))
  2093  	}
  2094  
  2095  	childCmd := &Command{
  2096  		Use: "child",
  2097  		Run: emptyRun,
  2098  	}
  2099  	for i := 2; i <= maxFlags; i += 2 {
  2100  		flagName := fmt.Sprintf("flag%d", i)
  2101  		childCmd.Flags().String(flagName, "", fmt.Sprintf("test %s flag on child", flagName))
  2102  	}
  2103  
  2104  	rootCmd.AddCommand(childCmd)
  2105  
  2106  	// Register completion in different threads to test concurrency.
  2107  	var wg sync.WaitGroup
  2108  	for i := 1; i <= maxFlags; i++ {
  2109  		index := i
  2110  		flagName := fmt.Sprintf("flag%d", i)
  2111  		wg.Add(1)
  2112  		go func() {
  2113  			defer wg.Done()
  2114  			cmd := rootCmd
  2115  			if index%2 == 0 {
  2116  				cmd = childCmd
  2117  			}
  2118  			_ = cmd.RegisterFlagCompletionFunc(flagName, func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2119  				return []string{fmt.Sprintf("flag%d", index)}, ShellCompDirectiveDefault
  2120  			})
  2121  		}()
  2122  	}
  2123  
  2124  	wg.Wait()
  2125  
  2126  	// Test that flag completion works for each flag
  2127  	for i := 1; i <= 6; i++ {
  2128  		var output string
  2129  		var err error
  2130  		flagName := fmt.Sprintf("flag%d", i)
  2131  
  2132  		if i%2 == 1 {
  2133  			output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--"+flagName, "")
  2134  		} else {
  2135  			output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--"+flagName, "")
  2136  		}
  2137  
  2138  		if err != nil {
  2139  			t.Errorf("Unexpected error: %v", err)
  2140  		}
  2141  
  2142  		expected := strings.Join([]string{
  2143  			flagName,
  2144  			":0",
  2145  			"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  2146  
  2147  		if output != expected {
  2148  			t.Errorf("expected: %q, got: %q", expected, output)
  2149  		}
  2150  	}
  2151  }
  2152  
  2153  func TestFlagCompletionInGoWithDesc(t *testing.T) {
  2154  	rootCmd := &Command{
  2155  		Use: "root",
  2156  		Run: emptyRun,
  2157  	}
  2158  	rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
  2159  	assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2160  		completions := []string{}
  2161  		for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
  2162  			if strings.HasPrefix(comp, toComplete) {
  2163  				completions = append(completions, comp)
  2164  			}
  2165  		}
  2166  		return completions, ShellCompDirectiveDefault
  2167  	}))
  2168  	rootCmd.Flags().String("filename", "", "Enter a filename")
  2169  	assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2170  		completions := []string{}
  2171  		for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
  2172  			if strings.HasPrefix(comp, toComplete) {
  2173  				completions = append(completions, comp)
  2174  			}
  2175  		}
  2176  		return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
  2177  	}))
  2178  
  2179  	// Test completing an empty string
  2180  	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "")
  2181  	if err != nil {
  2182  		t.Errorf("Unexpected error: %v", err)
  2183  	}
  2184  
  2185  	expected := strings.Join([]string{
  2186  		"1\tThe first",
  2187  		"2\tThe second",
  2188  		"10\tThe tenth",
  2189  		":0",
  2190  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  2191  
  2192  	if output != expected {
  2193  		t.Errorf("expected: %q, got: %q", expected, output)
  2194  	}
  2195  
  2196  	// Check completing with a prefix
  2197  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "1")
  2198  	if err != nil {
  2199  		t.Errorf("Unexpected error: %v", err)
  2200  	}
  2201  
  2202  	expected = strings.Join([]string{
  2203  		"1\tThe first",
  2204  		"10\tThe tenth",
  2205  		":0",
  2206  		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
  2207  
  2208  	if output != expected {
  2209  		t.Errorf("expected: %q, got: %q", expected, output)
  2210  	}
  2211  
  2212  	// Test completing an empty string
  2213  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "")
  2214  	if err != nil {
  2215  		t.Errorf("Unexpected error: %v", err)
  2216  	}
  2217  
  2218  	expected = strings.Join([]string{
  2219  		"file.yaml\tYAML format",
  2220  		"myfile.json\tJSON format",
  2221  		"file.xml\tXML format",
  2222  		":6",
  2223  		"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
  2224  
  2225  	if output != expected {
  2226  		t.Errorf("expected: %q, got: %q", expected, output)
  2227  	}
  2228  
  2229  	// Check completing with a prefix
  2230  	output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "f")
  2231  	if err != nil {
  2232  		t.Errorf("Unexpected error: %v", err)
  2233  	}
  2234  
  2235  	expected = strings.Join([]string{
  2236  		"file.yaml\tYAML format",
  2237  		"file.xml\tXML format",
  2238  		":6",
  2239  		"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
  2240  
  2241  	if output != expected {
  2242  		t.Errorf("expected: %q, got: %q", expected, output)
  2243  	}
  2244  }
  2245  
  2246  func TestValidArgsNotValidArgsFunc(t *testing.T) {
  2247  	rootCmd := &Command{
  2248  		Use:       "root",
  2249  		ValidArgs: []string{"one", "two"},
  2250  		ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2251  			return []string{"three", "four"}, ShellCompDirectiveNoFileComp
  2252  		},
  2253  		Run: emptyRun,
  2254  	}
  2255  
  2256  	// Test that if both ValidArgs and ValidArgsFunction are present
  2257  	// only ValidArgs is considered
  2258  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
  2259  	if err != nil {
  2260  		t.Errorf("Unexpected error: %v", err)
  2261  	}
  2262  
  2263  	expected := strings.Join([]string{
  2264  		"one",
  2265  		"two",
  2266  		":4",
  2267  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2268  
  2269  	if output != expected {
  2270  		t.Errorf("expected: %q, got: %q", expected, output)
  2271  	}
  2272  
  2273  	// Check completing with a prefix
  2274  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
  2275  	if err != nil {
  2276  		t.Errorf("Unexpected error: %v", err)
  2277  	}
  2278  
  2279  	expected = strings.Join([]string{
  2280  		"two",
  2281  		":4",
  2282  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2283  
  2284  	if output != expected {
  2285  		t.Errorf("expected: %q, got: %q", expected, output)
  2286  	}
  2287  }
  2288  
  2289  func TestArgAliasesCompletionInGo(t *testing.T) {
  2290  	rootCmd := &Command{
  2291  		Use:        "root",
  2292  		Args:       OnlyValidArgs,
  2293  		ValidArgs:  []string{"one", "two", "three"},
  2294  		ArgAliases: []string{"un", "deux", "trois"},
  2295  		Run:        emptyRun,
  2296  	}
  2297  
  2298  	// Test that argaliases are not completed when there are validargs that match
  2299  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
  2300  	if err != nil {
  2301  		t.Errorf("Unexpected error: %v", err)
  2302  	}
  2303  
  2304  	expected := strings.Join([]string{
  2305  		"one",
  2306  		"two",
  2307  		"three",
  2308  		":4",
  2309  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2310  
  2311  	if output != expected {
  2312  		t.Errorf("expected: %q, got: %q", expected, output)
  2313  	}
  2314  
  2315  	// Test that argaliases are not completed when there are validargs that match using a prefix
  2316  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
  2317  	if err != nil {
  2318  		t.Errorf("Unexpected error: %v", err)
  2319  	}
  2320  
  2321  	expected = strings.Join([]string{
  2322  		"two",
  2323  		"three",
  2324  		":4",
  2325  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2326  
  2327  	if output != expected {
  2328  		t.Errorf("expected: %q, got: %q", expected, output)
  2329  	}
  2330  
  2331  	// Test that argaliases are completed when there are no validargs that match
  2332  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "tr")
  2333  	if err != nil {
  2334  		t.Errorf("Unexpected error: %v", err)
  2335  	}
  2336  
  2337  	expected = strings.Join([]string{
  2338  		"trois",
  2339  		":4",
  2340  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2341  
  2342  	if output != expected {
  2343  		t.Errorf("expected: %q, got: %q", expected, output)
  2344  	}
  2345  }
  2346  
  2347  func TestCompleteHelp(t *testing.T) {
  2348  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  2349  	child1Cmd := &Command{
  2350  		Use: "child1",
  2351  		Run: emptyRun,
  2352  	}
  2353  	child2Cmd := &Command{
  2354  		Use: "child2",
  2355  		Run: emptyRun,
  2356  	}
  2357  	rootCmd.AddCommand(child1Cmd, child2Cmd)
  2358  
  2359  	child3Cmd := &Command{
  2360  		Use: "child3",
  2361  		Run: emptyRun,
  2362  	}
  2363  	child1Cmd.AddCommand(child3Cmd)
  2364  
  2365  	// Test that completion includes the help command
  2366  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
  2367  	if err != nil {
  2368  		t.Errorf("Unexpected error: %v", err)
  2369  	}
  2370  
  2371  	expected := strings.Join([]string{
  2372  		"child1",
  2373  		"child2",
  2374  		"completion",
  2375  		"help",
  2376  		":4",
  2377  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2378  
  2379  	if output != expected {
  2380  		t.Errorf("expected: %q, got: %q", expected, output)
  2381  	}
  2382  
  2383  	// Test sub-commands are completed on first level of help command
  2384  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "")
  2385  	if err != nil {
  2386  		t.Errorf("Unexpected error: %v", err)
  2387  	}
  2388  
  2389  	expected = strings.Join([]string{
  2390  		"child1",
  2391  		"child2",
  2392  		"completion",
  2393  		"help", // "<program> help help" is a valid command, so should be completed
  2394  		":4",
  2395  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2396  
  2397  	if output != expected {
  2398  		t.Errorf("expected: %q, got: %q", expected, output)
  2399  	}
  2400  
  2401  	// Test sub-commands are completed on first level of help command
  2402  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "child1", "")
  2403  	if err != nil {
  2404  		t.Errorf("Unexpected error: %v", err)
  2405  	}
  2406  
  2407  	expected = strings.Join([]string{
  2408  		"child3",
  2409  		":4",
  2410  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2411  
  2412  	if output != expected {
  2413  		t.Errorf("expected: %q, got: %q", expected, output)
  2414  	}
  2415  }
  2416  
  2417  func removeCompCmd(rootCmd *Command) {
  2418  	// Remove completion command for the next test
  2419  	for _, cmd := range rootCmd.commands {
  2420  		if cmd.Name() == compCmdName {
  2421  			rootCmd.RemoveCommand(cmd)
  2422  			return
  2423  		}
  2424  	}
  2425  }
  2426  
  2427  func TestDefaultCompletionCmd(t *testing.T) {
  2428  	rootCmd := &Command{
  2429  		Use:  "root",
  2430  		Args: NoArgs,
  2431  		Run:  emptyRun,
  2432  	}
  2433  
  2434  	// Test that no completion command is created if there are not other sub-commands
  2435  	assertNoErr(t, rootCmd.Execute())
  2436  	for _, cmd := range rootCmd.commands {
  2437  		if cmd.Name() == compCmdName {
  2438  			t.Errorf("Should not have a 'completion' command when there are no other sub-commands of root")
  2439  			break
  2440  		}
  2441  	}
  2442  
  2443  	subCmd := &Command{
  2444  		Use: "sub",
  2445  		Run: emptyRun,
  2446  	}
  2447  	rootCmd.AddCommand(subCmd)
  2448  
  2449  	// Test that a completion command is created if there are other sub-commands
  2450  	found := false
  2451  	assertNoErr(t, rootCmd.Execute())
  2452  	for _, cmd := range rootCmd.commands {
  2453  		if cmd.Name() == compCmdName {
  2454  			found = true
  2455  			break
  2456  		}
  2457  	}
  2458  	if !found {
  2459  		t.Errorf("Should have a 'completion' command when there are other sub-commands of root")
  2460  	}
  2461  	// Remove completion command for the next test
  2462  	removeCompCmd(rootCmd)
  2463  
  2464  	// Test that the default completion command can be disabled
  2465  	rootCmd.CompletionOptions.DisableDefaultCmd = true
  2466  	assertNoErr(t, rootCmd.Execute())
  2467  	for _, cmd := range rootCmd.commands {
  2468  		if cmd.Name() == compCmdName {
  2469  			t.Errorf("Should not have a 'completion' command when the feature is disabled")
  2470  			break
  2471  		}
  2472  	}
  2473  	// Re-enable for next test
  2474  	rootCmd.CompletionOptions.DisableDefaultCmd = false
  2475  
  2476  	// Test that completion descriptions are enabled by default
  2477  	output, err := executeCommand(rootCmd, compCmdName, "zsh")
  2478  	if err != nil {
  2479  		t.Errorf("Unexpected error: %v", err)
  2480  	}
  2481  
  2482  	check(t, output, ShellCompRequestCmd)
  2483  	checkOmit(t, output, ShellCompNoDescRequestCmd)
  2484  	// Remove completion command for the next test
  2485  	removeCompCmd(rootCmd)
  2486  
  2487  	// Test that completion descriptions can be disabled completely
  2488  	rootCmd.CompletionOptions.DisableDescriptions = true
  2489  	output, err = executeCommand(rootCmd, compCmdName, "zsh")
  2490  	if err != nil {
  2491  		t.Errorf("Unexpected error: %v", err)
  2492  	}
  2493  
  2494  	check(t, output, ShellCompNoDescRequestCmd)
  2495  	// Re-enable for next test
  2496  	rootCmd.CompletionOptions.DisableDescriptions = false
  2497  	// Remove completion command for the next test
  2498  	removeCompCmd(rootCmd)
  2499  
  2500  	var compCmd *Command
  2501  	// Test that the --no-descriptions flag is present on all shells
  2502  	assertNoErr(t, rootCmd.Execute())
  2503  	for _, shell := range []string{"bash", "fish", "powershell", "zsh"} {
  2504  		if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
  2505  			t.Errorf("Unexpected error: %v", err)
  2506  		}
  2507  		if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag == nil {
  2508  			t.Errorf("Missing --%s flag for %s shell", compCmdNoDescFlagName, shell)
  2509  		}
  2510  	}
  2511  	// Remove completion command for the next test
  2512  	removeCompCmd(rootCmd)
  2513  
  2514  	// Test that the '--no-descriptions' flag can be disabled
  2515  	rootCmd.CompletionOptions.DisableNoDescFlag = true
  2516  	assertNoErr(t, rootCmd.Execute())
  2517  	for _, shell := range []string{"fish", "zsh", "bash", "powershell"} {
  2518  		if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
  2519  			t.Errorf("Unexpected error: %v", err)
  2520  		}
  2521  		if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil {
  2522  			t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell)
  2523  		}
  2524  	}
  2525  	// Re-enable for next test
  2526  	rootCmd.CompletionOptions.DisableNoDescFlag = false
  2527  	// Remove completion command for the next test
  2528  	removeCompCmd(rootCmd)
  2529  
  2530  	// Test that the '--no-descriptions' flag is disabled when descriptions are disabled
  2531  	rootCmd.CompletionOptions.DisableDescriptions = true
  2532  	assertNoErr(t, rootCmd.Execute())
  2533  	for _, shell := range []string{"fish", "zsh", "bash", "powershell"} {
  2534  		if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
  2535  			t.Errorf("Unexpected error: %v", err)
  2536  		}
  2537  		if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil {
  2538  			t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell)
  2539  		}
  2540  	}
  2541  	// Re-enable for next test
  2542  	rootCmd.CompletionOptions.DisableDescriptions = false
  2543  	// Remove completion command for the next test
  2544  	removeCompCmd(rootCmd)
  2545  
  2546  	// Test that the 'completion' command can be hidden
  2547  	rootCmd.CompletionOptions.HiddenDefaultCmd = true
  2548  	assertNoErr(t, rootCmd.Execute())
  2549  	compCmd, _, err = rootCmd.Find([]string{compCmdName})
  2550  	if err != nil {
  2551  		t.Errorf("Unexpected error: %v", err)
  2552  	}
  2553  	if compCmd.Hidden == false {
  2554  		t.Error("Default 'completion' command should be hidden but it is not")
  2555  	}
  2556  	// Re-enable for next test
  2557  	rootCmd.CompletionOptions.HiddenDefaultCmd = false
  2558  	// Remove completion command for the next test
  2559  	removeCompCmd(rootCmd)
  2560  }
  2561  
  2562  func TestCompleteCompletion(t *testing.T) {
  2563  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  2564  	subCmd := &Command{
  2565  		Use: "sub",
  2566  		Run: emptyRun,
  2567  	}
  2568  	rootCmd.AddCommand(subCmd)
  2569  
  2570  	// Test sub-commands of the completion command
  2571  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "completion", "")
  2572  	if err != nil {
  2573  		t.Errorf("Unexpected error: %v", err)
  2574  	}
  2575  
  2576  	expected := strings.Join([]string{
  2577  		"bash",
  2578  		"fish",
  2579  		"powershell",
  2580  		"zsh",
  2581  		":4",
  2582  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2583  
  2584  	if output != expected {
  2585  		t.Errorf("expected: %q, got: %q", expected, output)
  2586  	}
  2587  
  2588  	// Test there are no completions for the sub-commands of the completion command
  2589  	var compCmd *Command
  2590  	for _, cmd := range rootCmd.Commands() {
  2591  		if cmd.Name() == compCmdName {
  2592  			compCmd = cmd
  2593  			break
  2594  		}
  2595  	}
  2596  
  2597  	for _, shell := range compCmd.Commands() {
  2598  		output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, compCmdName, shell.Name(), "")
  2599  		if err != nil {
  2600  			t.Errorf("Unexpected error: %v", err)
  2601  		}
  2602  
  2603  		expected = strings.Join([]string{
  2604  			":4",
  2605  			"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2606  
  2607  		if output != expected {
  2608  			t.Errorf("expected: %q, got: %q", expected, output)
  2609  		}
  2610  	}
  2611  }
  2612  
  2613  func TestMultipleShorthandFlagCompletion(t *testing.T) {
  2614  	rootCmd := &Command{
  2615  		Use:       "root",
  2616  		ValidArgs: []string{"foo", "bar"},
  2617  		Run:       emptyRun,
  2618  	}
  2619  	f := rootCmd.Flags()
  2620  	f.BoolP("short", "s", false, "short flag 1")
  2621  	f.BoolP("short2", "d", false, "short flag 2")
  2622  	f.StringP("short3", "f", "", "short flag 3")
  2623  	_ = rootCmd.RegisterFlagCompletionFunc("short3", func(*Command, []string, string) ([]string, ShellCompDirective) {
  2624  		return []string{"works"}, ShellCompDirectiveNoFileComp
  2625  	})
  2626  
  2627  	// Test that a single shorthand flag works
  2628  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "")
  2629  	if err != nil {
  2630  		t.Errorf("Unexpected error: %v", err)
  2631  	}
  2632  
  2633  	expected := strings.Join([]string{
  2634  		"foo",
  2635  		"bar",
  2636  		":4",
  2637  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2638  
  2639  	if output != expected {
  2640  		t.Errorf("expected: %q, got: %q", expected, output)
  2641  	}
  2642  
  2643  	// Test that multiple boolean shorthand flags work
  2644  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sd", "")
  2645  	if err != nil {
  2646  		t.Errorf("Unexpected error: %v", err)
  2647  	}
  2648  
  2649  	expected = strings.Join([]string{
  2650  		"foo",
  2651  		"bar",
  2652  		":4",
  2653  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2654  
  2655  	if output != expected {
  2656  		t.Errorf("expected: %q, got: %q", expected, output)
  2657  	}
  2658  
  2659  	// Test that multiple boolean + string shorthand flags work
  2660  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf", "")
  2661  	if err != nil {
  2662  		t.Errorf("Unexpected error: %v", err)
  2663  	}
  2664  
  2665  	expected = strings.Join([]string{
  2666  		"works",
  2667  		":4",
  2668  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2669  
  2670  	if output != expected {
  2671  		t.Errorf("expected: %q, got: %q", expected, output)
  2672  	}
  2673  
  2674  	// Test that multiple boolean + string with equal sign shorthand flags work
  2675  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=")
  2676  	if err != nil {
  2677  		t.Errorf("Unexpected error: %v", err)
  2678  	}
  2679  
  2680  	expected = strings.Join([]string{
  2681  		"works",
  2682  		":4",
  2683  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2684  
  2685  	if output != expected {
  2686  		t.Errorf("expected: %q, got: %q", expected, output)
  2687  	}
  2688  
  2689  	// Test that multiple boolean + string with equal sign with value shorthand flags work
  2690  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=abc", "")
  2691  	if err != nil {
  2692  		t.Errorf("Unexpected error: %v", err)
  2693  	}
  2694  
  2695  	expected = strings.Join([]string{
  2696  		"foo",
  2697  		"bar",
  2698  		":4",
  2699  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2700  
  2701  	if output != expected {
  2702  		t.Errorf("expected: %q, got: %q", expected, output)
  2703  	}
  2704  }
  2705  
  2706  func TestCompleteWithDisableFlagParsing(t *testing.T) {
  2707  
  2708  	flagValidArgs := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2709  		return []string{"--flag", "-f"}, ShellCompDirectiveNoFileComp
  2710  	}
  2711  
  2712  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  2713  	childCmd := &Command{
  2714  		Use:                "child",
  2715  		Run:                emptyRun,
  2716  		DisableFlagParsing: true,
  2717  		ValidArgsFunction:  flagValidArgs,
  2718  	}
  2719  	rootCmd.AddCommand(childCmd)
  2720  
  2721  	rootCmd.PersistentFlags().StringP("persistent", "p", "", "persistent flag")
  2722  	childCmd.Flags().StringP("nonPersistent", "n", "", "non-persistent flag")
  2723  
  2724  	// Test that when DisableFlagParsing==true, ValidArgsFunction is called to complete flag names,
  2725  	// after Cobra tried to complete the flags it knows about.
  2726  	childCmd.DisableFlagParsing = true
  2727  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "-")
  2728  	if err != nil {
  2729  		t.Errorf("Unexpected error: %v", err)
  2730  	}
  2731  
  2732  	expected := strings.Join([]string{
  2733  		"--persistent",
  2734  		"-p",
  2735  		"--nonPersistent",
  2736  		"-n",
  2737  		"--flag",
  2738  		"-f",
  2739  		":4",
  2740  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2741  
  2742  	if output != expected {
  2743  		t.Errorf("expected: %q, got: %q", expected, output)
  2744  	}
  2745  
  2746  	// Test that when DisableFlagParsing==false, Cobra completes the flags itself and ValidArgsFunction is not called
  2747  	childCmd.DisableFlagParsing = false
  2748  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "-")
  2749  	if err != nil {
  2750  		t.Errorf("Unexpected error: %v", err)
  2751  	}
  2752  
  2753  	// Cobra was not told of any flags, so it returns nothing
  2754  	expected = strings.Join([]string{
  2755  		"--persistent",
  2756  		"-p",
  2757  		"--help",
  2758  		"-h",
  2759  		"--nonPersistent",
  2760  		"-n",
  2761  		":4",
  2762  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2763  
  2764  	if output != expected {
  2765  		t.Errorf("expected: %q, got: %q", expected, output)
  2766  	}
  2767  }
  2768  
  2769  func TestCompleteWithRootAndLegacyArgs(t *testing.T) {
  2770  	// Test a lonely root command which uses legacyArgs().  In such a case, the root
  2771  	// command should accept any number of arguments and completion should behave accordingly.
  2772  	rootCmd := &Command{
  2773  		Use:  "root",
  2774  		Args: nil, // Args must be nil to trigger the legacyArgs() function
  2775  		Run:  emptyRun,
  2776  		ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2777  			return []string{"arg1", "arg2"}, ShellCompDirectiveNoFileComp
  2778  		},
  2779  	}
  2780  
  2781  	// Make sure the first arg is completed
  2782  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
  2783  	if err != nil {
  2784  		t.Errorf("Unexpected error: %v", err)
  2785  	}
  2786  
  2787  	expected := strings.Join([]string{
  2788  		"arg1",
  2789  		"arg2",
  2790  		":4",
  2791  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2792  
  2793  	if output != expected {
  2794  		t.Errorf("expected: %q, got: %q", expected, output)
  2795  	}
  2796  
  2797  	// Make sure the completion of arguments continues
  2798  	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "arg1", "")
  2799  	if err != nil {
  2800  		t.Errorf("Unexpected error: %v", err)
  2801  	}
  2802  
  2803  	expected = strings.Join([]string{
  2804  		"arg1",
  2805  		"arg2",
  2806  		":4",
  2807  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2808  
  2809  	if output != expected {
  2810  		t.Errorf("expected: %q, got: %q", expected, output)
  2811  	}
  2812  }
  2813  
  2814  func TestFixedCompletions(t *testing.T) {
  2815  	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
  2816  	choices := []string{"apple", "banana", "orange"}
  2817  	childCmd := &Command{
  2818  		Use:               "child",
  2819  		ValidArgsFunction: FixedCompletions(choices, ShellCompDirectiveNoFileComp),
  2820  		Run:               emptyRun,
  2821  	}
  2822  	rootCmd.AddCommand(childCmd)
  2823  
  2824  	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "a")
  2825  	if err != nil {
  2826  		t.Errorf("Unexpected error: %v", err)
  2827  	}
  2828  
  2829  	expected := strings.Join([]string{
  2830  		"apple",
  2831  		"banana",
  2832  		"orange",
  2833  		":4",
  2834  		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
  2835  
  2836  	if output != expected {
  2837  		t.Errorf("expected: %q, got: %q", expected, output)
  2838  	}
  2839  }
  2840  
  2841  func TestCompletionForGroupedFlags(t *testing.T) {
  2842  	getCmd := func() *Command {
  2843  		rootCmd := &Command{
  2844  			Use: "root",
  2845  			Run: emptyRun,
  2846  		}
  2847  		childCmd := &Command{
  2848  			Use: "child",
  2849  			ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2850  				return []string{"subArg"}, ShellCompDirectiveNoFileComp
  2851  			},
  2852  			Run: emptyRun,
  2853  		}
  2854  		rootCmd.AddCommand(childCmd)
  2855  
  2856  		rootCmd.PersistentFlags().Int("ingroup1", -1, "ingroup1")
  2857  		rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2")
  2858  
  2859  		childCmd.Flags().Bool("ingroup3", false, "ingroup3")
  2860  		childCmd.Flags().Bool("nogroup", false, "nogroup")
  2861  
  2862  		// Add flags to a group
  2863  		childCmd.MarkFlagsRequiredTogether("ingroup1", "ingroup2", "ingroup3")
  2864  
  2865  		return rootCmd
  2866  	}
  2867  
  2868  	// Each test case uses a unique command from the function above.
  2869  	testcases := []struct {
  2870  		desc           string
  2871  		args           []string
  2872  		expectedOutput string
  2873  	}{
  2874  		{
  2875  			desc: "flags in group not suggested without - prefix",
  2876  			args: []string{"child", ""},
  2877  			expectedOutput: strings.Join([]string{
  2878  				"subArg",
  2879  				":4",
  2880  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  2881  		},
  2882  		{
  2883  			desc: "flags in group suggested with - prefix",
  2884  			args: []string{"child", "-"},
  2885  			expectedOutput: strings.Join([]string{
  2886  				"--ingroup1",
  2887  				"--ingroup2",
  2888  				"--help",
  2889  				"-h",
  2890  				"--ingroup3",
  2891  				"--nogroup",
  2892  				":4",
  2893  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  2894  		},
  2895  		{
  2896  			desc: "when flag in group present, other flags in group suggested even without - prefix",
  2897  			args: []string{"child", "--ingroup2", "value", ""},
  2898  			expectedOutput: strings.Join([]string{
  2899  				"--ingroup1",
  2900  				"--ingroup3",
  2901  				"subArg",
  2902  				":4",
  2903  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  2904  		},
  2905  		{
  2906  			desc: "when all flags in group present, flags not suggested without - prefix",
  2907  			args: []string{"child", "--ingroup1", "8", "--ingroup2", "value2", "--ingroup3", ""},
  2908  			expectedOutput: strings.Join([]string{
  2909  				"subArg",
  2910  				":4",
  2911  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  2912  		},
  2913  		{
  2914  			desc: "group ignored if some flags not applicable",
  2915  			args: []string{"--ingroup2", "value", ""},
  2916  			expectedOutput: strings.Join([]string{
  2917  				"child",
  2918  				"completion",
  2919  				"help",
  2920  				":4",
  2921  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  2922  		},
  2923  	}
  2924  
  2925  	for _, tc := range testcases {
  2926  		t.Run(tc.desc, func(t *testing.T) {
  2927  			c := getCmd()
  2928  			args := []string{ShellCompNoDescRequestCmd}
  2929  			args = append(args, tc.args...)
  2930  			output, err := executeCommand(c, args...)
  2931  			switch {
  2932  			case err == nil && output != tc.expectedOutput:
  2933  				t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
  2934  			case err != nil:
  2935  				t.Errorf("Unexpected error %q", err)
  2936  			}
  2937  		})
  2938  	}
  2939  }
  2940  
  2941  func TestCompletionForOneRequiredGroupFlags(t *testing.T) {
  2942  	getCmd := func() *Command {
  2943  		rootCmd := &Command{
  2944  			Use: "root",
  2945  			Run: emptyRun,
  2946  		}
  2947  		childCmd := &Command{
  2948  			Use: "child",
  2949  			ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  2950  				return []string{"subArg"}, ShellCompDirectiveNoFileComp
  2951  			},
  2952  			Run: emptyRun,
  2953  		}
  2954  		rootCmd.AddCommand(childCmd)
  2955  
  2956  		rootCmd.PersistentFlags().Int("ingroup1", -1, "ingroup1")
  2957  		rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2")
  2958  
  2959  		childCmd.Flags().Bool("ingroup3", false, "ingroup3")
  2960  		childCmd.Flags().Bool("nogroup", false, "nogroup")
  2961  
  2962  		// Add flags to a group
  2963  		childCmd.MarkFlagsOneRequired("ingroup1", "ingroup2", "ingroup3")
  2964  
  2965  		return rootCmd
  2966  	}
  2967  
  2968  	// Each test case uses a unique command from the function above.
  2969  	testcases := []struct {
  2970  		desc           string
  2971  		args           []string
  2972  		expectedOutput string
  2973  	}{
  2974  		{
  2975  			desc: "flags in group suggested without - prefix",
  2976  			args: []string{"child", ""},
  2977  			expectedOutput: strings.Join([]string{
  2978  				"--ingroup1",
  2979  				"--ingroup2",
  2980  				"--ingroup3",
  2981  				"subArg",
  2982  				":4",
  2983  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  2984  		},
  2985  		{
  2986  			desc: "flags in group suggested with - prefix",
  2987  			args: []string{"child", "-"},
  2988  			expectedOutput: strings.Join([]string{
  2989  				"--ingroup1",
  2990  				"--ingroup2",
  2991  				"--ingroup3",
  2992  				":4",
  2993  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  2994  		},
  2995  		{
  2996  			desc: "when any flag in group present, other flags in group not suggested without - prefix",
  2997  			args: []string{"child", "--ingroup2", "value", ""},
  2998  			expectedOutput: strings.Join([]string{
  2999  				"subArg",
  3000  				":4",
  3001  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3002  		},
  3003  		{
  3004  			desc: "when all flags in group present, flags not suggested without - prefix",
  3005  			args: []string{"child", "--ingroup1", "8", "--ingroup2", "value2", "--ingroup3", ""},
  3006  			expectedOutput: strings.Join([]string{
  3007  				"subArg",
  3008  				":4",
  3009  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3010  		},
  3011  		{
  3012  			desc: "group ignored if some flags not applicable",
  3013  			args: []string{"--ingroup2", "value", ""},
  3014  			expectedOutput: strings.Join([]string{
  3015  				"child",
  3016  				"completion",
  3017  				"help",
  3018  				":4",
  3019  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3020  		},
  3021  	}
  3022  
  3023  	for _, tc := range testcases {
  3024  		t.Run(tc.desc, func(t *testing.T) {
  3025  			c := getCmd()
  3026  			args := []string{ShellCompNoDescRequestCmd}
  3027  			args = append(args, tc.args...)
  3028  			output, err := executeCommand(c, args...)
  3029  			switch {
  3030  			case err == nil && output != tc.expectedOutput:
  3031  				t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
  3032  			case err != nil:
  3033  				t.Errorf("Unexpected error %q", err)
  3034  			}
  3035  		})
  3036  	}
  3037  }
  3038  
  3039  func TestCompletionForMutuallyExclusiveFlags(t *testing.T) {
  3040  	getCmd := func() *Command {
  3041  		rootCmd := &Command{
  3042  			Use: "root",
  3043  			Run: emptyRun,
  3044  		}
  3045  		childCmd := &Command{
  3046  			Use: "child",
  3047  			ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  3048  				return []string{"subArg"}, ShellCompDirectiveNoFileComp
  3049  			},
  3050  			Run: emptyRun,
  3051  		}
  3052  		rootCmd.AddCommand(childCmd)
  3053  
  3054  		rootCmd.PersistentFlags().IntSlice("ingroup1", []int{1}, "ingroup1")
  3055  		rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2")
  3056  
  3057  		childCmd.Flags().Bool("ingroup3", false, "ingroup3")
  3058  		childCmd.Flags().Bool("nogroup", false, "nogroup")
  3059  
  3060  		// Add flags to a group
  3061  		childCmd.MarkFlagsMutuallyExclusive("ingroup1", "ingroup2", "ingroup3")
  3062  
  3063  		return rootCmd
  3064  	}
  3065  
  3066  	// Each test case uses a unique command from the function above.
  3067  	testcases := []struct {
  3068  		desc           string
  3069  		args           []string
  3070  		expectedOutput string
  3071  	}{
  3072  		{
  3073  			desc: "flags in mutually exclusive group not suggested without the - prefix",
  3074  			args: []string{"child", ""},
  3075  			expectedOutput: strings.Join([]string{
  3076  				"subArg",
  3077  				":4",
  3078  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3079  		},
  3080  		{
  3081  			desc: "flags in mutually exclusive group suggested with the - prefix",
  3082  			args: []string{"child", "-"},
  3083  			expectedOutput: strings.Join([]string{
  3084  				"--ingroup1",
  3085  				"--ingroup2",
  3086  				"--help",
  3087  				"-h",
  3088  				"--ingroup3",
  3089  				"--nogroup",
  3090  				":4",
  3091  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3092  		},
  3093  		{
  3094  			desc: "when flag in mutually exclusive group present, other flags in group not suggested even with the - prefix",
  3095  			args: []string{"child", "--ingroup1", "8", "-"},
  3096  			expectedOutput: strings.Join([]string{
  3097  				"--ingroup1", // Should be suggested again since it is a slice
  3098  				"--help",
  3099  				"-h",
  3100  				"--nogroup",
  3101  				":4",
  3102  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3103  		},
  3104  		{
  3105  			desc: "group ignored if some flags not applicable",
  3106  			args: []string{"--ingroup1", "8", "-"},
  3107  			expectedOutput: strings.Join([]string{
  3108  				"--help",
  3109  				"-h",
  3110  				"--ingroup1",
  3111  				"--ingroup2",
  3112  				":4",
  3113  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3114  		},
  3115  	}
  3116  
  3117  	for _, tc := range testcases {
  3118  		t.Run(tc.desc, func(t *testing.T) {
  3119  			c := getCmd()
  3120  			args := []string{ShellCompNoDescRequestCmd}
  3121  			args = append(args, tc.args...)
  3122  			output, err := executeCommand(c, args...)
  3123  			switch {
  3124  			case err == nil && output != tc.expectedOutput:
  3125  				t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
  3126  			case err != nil:
  3127  				t.Errorf("Unexpected error %q", err)
  3128  			}
  3129  		})
  3130  	}
  3131  }
  3132  
  3133  func TestCompletionCobraFlags(t *testing.T) {
  3134  	getCmd := func() *Command {
  3135  		rootCmd := &Command{
  3136  			Use:     "root",
  3137  			Version: "1.1.1",
  3138  			Run:     emptyRun,
  3139  		}
  3140  		childCmd := &Command{
  3141  			Use:     "child",
  3142  			Version: "1.1.1",
  3143  			Run:     emptyRun,
  3144  			ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  3145  				return []string{"extra"}, ShellCompDirectiveNoFileComp
  3146  			},
  3147  		}
  3148  		childCmd2 := &Command{
  3149  			Use:     "child2",
  3150  			Version: "1.1.1",
  3151  			Run:     emptyRun,
  3152  			ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  3153  				return []string{"extra2"}, ShellCompDirectiveNoFileComp
  3154  			},
  3155  		}
  3156  		childCmd3 := &Command{
  3157  			Use:     "child3",
  3158  			Version: "1.1.1",
  3159  			Run:     emptyRun,
  3160  			ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  3161  				return []string{"extra3"}, ShellCompDirectiveNoFileComp
  3162  			},
  3163  		}
  3164  		childCmd4 := &Command{
  3165  			Use:     "child4",
  3166  			Version: "1.1.1",
  3167  			Run:     emptyRun,
  3168  			ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  3169  				return []string{"extra4"}, ShellCompDirectiveNoFileComp
  3170  			},
  3171  			DisableFlagParsing: true,
  3172  		}
  3173  		childCmd5 := &Command{
  3174  			Use:     "child5",
  3175  			Version: "1.1.1",
  3176  			Run:     emptyRun,
  3177  			ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  3178  				return []string{"extra5"}, ShellCompDirectiveNoFileComp
  3179  			},
  3180  			DisableFlagParsing: true,
  3181  		}
  3182  
  3183  		rootCmd.AddCommand(childCmd, childCmd2, childCmd3, childCmd4, childCmd5)
  3184  
  3185  		_ = childCmd.Flags().Bool("bool", false, "A bool flag")
  3186  		_ = childCmd.MarkFlagRequired("bool")
  3187  
  3188  		// Have a command that adds its own help and version flag
  3189  		_ = childCmd2.Flags().BoolP("help", "h", false, "My own help")
  3190  		_ = childCmd2.Flags().BoolP("version", "v", false, "My own version")
  3191  
  3192  		// Have a command that only adds its own -v flag
  3193  		_ = childCmd3.Flags().BoolP("verbose", "v", false, "Not a version flag")
  3194  
  3195  		// Have a command that DisablesFlagParsing but that also adds its own help and version flags
  3196  		_ = childCmd5.Flags().BoolP("help", "h", false, "My own help")
  3197  		_ = childCmd5.Flags().BoolP("version", "v", false, "My own version")
  3198  
  3199  		return rootCmd
  3200  	}
  3201  
  3202  	// Each test case uses a unique command from the function above.
  3203  	testcases := []struct {
  3204  		desc           string
  3205  		args           []string
  3206  		expectedOutput string
  3207  	}{
  3208  		{
  3209  			desc: "completion of help and version flags",
  3210  			args: []string{"-"},
  3211  			expectedOutput: strings.Join([]string{
  3212  				"--help",
  3213  				"-h",
  3214  				"--version",
  3215  				"-v",
  3216  				":4",
  3217  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3218  		},
  3219  		{
  3220  			desc: "no completion after --help flag",
  3221  			args: []string{"--help", ""},
  3222  			expectedOutput: strings.Join([]string{
  3223  				":4",
  3224  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3225  		},
  3226  		{
  3227  			desc: "no completion after -h flag",
  3228  			args: []string{"-h", ""},
  3229  			expectedOutput: strings.Join([]string{
  3230  				":4",
  3231  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3232  		},
  3233  		{
  3234  			desc: "no completion after --version flag",
  3235  			args: []string{"--version", ""},
  3236  			expectedOutput: strings.Join([]string{
  3237  				":4",
  3238  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3239  		},
  3240  		{
  3241  			desc: "no completion after -v flag",
  3242  			args: []string{"-v", ""},
  3243  			expectedOutput: strings.Join([]string{
  3244  				":4",
  3245  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3246  		},
  3247  		{
  3248  			desc: "no completion after --help flag even with other completions",
  3249  			args: []string{"child", "--help", ""},
  3250  			expectedOutput: strings.Join([]string{
  3251  				":4",
  3252  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3253  		},
  3254  		{
  3255  			desc: "no completion after -h flag even with other completions",
  3256  			args: []string{"child", "-h", ""},
  3257  			expectedOutput: strings.Join([]string{
  3258  				":4",
  3259  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3260  		},
  3261  		{
  3262  			desc: "no completion after --version flag even with other completions",
  3263  			args: []string{"child", "--version", ""},
  3264  			expectedOutput: strings.Join([]string{
  3265  				":4",
  3266  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3267  		},
  3268  		{
  3269  			desc: "no completion after -v flag even with other completions",
  3270  			args: []string{"child", "-v", ""},
  3271  			expectedOutput: strings.Join([]string{
  3272  				":4",
  3273  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3274  		},
  3275  		{
  3276  			desc: "no completion after -v flag even with other flag completions",
  3277  			args: []string{"child", "-v", "-"},
  3278  			expectedOutput: strings.Join([]string{
  3279  				":4",
  3280  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3281  		},
  3282  		{
  3283  			desc: "completion after --help flag when created by program",
  3284  			args: []string{"child2", "--help", ""},
  3285  			expectedOutput: strings.Join([]string{
  3286  				"extra2",
  3287  				":4",
  3288  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3289  		},
  3290  		{
  3291  			desc: "completion after -h flag when created by program",
  3292  			args: []string{"child2", "-h", ""},
  3293  			expectedOutput: strings.Join([]string{
  3294  				"extra2",
  3295  				":4",
  3296  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3297  		},
  3298  		{
  3299  			desc: "completion after --version flag when created by program",
  3300  			args: []string{"child2", "--version", ""},
  3301  			expectedOutput: strings.Join([]string{
  3302  				"extra2",
  3303  				":4",
  3304  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3305  		},
  3306  		{
  3307  			desc: "completion after -v flag when created by program",
  3308  			args: []string{"child2", "-v", ""},
  3309  			expectedOutput: strings.Join([]string{
  3310  				"extra2",
  3311  				":4",
  3312  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3313  		},
  3314  		{
  3315  			desc: "completion after --version when only -v flag was created by program",
  3316  			args: []string{"child3", "--version", ""},
  3317  			expectedOutput: strings.Join([]string{
  3318  				":4",
  3319  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3320  		},
  3321  		{
  3322  			desc: "completion after -v flag when only -v flag was created by program",
  3323  			args: []string{"child3", "-v", ""},
  3324  			expectedOutput: strings.Join([]string{
  3325  				"extra3",
  3326  				":4",
  3327  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3328  		},
  3329  		{
  3330  			desc: "no completion for --help/-h and --version/-v flags when DisableFlagParsing=true",
  3331  			args: []string{"child4", "-"},
  3332  			expectedOutput: strings.Join([]string{
  3333  				"extra4",
  3334  				":4",
  3335  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3336  		},
  3337  		{
  3338  			desc: "completions for program-defined --help/-h and --version/-v flags even when DisableFlagParsing=true",
  3339  			args: []string{"child5", "-"},
  3340  			expectedOutput: strings.Join([]string{
  3341  				"--help",
  3342  				"-h",
  3343  				"--version",
  3344  				"-v",
  3345  				"extra5",
  3346  				":4",
  3347  				"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
  3348  		},
  3349  	}
  3350  
  3351  	for _, tc := range testcases {
  3352  		t.Run(tc.desc, func(t *testing.T) {
  3353  			c := getCmd()
  3354  			args := []string{ShellCompNoDescRequestCmd}
  3355  			args = append(args, tc.args...)
  3356  			output, err := executeCommand(c, args...)
  3357  			switch {
  3358  			case err == nil && output != tc.expectedOutput:
  3359  				t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
  3360  			case err != nil:
  3361  				t.Errorf("Unexpected error %q", err)
  3362  			}
  3363  		})
  3364  	}
  3365  }
  3366  
  3367  func TestArgsNotDetectedAsFlagsCompletionInGo(t *testing.T) {
  3368  	// Regression test that ensures the bug described in
  3369  	// https://github.com/spf13/cobra/issues/1816 does not occur anymore.
  3370  
  3371  	root := Command{
  3372  		Use: "root",
  3373  		ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  3374  			return []string{"service", "1-123", "11-123"}, ShellCompDirectiveNoFileComp
  3375  		},
  3376  	}
  3377  
  3378  	completion := `service
  3379  1-123
  3380  11-123
  3381  :4
  3382  Completion ended with directive: ShellCompDirectiveNoFileComp
  3383  `
  3384  
  3385  	testcases := []struct {
  3386  		desc           string
  3387  		args           []string
  3388  		expectedOutput string
  3389  	}{
  3390  		{
  3391  			desc:           "empty",
  3392  			args:           []string{""},
  3393  			expectedOutput: completion,
  3394  		},
  3395  		{
  3396  			desc:           "service only",
  3397  			args:           []string{"service", ""},
  3398  			expectedOutput: completion,
  3399  		},
  3400  		{
  3401  			desc:           "service last",
  3402  			args:           []string{"1-123", "service", ""},
  3403  			expectedOutput: completion,
  3404  		},
  3405  		{
  3406  			desc:           "two digit prefixed dash last",
  3407  			args:           []string{"service", "11-123", ""},
  3408  			expectedOutput: completion,
  3409  		},
  3410  		{
  3411  			desc:           "one digit prefixed dash last",
  3412  			args:           []string{"service", "1-123", ""},
  3413  			expectedOutput: completion,
  3414  		},
  3415  	}
  3416  	for _, tc := range testcases {
  3417  		t.Run(tc.desc, func(t *testing.T) {
  3418  			args := []string{ShellCompNoDescRequestCmd}
  3419  			args = append(args, tc.args...)
  3420  			output, err := executeCommand(&root, args...)
  3421  			switch {
  3422  			case err == nil && output != tc.expectedOutput:
  3423  				t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
  3424  			case err != nil:
  3425  				t.Errorf("Unexpected error %q", err)
  3426  			}
  3427  		})
  3428  	}
  3429  }
  3430  
  3431  func TestGetFlagCompletion(t *testing.T) {
  3432  	rootCmd := &Command{Use: "root", Run: emptyRun}
  3433  
  3434  	rootCmd.Flags().String("rootflag", "", "root flag")
  3435  	_ = rootCmd.RegisterFlagCompletionFunc("rootflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  3436  		return []string{"rootvalue"}, ShellCompDirectiveKeepOrder
  3437  	})
  3438  
  3439  	rootCmd.PersistentFlags().String("persistentflag", "", "persistent flag")
  3440  	_ = rootCmd.RegisterFlagCompletionFunc("persistentflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  3441  		return []string{"persistentvalue"}, ShellCompDirectiveDefault
  3442  	})
  3443  
  3444  	childCmd := &Command{Use: "child", Run: emptyRun}
  3445  
  3446  	childCmd.Flags().String("childflag", "", "child flag")
  3447  	_ = childCmd.RegisterFlagCompletionFunc("childflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
  3448  		return []string{"childvalue"}, ShellCompDirectiveNoFileComp | ShellCompDirectiveNoSpace
  3449  	})
  3450  
  3451  	rootCmd.AddCommand(childCmd)
  3452  
  3453  	testcases := []struct {
  3454  		desc      string
  3455  		cmd       *Command
  3456  		flagName  string
  3457  		exists    bool
  3458  		comps     []string
  3459  		directive ShellCompDirective
  3460  	}{
  3461  		{
  3462  			desc:      "get flag completion function for command",
  3463  			cmd:       rootCmd,
  3464  			flagName:  "rootflag",
  3465  			exists:    true,
  3466  			comps:     []string{"rootvalue"},
  3467  			directive: ShellCompDirectiveKeepOrder,
  3468  		},
  3469  		{
  3470  			desc:      "get persistent flag completion function for command",
  3471  			cmd:       rootCmd,
  3472  			flagName:  "persistentflag",
  3473  			exists:    true,
  3474  			comps:     []string{"persistentvalue"},
  3475  			directive: ShellCompDirectiveDefault,
  3476  		},
  3477  		{
  3478  			desc:      "get flag completion function for child command",
  3479  			cmd:       childCmd,
  3480  			flagName:  "childflag",
  3481  			exists:    true,
  3482  			comps:     []string{"childvalue"},
  3483  			directive: ShellCompDirectiveNoFileComp | ShellCompDirectiveNoSpace,
  3484  		},
  3485  		{
  3486  			desc:      "get persistent flag completion function for child command",
  3487  			cmd:       childCmd,
  3488  			flagName:  "persistentflag",
  3489  			exists:    true,
  3490  			comps:     []string{"persistentvalue"},
  3491  			directive: ShellCompDirectiveDefault,
  3492  		},
  3493  		{
  3494  			desc:     "cannot get flag completion function for local parent flag",
  3495  			cmd:      childCmd,
  3496  			flagName: "rootflag",
  3497  			exists:   false,
  3498  		},
  3499  	}
  3500  
  3501  	for _, tc := range testcases {
  3502  		t.Run(tc.desc, func(t *testing.T) {
  3503  			compFunc, exists := tc.cmd.GetFlagCompletionFunc(tc.flagName)
  3504  			if tc.exists != exists {
  3505  				t.Errorf("Unexpected result looking for flag completion function")
  3506  			}
  3507  
  3508  			if exists {
  3509  				comps, directive := compFunc(tc.cmd, []string{}, "")
  3510  				if strings.Join(tc.comps, " ") != strings.Join(comps, " ") {
  3511  					t.Errorf("Unexpected completions %q", comps)
  3512  				}
  3513  				if tc.directive != directive {
  3514  					t.Errorf("Unexpected directive %q", directive)
  3515  				}
  3516  			}
  3517  		})
  3518  	}
  3519  }
  3520  

View as plain text