@@ -17,7 +17,9 @@ package cobra
1717import (
1818 "bytes"
1919 "context"
20+ "fmt"
2021 "strings"
22+ "sync"
2123 "testing"
2224)
2325
@@ -2040,6 +2042,114 @@ func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) {
20402042 }
20412043}
20422044
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+
20432153func TestFlagCompletionInGoWithDesc (t * testing.T ) {
20442154 rootCmd := & Command {
20452155 Use : "root" ,
0 commit comments