Skip to content

Commit 00b68a1

Browse files
authored
Add tests for flag completion registration (#2053)
Different problems have been reported about flag completion registration. These two tests are the cases that were not being verified but had been mentioned as problematic. Ref: - #1320 - #1438 (comment) Signed-off-by: Marc Khouzam <marc.khouzam@gmail.com>
1 parent b711e87 commit 00b68a1

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

completions_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ package cobra
1717
import (
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+
20432153
func TestFlagCompletionInGoWithDesc(t *testing.T) {
20442154
rootCmd := &Command{
20452155
Use: "root",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy