@@ -17,7 +17,9 @@ package cobra
17
17
import (
18
18
"bytes"
19
19
"context"
20
+ "fmt"
20
21
"strings"
22
+ "sync"
21
23
"testing"
22
24
)
23
25
@@ -2040,6 +2042,114 @@ func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) {
2040
2042
}
2041
2043
}
2042
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
+
2043
2153
func TestFlagCompletionInGoWithDesc (t * testing.T ) {
2044
2154
rootCmd := & Command {
2045
2155
Use : "root" ,
0 commit comments