mirror of
https://github.com/spf13/cobra
synced 2025-01-27 14:06:46 +00:00
0745e55762
The completion code attempts to detect whether a flag can be specified
more than once, and therefore should provide completion even if already
set.
Currently, this code depends on conventions used in the pflag package,
which uses an "Array" or "Slice" suffix or for some types a "stringTo"
prefix.
Cobra allows custom value types to be used, which may not use the same
convention for naming, and therefore currently aren't detected to allow
multiple values.
The pflag module defines a [SliceValue] interface, which is implemented
by the Slice and Array value types it provides (unfortunately, it's not
currently implemented by the "stringTo" values).
This patch adds a reduced interface based on the [SliceValue] interface
mentioned above to allow detecting Value-types that accept multiple values.
Custom types can implement this interface to make completion work for
those values.
I deliberately used a reduced interface to keep the requirements for this
detection as low as possible, without enforcing the other methods defined
in the interface (Append, Replace) which may not apply to all custom types.
Future improvements can likely still be made, considering either implementing
the SliceValue interface for the "stringTo" values or defining a separate
"MapValue" interface for those types.
Possibly providing the reduced interface as part of the pflag module and
to export it.
[SliceValue]: d5e0c0615a/flag.go (L193-L203)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Signed-off-by: Marc Khouzam <marc.khouzam@gmail.com>
Co-authored-by: Marc Khouzam <marc.khouzam@gmail.com>
3744 lines
104 KiB
Go
3744 lines
104 KiB
Go
// Copyright 2013-2023 The Cobra Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package cobra
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
func validArgsFunc(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
if len(args) != 0 {
|
|
return nil, ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
var completions []string
|
|
for _, comp := range []string{"one\tThe first", "two\tThe second"} {
|
|
if strings.HasPrefix(comp, toComplete) {
|
|
completions = append(completions, comp)
|
|
}
|
|
}
|
|
return completions, ShellCompDirectiveDefault
|
|
}
|
|
|
|
func validArgsFunc2(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
if len(args) != 0 {
|
|
return nil, ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
var completions []string
|
|
for _, comp := range []string{"three\tThe third", "four\tThe fourth"} {
|
|
if strings.HasPrefix(comp, toComplete) {
|
|
completions = append(completions, comp)
|
|
}
|
|
}
|
|
return completions, ShellCompDirectiveDefault
|
|
}
|
|
|
|
func TestCmdNameCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd1 := &Command{
|
|
Use: "firstChild",
|
|
Short: "First command",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd2 := &Command{
|
|
Use: "secondChild",
|
|
Run: emptyRun,
|
|
}
|
|
hiddenCmd := &Command{
|
|
Use: "testHidden",
|
|
Hidden: true, // Not completed
|
|
Run: emptyRun,
|
|
}
|
|
deprecatedCmd := &Command{
|
|
Use: "testDeprecated",
|
|
Deprecated: "deprecated", // Not completed
|
|
Run: emptyRun,
|
|
}
|
|
aliasedCmd := &Command{
|
|
Use: "aliased",
|
|
Short: "A command with aliases",
|
|
Aliases: []string{"testAlias", "testSynonym"}, // Not completed
|
|
Run: emptyRun,
|
|
}
|
|
|
|
rootCmd.AddCommand(childCmd1, childCmd2, hiddenCmd, deprecatedCmd, aliasedCmd)
|
|
|
|
// Test that sub-command names are completed
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"aliased",
|
|
"completion",
|
|
"firstChild",
|
|
"help",
|
|
"secondChild",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that sub-command names are completed with prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "s")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"secondChild",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that even with no valid sub-command matches, hidden, deprecated and
|
|
// aliases are not completed
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "test")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that sub-command names are completed with description
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"aliased\tA command with aliases",
|
|
"completion\tGenerate the autocompletion script for the specified shell",
|
|
"firstChild\tFirst command",
|
|
"help\tHelp about any command",
|
|
"secondChild",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestNoCmdNameCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.Flags().String("localroot", "", "local root flag")
|
|
|
|
childCmd1 := &Command{
|
|
Use: "childCmd1",
|
|
Short: "First command",
|
|
Args: MinimumNArgs(0),
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd1)
|
|
childCmd1.PersistentFlags().StringP("persistent", "p", "", "persistent flag")
|
|
persistentFlag := childCmd1.PersistentFlags().Lookup("persistent")
|
|
childCmd1.Flags().StringP("nonPersistent", "n", "", "non-persistent flag")
|
|
nonPersistentFlag := childCmd1.Flags().Lookup("nonPersistent")
|
|
|
|
childCmd2 := &Command{
|
|
Use: "childCmd2",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd1.AddCommand(childCmd2)
|
|
|
|
// Test that sub-command names are not completed if there is an argument already
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "arg1", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that sub-command names are not completed if a local non-persistent flag is present
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--nonPersistent", "value", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
nonPersistentFlag.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that sub-command names are completed if a local non-persistent flag is present and TraverseChildren is set to true
|
|
// set TraverseChildren to true on the root cmd
|
|
rootCmd.TraverseChildren = true
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset TraverseChildren for next command
|
|
rootCmd.TraverseChildren = false
|
|
|
|
expected = strings.Join([]string{
|
|
"childCmd1",
|
|
"completion",
|
|
"help",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that sub-command names from a child cmd are completed if a local non-persistent flag is present
|
|
// and TraverseChildren is set to true on the root cmd
|
|
rootCmd.TraverseChildren = true
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "--nonPersistent", "value", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset TraverseChildren for next command
|
|
rootCmd.TraverseChildren = false
|
|
// Reset the flag for the next command
|
|
nonPersistentFlag.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
"childCmd2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that we don't use Traverse when we shouldn't.
|
|
// This command should not return a completion since the command line is invalid without TraverseChildren.
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that sub-command names are not completed if a local non-persistent short flag is present
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-n", "value", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
nonPersistentFlag.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that sub-command names are completed with a persistent flag
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--persistent", "value", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
persistentFlag.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
"childCmd2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that sub-command names are completed with a persistent short flag
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-p", "value", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
persistentFlag.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
"childCmd2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
ValidArgs: []string{"one", "two", "three"},
|
|
Args: MinimumNArgs(1),
|
|
}
|
|
|
|
// Test that validArgs are completed
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"one",
|
|
"two",
|
|
"three",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that validArgs are completed with prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "o")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"one",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that validArgs don't repeat
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "one", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsAndCmdCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
ValidArgs: []string{"one", "two"},
|
|
Run: emptyRun,
|
|
}
|
|
|
|
childCmd := &Command{
|
|
Use: "thechild",
|
|
Run: emptyRun,
|
|
}
|
|
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
// Test that both sub-commands and validArgs are completed
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"completion",
|
|
"help",
|
|
"thechild",
|
|
"one",
|
|
"two",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that both sub-commands and validArgs are completed with prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"thechild",
|
|
"two",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
|
|
childCmd := &Command{
|
|
Use: "thechild",
|
|
Short: "The child command",
|
|
Run: emptyRun,
|
|
}
|
|
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
// Test that both sub-commands and validArgsFunction are completed
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"completion",
|
|
"help",
|
|
"thechild",
|
|
"one",
|
|
"two",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that both sub-commands and validArgs are completed with prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"thechild",
|
|
"two",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that both sub-commands and validArgs are completed with description
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"thechild\tThe child command",
|
|
"two\tThe second",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestFlagNameCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd := &Command{
|
|
Use: "childCmd",
|
|
Version: "1.2.3",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.Flags().IntP("first", "f", -1, "first flag")
|
|
rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag")
|
|
childCmd.Flags().String("subFlag", "", "sub flag")
|
|
|
|
// Test that flag names are not shown if the user has not given the '-' prefix
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"childCmd",
|
|
"completion",
|
|
"help",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that flag names are completed
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--first",
|
|
"-f",
|
|
"--help",
|
|
"-h",
|
|
"--second",
|
|
"-s",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that flag names are completed when a prefix is given
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--f")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--first",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that flag names are completed in a sub-cmd
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--second",
|
|
"-s",
|
|
"--help",
|
|
"-h",
|
|
"--subFlag",
|
|
"--version",
|
|
"-v",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestFlagNameCompletionInGoWithDesc(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd := &Command{
|
|
Use: "childCmd",
|
|
Short: "first command",
|
|
Version: "1.2.3",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.Flags().IntP("first", "f", -1, "first flag\nlonger description for flag")
|
|
rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag")
|
|
childCmd.Flags().String("subFlag", "", "sub flag")
|
|
|
|
// Test that flag names are not shown if the user has not given the '-' prefix
|
|
output, err := executeCommand(rootCmd, ShellCompRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"childCmd\tfirst command",
|
|
"completion\tGenerate the autocompletion script for the specified shell",
|
|
"help\tHelp about any command",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that flag names are completed
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--first\tfirst flag",
|
|
"-f\tfirst flag",
|
|
"--help\thelp for root",
|
|
"-h\thelp for root",
|
|
"--second\tsecond flag",
|
|
"-s\tsecond flag",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that flag names are completed when a prefix is given
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--f")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--first\tfirst flag",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that flag names are completed in a sub-cmd
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "childCmd", "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--second\tsecond flag",
|
|
"-s\tsecond flag",
|
|
"--help\thelp for childCmd",
|
|
"-h\thelp for childCmd",
|
|
"--subFlag\tsub flag",
|
|
"--version\tversion for childCmd",
|
|
"-v\tversion for childCmd",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
// customMultiString is a custom Value type that accepts multiple values,
|
|
// but does not include "Slice" or "Array" in its "Type" string.
|
|
type customMultiString []string
|
|
|
|
var _ SliceValue = (*customMultiString)(nil)
|
|
|
|
func (s *customMultiString) String() string {
|
|
return fmt.Sprintf("%v", *s)
|
|
}
|
|
|
|
func (s *customMultiString) Set(v string) error {
|
|
*s = append(*s, v)
|
|
return nil
|
|
}
|
|
|
|
func (s *customMultiString) Type() string {
|
|
return "multi string"
|
|
}
|
|
|
|
func (s *customMultiString) GetSlice() []string {
|
|
return *s
|
|
}
|
|
|
|
func TestFlagNameCompletionRepeat(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd := &Command{
|
|
Use: "childCmd",
|
|
Short: "first command",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.Flags().IntP("first", "f", -1, "first flag")
|
|
firstFlag := rootCmd.Flags().Lookup("first")
|
|
rootCmd.Flags().BoolP("second", "s", false, "second flag")
|
|
secondFlag := rootCmd.Flags().Lookup("second")
|
|
rootCmd.Flags().StringArrayP("array", "a", nil, "array flag")
|
|
arrayFlag := rootCmd.Flags().Lookup("array")
|
|
rootCmd.Flags().IntSliceP("slice", "l", nil, "slice flag")
|
|
sliceFlag := rootCmd.Flags().Lookup("slice")
|
|
rootCmd.Flags().BoolSliceP("bslice", "b", nil, "bool slice flag")
|
|
bsliceFlag := rootCmd.Flags().Lookup("bslice")
|
|
rootCmd.Flags().VarP(&customMultiString{}, "multi", "m", "multi string flag")
|
|
multiFlag := rootCmd.Flags().Lookup("multi")
|
|
|
|
// Test that flag names are not repeated unless they are an array or slice
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
firstFlag.Changed = false
|
|
|
|
expected := strings.Join([]string{
|
|
"--array",
|
|
"--bslice",
|
|
"--help",
|
|
"--multi",
|
|
"--second",
|
|
"--slice",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that flag names are not repeated unless they are an array or slice
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--second=false", "--")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
firstFlag.Changed = false
|
|
secondFlag.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
"--array",
|
|
"--bslice",
|
|
"--help",
|
|
"--multi",
|
|
"--slice",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that flag names are not repeated unless they are an array or slice
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--slice", "1", "--slice=2", "--array", "val", "--bslice", "true", "--multi", "val", "--")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
sliceFlag.Changed = false
|
|
arrayFlag.Changed = false
|
|
bsliceFlag.Changed = false
|
|
multiFlag.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
"--array",
|
|
"--bslice",
|
|
"--first",
|
|
"--help",
|
|
"--multi",
|
|
"--second",
|
|
"--slice",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that flag names are not repeated unless they are an array or slice, using shortname
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
sliceFlag.Changed = false
|
|
arrayFlag.Changed = false
|
|
multiFlag.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
"--array",
|
|
"-a",
|
|
"--bslice",
|
|
"-b",
|
|
"--first",
|
|
"-f",
|
|
"--help",
|
|
"-h",
|
|
"--multi",
|
|
"-m",
|
|
"--second",
|
|
"-s",
|
|
"--slice",
|
|
"-l",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that flag names are not repeated unless they are an array or slice, using shortname with prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-a")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
sliceFlag.Changed = false
|
|
arrayFlag.Changed = false
|
|
multiFlag.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
"-a",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestRequiredFlagNameCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
ValidArgs: []string{"realArg"},
|
|
Run: emptyRun,
|
|
}
|
|
childCmd := &Command{
|
|
Use: "childCmd",
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"subArg"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.Flags().IntP("requiredFlag", "r", -1, "required flag")
|
|
assertNoErr(t, rootCmd.MarkFlagRequired("requiredFlag"))
|
|
requiredFlag := rootCmd.Flags().Lookup("requiredFlag")
|
|
|
|
rootCmd.PersistentFlags().IntP("requiredPersistent", "p", -1, "required persistent")
|
|
assertNoErr(t, rootCmd.MarkPersistentFlagRequired("requiredPersistent"))
|
|
requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent")
|
|
|
|
rootCmd.Flags().StringP("release", "R", "", "Release name")
|
|
|
|
childCmd.Flags().BoolP("subRequired", "s", false, "sub required flag")
|
|
assertNoErr(t, childCmd.MarkFlagRequired("subRequired"))
|
|
childCmd.Flags().BoolP("subNotRequired", "n", false, "sub not required flag")
|
|
|
|
// Test that a required flag is suggested even without the - prefix
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"childCmd",
|
|
"completion",
|
|
"help",
|
|
"--requiredFlag",
|
|
"-r",
|
|
"--requiredPersistent",
|
|
"-p",
|
|
"realArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that a required flag is suggested without other flags when using the '-' prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--requiredFlag",
|
|
"-r",
|
|
"--requiredPersistent",
|
|
"-p",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that if no required flag matches, the normal flags are suggested
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--relea")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--release",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test required flags for sub-commands
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--requiredPersistent",
|
|
"-p",
|
|
"--subRequired",
|
|
"-s",
|
|
"subArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--requiredPersistent",
|
|
"-p",
|
|
"--subRequired",
|
|
"-s",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "--subNot")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--subNotRequired",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that when a required flag is present, it is not suggested anymore
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
requiredFlag.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
"--requiredPersistent",
|
|
"-p",
|
|
"realArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that when a persistent required flag is present, it is not suggested anymore
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredPersistent", "1", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flag for the next command
|
|
requiredPersistent.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
"childCmd",
|
|
"completion",
|
|
"help",
|
|
"--requiredFlag",
|
|
"-r",
|
|
"realArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that when all required flags are present, normal completion is done
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "--requiredPersistent", "1", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
// Reset the flags for the next command
|
|
requiredFlag.Changed = false
|
|
requiredPersistent.Changed = false
|
|
|
|
expected = strings.Join([]string{
|
|
"realArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestFlagFileExtFilterCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
|
|
// No extensions. Should be ignored.
|
|
rootCmd.Flags().StringP("file", "f", "", "file flag")
|
|
assertNoErr(t, rootCmd.MarkFlagFilename("file"))
|
|
|
|
// Single extension
|
|
rootCmd.Flags().StringP("log", "l", "", "log flag")
|
|
assertNoErr(t, rootCmd.MarkFlagFilename("log", "log"))
|
|
|
|
// Multiple extensions
|
|
rootCmd.Flags().StringP("yaml", "y", "", "yaml flag")
|
|
assertNoErr(t, rootCmd.MarkFlagFilename("yaml", "yaml", "yml"))
|
|
|
|
// Directly using annotation
|
|
rootCmd.Flags().StringP("text", "t", "", "text flag")
|
|
assertNoErr(t, rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"}))
|
|
|
|
// Test that the completion logic returns the proper info for the completion
|
|
// script to handle the file filtering
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--file", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--log", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"log",
|
|
":8",
|
|
"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"yaml", "yml",
|
|
":8",
|
|
"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml=")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"yaml", "yml",
|
|
":8",
|
|
"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"yaml", "yml",
|
|
":8",
|
|
"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y=")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"yaml", "yml",
|
|
":8",
|
|
"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--text", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"txt",
|
|
":8",
|
|
"Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestFlagDirFilterCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
|
|
// Filter directories
|
|
rootCmd.Flags().StringP("dir", "d", "", "dir flag")
|
|
assertNoErr(t, rootCmd.MarkFlagDirname("dir"))
|
|
|
|
// Filter directories within a directory
|
|
rootCmd.Flags().StringP("subdir", "s", "", "subdir")
|
|
assertNoErr(t, rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"}))
|
|
|
|
// Multiple directory specification get ignored
|
|
rootCmd.Flags().StringP("manydir", "m", "", "manydir")
|
|
assertNoErr(t, rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"}))
|
|
|
|
// Test that the completion logic returns the proper info for the completion
|
|
// script to handle the directory filtering
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--dir", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
":16",
|
|
"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-d", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":16",
|
|
"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"themes",
|
|
":16",
|
|
"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir=")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"themes",
|
|
":16",
|
|
"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"themes",
|
|
":16",
|
|
"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s=")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"themes",
|
|
":16",
|
|
"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--manydir", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":16",
|
|
"Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsFuncCmdContext(t *testing.T) {
|
|
validArgsFunc := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
ctx := cmd.Context()
|
|
|
|
if ctx == nil {
|
|
t.Error("Received nil context in completion func")
|
|
} else if ctx.Value("testKey") != "123" {
|
|
t.Error("Received invalid context")
|
|
}
|
|
|
|
return nil, ShellCompDirectiveDefault
|
|
}
|
|
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd := &Command{
|
|
Use: "childCmd",
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
//nolint:golint,staticcheck // We can safely use a basic type as key in tests.
|
|
ctx := context.WithValue(context.Background(), "testKey", "123")
|
|
|
|
// Test completing an empty string on the childCmd
|
|
_, output, err := executeCommandWithContextC(ctx, rootCmd, ShellCompNoDescRequestCmd, "childCmd", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsFuncSingleCmd(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
|
|
// Test completing an empty string
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"one",
|
|
"two",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with a prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"two",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsFuncSingleCmdInvalidArg(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
// If we don't specify a value for Args, this test fails.
|
|
// This is only true for a root command without any subcommands, and is caused
|
|
// by the fact that the __complete command becomes a subcommand when there should not be one.
|
|
// The problem is in the implementation of legacyArgs().
|
|
Args: MinimumNArgs(1),
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
|
|
// Check completing with wrong number of args
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "unexpectedArg", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsFuncChildCmds(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
child1Cmd := &Command{
|
|
Use: "child1",
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
child2Cmd := &Command{
|
|
Use: "child2",
|
|
ValidArgsFunction: validArgsFunc2,
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(child1Cmd, child2Cmd)
|
|
|
|
// Test completion of first sub-command with empty argument
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"one",
|
|
"two",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test completion of first sub-command with a prefix to complete
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"two",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with wrong number of args
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "unexpectedArg", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test completion of second sub-command with empty argument
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"three",
|
|
"four",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"three",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with wrong number of args
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "unexpectedArg", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsFuncAliases(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
child := &Command{
|
|
Use: "child",
|
|
Aliases: []string{"son", "daughter"},
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(child)
|
|
|
|
// Test completion of first sub-command with empty argument
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"one",
|
|
"two",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test completion of first sub-command with a prefix to complete
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "daughter", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"two",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with wrong number of args
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "unexpectedArg", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsFuncInBashScript(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
child := &Command{
|
|
Use: "child",
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(child)
|
|
|
|
buf := new(bytes.Buffer)
|
|
assertNoErr(t, rootCmd.GenBashCompletion(buf))
|
|
output := buf.String()
|
|
|
|
check(t, output, "has_completion_function=1")
|
|
}
|
|
|
|
func TestNoValidArgsFuncInBashScript(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
child := &Command{
|
|
Use: "child",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(child)
|
|
|
|
buf := new(bytes.Buffer)
|
|
assertNoErr(t, rootCmd.GenBashCompletion(buf))
|
|
output := buf.String()
|
|
|
|
checkOmit(t, output, "has_completion_function=1")
|
|
}
|
|
|
|
func TestCompleteCmdInBashScript(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
child := &Command{
|
|
Use: "child",
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(child)
|
|
|
|
buf := new(bytes.Buffer)
|
|
assertNoErr(t, rootCmd.GenBashCompletion(buf))
|
|
output := buf.String()
|
|
|
|
check(t, output, ShellCompNoDescRequestCmd)
|
|
}
|
|
|
|
func TestCompleteNoDesCmdInZshScript(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
child := &Command{
|
|
Use: "child",
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(child)
|
|
|
|
buf := new(bytes.Buffer)
|
|
assertNoErr(t, rootCmd.GenZshCompletionNoDesc(buf))
|
|
output := buf.String()
|
|
|
|
check(t, output, ShellCompNoDescRequestCmd)
|
|
}
|
|
|
|
func TestCompleteCmdInZshScript(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
child := &Command{
|
|
Use: "child",
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(child)
|
|
|
|
buf := new(bytes.Buffer)
|
|
assertNoErr(t, rootCmd.GenZshCompletion(buf))
|
|
output := buf.String()
|
|
|
|
check(t, output, ShellCompRequestCmd)
|
|
checkOmit(t, output, ShellCompNoDescRequestCmd)
|
|
}
|
|
|
|
func TestFlagCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
|
|
assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
completions := []string{}
|
|
for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
|
|
if strings.HasPrefix(comp, toComplete) {
|
|
completions = append(completions, comp)
|
|
}
|
|
}
|
|
return completions, ShellCompDirectiveDefault
|
|
}))
|
|
rootCmd.Flags().String("filename", "", "Enter a filename")
|
|
assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
completions := []string{}
|
|
for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
|
|
if strings.HasPrefix(comp, toComplete) {
|
|
completions = append(completions, comp)
|
|
}
|
|
}
|
|
return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
|
|
}))
|
|
|
|
// Test completing an empty string
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"1",
|
|
"2",
|
|
"10",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with a prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "1")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"1",
|
|
"10",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test completing an empty string
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"file.yaml",
|
|
"myfile.json",
|
|
"file.xml",
|
|
":6",
|
|
"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with a prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "f")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"file.yaml",
|
|
"file.xml",
|
|
":6",
|
|
"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
child1Cmd := &Command{
|
|
Use: "child1",
|
|
ValidArgsFunction: validArgsFunc,
|
|
Run: emptyRun,
|
|
}
|
|
child2Cmd := &Command{
|
|
Use: "child2",
|
|
ValidArgsFunction: validArgsFunc2,
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(child1Cmd, child2Cmd)
|
|
|
|
// Test completion of first sub-command with empty argument
|
|
output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child1", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"one\tThe first",
|
|
"two\tThe second",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test completion of first sub-command with a prefix to complete
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"two\tThe second",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with wrong number of args
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "unexpectedArg", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test completion of second sub-command with empty argument
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"three\tThe third",
|
|
"four\tThe fourth",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"three\tThe third",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with wrong number of args
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "unexpectedArg", "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
Run: emptyRun,
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"--validarg", "test"}, ShellCompDirectiveDefault
|
|
},
|
|
}
|
|
childCmd2 := &Command{
|
|
Use: "child2",
|
|
Run: emptyRun,
|
|
ValidArgs: []string{"arg1", "arg2"},
|
|
}
|
|
rootCmd.AddCommand(childCmd, childCmd2)
|
|
childCmd.Flags().Bool("bool", false, "test bool flag")
|
|
childCmd.Flags().String("string", "", "test string flag")
|
|
_ = childCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"myval"}, ShellCompDirectiveDefault
|
|
})
|
|
|
|
// Test flag completion with no argument
|
|
output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"--bool\ttest bool flag",
|
|
"--help\thelp for child",
|
|
"--string\ttest string flag",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that no flags are completed after the -- arg
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--validarg",
|
|
"test",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that no flags are completed after the -- arg with a flag set
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--bool", "--", "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--validarg",
|
|
"test",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// set Interspersed to false which means that no flags should be completed after the first arg
|
|
childCmd.Flags().SetInterspersed(false)
|
|
|
|
// Test that no flags are completed after the first arg
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "arg", "--")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--validarg",
|
|
"test",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that no flags are completed after the fist arg with a flag set
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "t", "arg", "--")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--validarg",
|
|
"test",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check that args are still completed after --
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--validarg",
|
|
"test",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check that args are still completed even if flagname with ValidArgsFunction exists
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--string", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--validarg",
|
|
"test",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check that args are still completed even if flagname with ValidArgsFunction exists
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "--", "a")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"arg1",
|
|
"arg2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check that --validarg is not parsed as flag after --
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--validarg",
|
|
"test",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check that --validarg is not parsed as flag after an arg
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "arg", "--validarg", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--validarg",
|
|
"test",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check that --validarg is added to args for the ValidArgsFunction
|
|
childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return args, ShellCompDirectiveDefault
|
|
}
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--validarg",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check that --validarg is added to args for the ValidArgsFunction and toComplete is also set correctly
|
|
childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return append(args, toComplete), ShellCompDirectiveDefault
|
|
}
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "--toComp=ab")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"--validarg",
|
|
"--toComp=ab",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
Run: emptyRun,
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"--validarg", "test"}, ShellCompDirectiveDefault
|
|
},
|
|
}
|
|
childCmd.Flags().Bool("bool", false, "test bool flag")
|
|
childCmd.Flags().String("string", "", "test string flag")
|
|
_ = childCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"myval"}, ShellCompDirectiveDefault
|
|
})
|
|
|
|
// Important: This is a test for https://github.com/spf13/cobra/issues/1437
|
|
// Only add the subcommand after RegisterFlagCompletionFunc was called, do not change this order!
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
// Test that flag completion works for the subcmd
|
|
output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"myval",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestFlagCompletionForPersistentFlagsCalledFromSubCmd(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
rootCmd.PersistentFlags().String("string", "", "test string flag")
|
|
_ = rootCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"myval"}, ShellCompDirectiveDefault
|
|
})
|
|
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
Run: emptyRun,
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"--validarg", "test"}, ShellCompDirectiveDefault
|
|
},
|
|
}
|
|
childCmd.Flags().Bool("bool", false, "test bool flag")
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
// Test that persistent flag completion works for the subcmd
|
|
output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"myval",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
// This test tries to register flag completion concurrently to make sure the
|
|
// code handles concurrency properly.
|
|
// This was reported as a problem when tests are run concurrently:
|
|
// https://github.com/spf13/cobra/issues/1320
|
|
//
|
|
// NOTE: this test can sometimes pass even if the code were to not handle
|
|
// concurrency properly. This is not great but the important part is that
|
|
// it should never fail. Therefore, if the tests fails sometimes, we will
|
|
// still be able to know there is a problem.
|
|
func TestFlagCompletionConcurrentRegistration(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
const maxFlags = 50
|
|
for i := 1; i < maxFlags; i += 2 {
|
|
flagName := fmt.Sprintf("flag%d", i)
|
|
rootCmd.Flags().String(flagName, "", fmt.Sprintf("test %s flag on root", flagName))
|
|
}
|
|
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
Run: emptyRun,
|
|
}
|
|
for i := 2; i <= maxFlags; i += 2 {
|
|
flagName := fmt.Sprintf("flag%d", i)
|
|
childCmd.Flags().String(flagName, "", fmt.Sprintf("test %s flag on child", flagName))
|
|
}
|
|
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
// Register completion in different threads to test concurrency.
|
|
var wg sync.WaitGroup
|
|
for i := 1; i <= maxFlags; i++ {
|
|
index := i
|
|
flagName := fmt.Sprintf("flag%d", i)
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
cmd := rootCmd
|
|
if index%2 == 0 {
|
|
cmd = childCmd
|
|
}
|
|
_ = cmd.RegisterFlagCompletionFunc(flagName, func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{fmt.Sprintf("flag%d", index)}, ShellCompDirectiveDefault
|
|
})
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
// Test that flag completion works for each flag
|
|
for i := 1; i <= 6; i++ {
|
|
var output string
|
|
var err error
|
|
flagName := fmt.Sprintf("flag%d", i)
|
|
|
|
if i%2 == 1 {
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--"+flagName, "")
|
|
} else {
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--"+flagName, "")
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
flagName,
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFlagCompletionInGoWithDesc(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
|
|
assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
completions := []string{}
|
|
for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
|
|
if strings.HasPrefix(comp, toComplete) {
|
|
completions = append(completions, comp)
|
|
}
|
|
}
|
|
return completions, ShellCompDirectiveDefault
|
|
}))
|
|
rootCmd.Flags().String("filename", "", "Enter a filename")
|
|
assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
completions := []string{}
|
|
for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
|
|
if strings.HasPrefix(comp, toComplete) {
|
|
completions = append(completions, comp)
|
|
}
|
|
}
|
|
return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
|
|
}))
|
|
|
|
// Test completing an empty string
|
|
output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"1\tThe first",
|
|
"2\tThe second",
|
|
"10\tThe tenth",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with a prefix
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "1")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"1\tThe first",
|
|
"10\tThe tenth",
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test completing an empty string
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"file.yaml\tYAML format",
|
|
"myfile.json\tJSON format",
|
|
"file.xml\tXML format",
|
|
":6",
|
|
"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with a prefix
|
|
output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "f")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"file.yaml\tYAML format",
|
|
"file.xml\tXML format",
|
|
":6",
|
|
"Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestValidArgsNotValidArgsFunc(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
ValidArgs: []string{"one", "two"},
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"three", "four"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
Run: emptyRun,
|
|
}
|
|
|
|
// Test that if both ValidArgs and ValidArgsFunction are present
|
|
// only ValidArgs is considered
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"one",
|
|
"two",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Check completing with a prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"two",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestArgAliasesCompletionInGo(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Args: OnlyValidArgs,
|
|
ValidArgs: []string{"one", "two", "three"},
|
|
ArgAliases: []string{"un", "deux", "trois"},
|
|
Run: emptyRun,
|
|
}
|
|
|
|
// Test that argaliases are not completed when there are validargs that match
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"one",
|
|
"two",
|
|
"three",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that argaliases are not completed when there are validargs that match using a prefix
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"two",
|
|
"three",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that argaliases are completed when there are no validargs that match
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "tr")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"trois",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestCompleteHelp(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
child1Cmd := &Command{
|
|
Use: "child1",
|
|
Run: emptyRun,
|
|
}
|
|
child2Cmd := &Command{
|
|
Use: "child2",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(child1Cmd, child2Cmd)
|
|
|
|
child3Cmd := &Command{
|
|
Use: "child3",
|
|
Run: emptyRun,
|
|
}
|
|
child1Cmd.AddCommand(child3Cmd)
|
|
|
|
// Test that completion includes the help command
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"child1",
|
|
"child2",
|
|
"completion",
|
|
"help",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test sub-commands are completed on first level of help command
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"child1",
|
|
"child2",
|
|
"completion",
|
|
"help", // "<program> help help" is a valid command, so should be completed
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test sub-commands are completed on first level of help command
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "child1", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"child3",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func removeCompCmd(rootCmd *Command) {
|
|
// Remove completion command for the next test
|
|
for _, cmd := range rootCmd.commands {
|
|
if cmd.Name() == compCmdName {
|
|
rootCmd.RemoveCommand(cmd)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDefaultCompletionCmd(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Args: NoArgs,
|
|
Run: emptyRun,
|
|
}
|
|
|
|
// Test that no completion command is created if there are not other sub-commands
|
|
assertNoErr(t, rootCmd.Execute())
|
|
for _, cmd := range rootCmd.commands {
|
|
if cmd.Name() == compCmdName {
|
|
t.Errorf("Should not have a 'completion' command when there are no other sub-commands of root")
|
|
break
|
|
}
|
|
}
|
|
|
|
subCmd := &Command{
|
|
Use: "sub",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(subCmd)
|
|
|
|
// Test that a completion command is created if there are other sub-commands
|
|
found := false
|
|
assertNoErr(t, rootCmd.Execute())
|
|
for _, cmd := range rootCmd.commands {
|
|
if cmd.Name() == compCmdName {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("Should have a 'completion' command when there are other sub-commands of root")
|
|
}
|
|
// Remove completion command for the next test
|
|
removeCompCmd(rootCmd)
|
|
|
|
// Test that the default completion command can be disabled
|
|
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
|
assertNoErr(t, rootCmd.Execute())
|
|
for _, cmd := range rootCmd.commands {
|
|
if cmd.Name() == compCmdName {
|
|
t.Errorf("Should not have a 'completion' command when the feature is disabled")
|
|
break
|
|
}
|
|
}
|
|
// Re-enable for next test
|
|
rootCmd.CompletionOptions.DisableDefaultCmd = false
|
|
|
|
// Test that completion descriptions are enabled by default
|
|
output, err := executeCommand(rootCmd, compCmdName, "zsh")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
check(t, output, ShellCompRequestCmd)
|
|
checkOmit(t, output, ShellCompNoDescRequestCmd)
|
|
// Remove completion command for the next test
|
|
removeCompCmd(rootCmd)
|
|
|
|
// Test that completion descriptions can be disabled completely
|
|
rootCmd.CompletionOptions.DisableDescriptions = true
|
|
output, err = executeCommand(rootCmd, compCmdName, "zsh")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
check(t, output, ShellCompNoDescRequestCmd)
|
|
// Re-enable for next test
|
|
rootCmd.CompletionOptions.DisableDescriptions = false
|
|
// Remove completion command for the next test
|
|
removeCompCmd(rootCmd)
|
|
|
|
var compCmd *Command
|
|
// Test that the --no-descriptions flag is present on all shells
|
|
assertNoErr(t, rootCmd.Execute())
|
|
for _, shell := range []string{"bash", "fish", "powershell", "zsh"} {
|
|
if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag == nil {
|
|
t.Errorf("Missing --%s flag for %s shell", compCmdNoDescFlagName, shell)
|
|
}
|
|
}
|
|
// Remove completion command for the next test
|
|
removeCompCmd(rootCmd)
|
|
|
|
// Test that the '--no-descriptions' flag can be disabled
|
|
rootCmd.CompletionOptions.DisableNoDescFlag = true
|
|
assertNoErr(t, rootCmd.Execute())
|
|
for _, shell := range []string{"fish", "zsh", "bash", "powershell"} {
|
|
if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil {
|
|
t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell)
|
|
}
|
|
}
|
|
// Re-enable for next test
|
|
rootCmd.CompletionOptions.DisableNoDescFlag = false
|
|
// Remove completion command for the next test
|
|
removeCompCmd(rootCmd)
|
|
|
|
// Test that the '--no-descriptions' flag is disabled when descriptions are disabled
|
|
rootCmd.CompletionOptions.DisableDescriptions = true
|
|
assertNoErr(t, rootCmd.Execute())
|
|
for _, shell := range []string{"fish", "zsh", "bash", "powershell"} {
|
|
if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil {
|
|
t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell)
|
|
}
|
|
}
|
|
// Re-enable for next test
|
|
rootCmd.CompletionOptions.DisableDescriptions = false
|
|
// Remove completion command for the next test
|
|
removeCompCmd(rootCmd)
|
|
|
|
// Test that the 'completion' command can be hidden
|
|
rootCmd.CompletionOptions.HiddenDefaultCmd = true
|
|
assertNoErr(t, rootCmd.Execute())
|
|
compCmd, _, err = rootCmd.Find([]string{compCmdName})
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
if compCmd.Hidden == false {
|
|
t.Error("Default 'completion' command should be hidden but it is not")
|
|
}
|
|
// Re-enable for next test
|
|
rootCmd.CompletionOptions.HiddenDefaultCmd = false
|
|
// Remove completion command for the next test
|
|
removeCompCmd(rootCmd)
|
|
}
|
|
|
|
func TestCompleteCompletion(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
subCmd := &Command{
|
|
Use: "sub",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(subCmd)
|
|
|
|
// Test sub-commands of the completion command
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "completion", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"bash",
|
|
"fish",
|
|
"powershell",
|
|
"zsh",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test there are no completions for the sub-commands of the completion command
|
|
var compCmd *Command
|
|
for _, cmd := range rootCmd.Commands() {
|
|
if cmd.Name() == compCmdName {
|
|
compCmd = cmd
|
|
break
|
|
}
|
|
}
|
|
|
|
for _, shell := range compCmd.Commands() {
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, compCmdName, shell.Name(), "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMultipleShorthandFlagCompletion(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
ValidArgs: []string{"foo", "bar"},
|
|
Run: emptyRun,
|
|
}
|
|
f := rootCmd.Flags()
|
|
f.BoolP("short", "s", false, "short flag 1")
|
|
f.BoolP("short2", "d", false, "short flag 2")
|
|
f.StringP("short3", "f", "", "short flag 3")
|
|
_ = rootCmd.RegisterFlagCompletionFunc("short3", func(*Command, []string, string) ([]string, ShellCompDirective) {
|
|
return []string{"works"}, ShellCompDirectiveNoFileComp
|
|
})
|
|
|
|
// Test that a single shorthand flag works
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"foo",
|
|
"bar",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that multiple boolean shorthand flags work
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sd", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"foo",
|
|
"bar",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that multiple boolean + string shorthand flags work
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"works",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that multiple boolean + string with equal sign shorthand flags work
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"works",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that multiple boolean + string with equal sign with value shorthand flags work
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=abc", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"foo",
|
|
"bar",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestCompleteWithDisableFlagParsing(t *testing.T) {
|
|
|
|
flagValidArgs := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"--flag", "-f"}, ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
Run: emptyRun,
|
|
DisableFlagParsing: true,
|
|
ValidArgsFunction: flagValidArgs,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.PersistentFlags().StringP("persistent", "p", "", "persistent flag")
|
|
childCmd.Flags().StringP("nonPersistent", "n", "", "non-persistent flag")
|
|
|
|
// Test that when DisableFlagParsing==true, ValidArgsFunction is called to complete flag names,
|
|
// after Cobra tried to complete the flags it knows about.
|
|
childCmd.DisableFlagParsing = true
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"--persistent",
|
|
"-p",
|
|
"--nonPersistent",
|
|
"-n",
|
|
"--flag",
|
|
"-f",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Test that when DisableFlagParsing==false, Cobra completes the flags itself and ValidArgsFunction is not called
|
|
childCmd.DisableFlagParsing = false
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "-")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// Cobra was not told of any flags, so it returns nothing
|
|
expected = strings.Join([]string{
|
|
"--persistent",
|
|
"-p",
|
|
"--help",
|
|
"-h",
|
|
"--nonPersistent",
|
|
"-n",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestCompleteWithRootAndLegacyArgs(t *testing.T) {
|
|
// Test a lonely root command which uses legacyArgs(). In such a case, the root
|
|
// command should accept any number of arguments and completion should behave accordingly.
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Args: nil, // Args must be nil to trigger the legacyArgs() function
|
|
Run: emptyRun,
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"arg1", "arg2"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
}
|
|
|
|
// Make sure the first arg is completed
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"arg1",
|
|
"arg2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
|
|
// Make sure the completion of arguments continues
|
|
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "arg1", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected = strings.Join([]string{
|
|
"arg1",
|
|
"arg2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestFixedCompletions(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
|
|
choices := []string{"apple", "banana", "orange"}
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
ValidArgsFunction: FixedCompletions(choices, ShellCompDirectiveNoFileComp),
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "a")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
"apple",
|
|
"banana",
|
|
"orange",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
|
|
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
func TestCompletionForGroupedFlags(t *testing.T) {
|
|
getCmd := func() *Command {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"subArg"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.PersistentFlags().Int("ingroup1", -1, "ingroup1")
|
|
rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2")
|
|
|
|
childCmd.Flags().Bool("ingroup3", false, "ingroup3")
|
|
childCmd.Flags().Bool("nogroup", false, "nogroup")
|
|
|
|
// Add flags to a group
|
|
childCmd.MarkFlagsRequiredTogether("ingroup1", "ingroup2", "ingroup3")
|
|
|
|
return rootCmd
|
|
}
|
|
|
|
// Each test case uses a unique command from the function above.
|
|
testcases := []struct {
|
|
desc string
|
|
args []string
|
|
expectedOutput string
|
|
}{
|
|
{
|
|
desc: "flags in group not suggested without - prefix",
|
|
args: []string{"child", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"subArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "flags in group suggested with - prefix",
|
|
args: []string{"child", "-"},
|
|
expectedOutput: strings.Join([]string{
|
|
"--ingroup1",
|
|
"--ingroup2",
|
|
"--help",
|
|
"-h",
|
|
"--ingroup3",
|
|
"--nogroup",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "when flag in group present, other flags in group suggested even without - prefix",
|
|
args: []string{"child", "--ingroup2", "value", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"--ingroup1",
|
|
"--ingroup3",
|
|
"subArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "when all flags in group present, flags not suggested without - prefix",
|
|
args: []string{"child", "--ingroup1", "8", "--ingroup2", "value2", "--ingroup3", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"subArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "group ignored if some flags not applicable",
|
|
args: []string{"--ingroup2", "value", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"child",
|
|
"completion",
|
|
"help",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
c := getCmd()
|
|
args := []string{ShellCompNoDescRequestCmd}
|
|
args = append(args, tc.args...)
|
|
output, err := executeCommand(c, args...)
|
|
switch {
|
|
case err == nil && output != tc.expectedOutput:
|
|
t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
|
|
case err != nil:
|
|
t.Errorf("Unexpected error %q", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCompletionForOneRequiredGroupFlags(t *testing.T) {
|
|
getCmd := func() *Command {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"subArg"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.PersistentFlags().Int("ingroup1", -1, "ingroup1")
|
|
rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2")
|
|
|
|
childCmd.Flags().Bool("ingroup3", false, "ingroup3")
|
|
childCmd.Flags().Bool("nogroup", false, "nogroup")
|
|
|
|
// Add flags to a group
|
|
childCmd.MarkFlagsOneRequired("ingroup1", "ingroup2", "ingroup3")
|
|
|
|
return rootCmd
|
|
}
|
|
|
|
// Each test case uses a unique command from the function above.
|
|
testcases := []struct {
|
|
desc string
|
|
args []string
|
|
expectedOutput string
|
|
}{
|
|
{
|
|
desc: "flags in group suggested without - prefix",
|
|
args: []string{"child", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"--ingroup1",
|
|
"--ingroup2",
|
|
"--ingroup3",
|
|
"subArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "flags in group suggested with - prefix",
|
|
args: []string{"child", "-"},
|
|
expectedOutput: strings.Join([]string{
|
|
"--ingroup1",
|
|
"--ingroup2",
|
|
"--ingroup3",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "when any flag in group present, other flags in group not suggested without - prefix",
|
|
args: []string{"child", "--ingroup2", "value", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"subArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "when all flags in group present, flags not suggested without - prefix",
|
|
args: []string{"child", "--ingroup1", "8", "--ingroup2", "value2", "--ingroup3", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"subArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "group ignored if some flags not applicable",
|
|
args: []string{"--ingroup2", "value", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"child",
|
|
"completion",
|
|
"help",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
c := getCmd()
|
|
args := []string{ShellCompNoDescRequestCmd}
|
|
args = append(args, tc.args...)
|
|
output, err := executeCommand(c, args...)
|
|
switch {
|
|
case err == nil && output != tc.expectedOutput:
|
|
t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
|
|
case err != nil:
|
|
t.Errorf("Unexpected error %q", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCompletionForMutuallyExclusiveFlags(t *testing.T) {
|
|
getCmd := func() *Command {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"subArg"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
rootCmd.PersistentFlags().IntSlice("ingroup1", []int{1}, "ingroup1")
|
|
rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2")
|
|
|
|
childCmd.Flags().Bool("ingroup3", false, "ingroup3")
|
|
childCmd.Flags().Bool("nogroup", false, "nogroup")
|
|
|
|
// Add flags to a group
|
|
childCmd.MarkFlagsMutuallyExclusive("ingroup1", "ingroup2", "ingroup3")
|
|
|
|
return rootCmd
|
|
}
|
|
|
|
// Each test case uses a unique command from the function above.
|
|
testcases := []struct {
|
|
desc string
|
|
args []string
|
|
expectedOutput string
|
|
}{
|
|
{
|
|
desc: "flags in mutually exclusive group not suggested without the - prefix",
|
|
args: []string{"child", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"subArg",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "flags in mutually exclusive group suggested with the - prefix",
|
|
args: []string{"child", "-"},
|
|
expectedOutput: strings.Join([]string{
|
|
"--ingroup1",
|
|
"--ingroup2",
|
|
"--help",
|
|
"-h",
|
|
"--ingroup3",
|
|
"--nogroup",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "when flag in mutually exclusive group present, other flags in group not suggested even with the - prefix",
|
|
args: []string{"child", "--ingroup1", "8", "-"},
|
|
expectedOutput: strings.Join([]string{
|
|
"--ingroup1", // Should be suggested again since it is a slice
|
|
"--help",
|
|
"-h",
|
|
"--nogroup",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "group ignored if some flags not applicable",
|
|
args: []string{"--ingroup1", "8", "-"},
|
|
expectedOutput: strings.Join([]string{
|
|
"--help",
|
|
"-h",
|
|
"--ingroup1",
|
|
"--ingroup2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
c := getCmd()
|
|
args := []string{ShellCompNoDescRequestCmd}
|
|
args = append(args, tc.args...)
|
|
output, err := executeCommand(c, args...)
|
|
switch {
|
|
case err == nil && output != tc.expectedOutput:
|
|
t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
|
|
case err != nil:
|
|
t.Errorf("Unexpected error %q", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCompletionCobraFlags(t *testing.T) {
|
|
getCmd := func() *Command {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Version: "1.1.1",
|
|
Run: emptyRun,
|
|
}
|
|
childCmd := &Command{
|
|
Use: "child",
|
|
Version: "1.1.1",
|
|
Run: emptyRun,
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"extra"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
}
|
|
childCmd2 := &Command{
|
|
Use: "child2",
|
|
Version: "1.1.1",
|
|
Run: emptyRun,
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"extra2"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
}
|
|
childCmd3 := &Command{
|
|
Use: "child3",
|
|
Version: "1.1.1",
|
|
Run: emptyRun,
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"extra3"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
}
|
|
childCmd4 := &Command{
|
|
Use: "child4",
|
|
Version: "1.1.1",
|
|
Run: emptyRun,
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"extra4"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
DisableFlagParsing: true,
|
|
}
|
|
childCmd5 := &Command{
|
|
Use: "child5",
|
|
Version: "1.1.1",
|
|
Run: emptyRun,
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"extra5"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
DisableFlagParsing: true,
|
|
}
|
|
|
|
rootCmd.AddCommand(childCmd, childCmd2, childCmd3, childCmd4, childCmd5)
|
|
|
|
_ = childCmd.Flags().Bool("bool", false, "A bool flag")
|
|
_ = childCmd.MarkFlagRequired("bool")
|
|
|
|
// Have a command that adds its own help and version flag
|
|
_ = childCmd2.Flags().BoolP("help", "h", false, "My own help")
|
|
_ = childCmd2.Flags().BoolP("version", "v", false, "My own version")
|
|
|
|
// Have a command that only adds its own -v flag
|
|
_ = childCmd3.Flags().BoolP("verbose", "v", false, "Not a version flag")
|
|
|
|
// Have a command that DisablesFlagParsing but that also adds its own help and version flags
|
|
_ = childCmd5.Flags().BoolP("help", "h", false, "My own help")
|
|
_ = childCmd5.Flags().BoolP("version", "v", false, "My own version")
|
|
|
|
return rootCmd
|
|
}
|
|
|
|
// Each test case uses a unique command from the function above.
|
|
testcases := []struct {
|
|
desc string
|
|
args []string
|
|
expectedOutput string
|
|
}{
|
|
{
|
|
desc: "completion of help and version flags",
|
|
args: []string{"-"},
|
|
expectedOutput: strings.Join([]string{
|
|
"--help",
|
|
"-h",
|
|
"--version",
|
|
"-v",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "no completion after --help flag",
|
|
args: []string{"--help", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "no completion after -h flag",
|
|
args: []string{"-h", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "no completion after --version flag",
|
|
args: []string{"--version", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "no completion after -v flag",
|
|
args: []string{"-v", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "no completion after --help flag even with other completions",
|
|
args: []string{"child", "--help", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "no completion after -h flag even with other completions",
|
|
args: []string{"child", "-h", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "no completion after --version flag even with other completions",
|
|
args: []string{"child", "--version", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "no completion after -v flag even with other completions",
|
|
args: []string{"child", "-v", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "no completion after -v flag even with other flag completions",
|
|
args: []string{"child", "-v", "-"},
|
|
expectedOutput: strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "completion after --help flag when created by program",
|
|
args: []string{"child2", "--help", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"extra2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "completion after -h flag when created by program",
|
|
args: []string{"child2", "-h", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"extra2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "completion after --version flag when created by program",
|
|
args: []string{"child2", "--version", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"extra2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "completion after -v flag when created by program",
|
|
args: []string{"child2", "-v", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"extra2",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "completion after --version when only -v flag was created by program",
|
|
args: []string{"child3", "--version", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "completion after -v flag when only -v flag was created by program",
|
|
args: []string{"child3", "-v", ""},
|
|
expectedOutput: strings.Join([]string{
|
|
"extra3",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "no completion for --help/-h and --version/-v flags when DisableFlagParsing=true",
|
|
args: []string{"child4", "-"},
|
|
expectedOutput: strings.Join([]string{
|
|
"extra4",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
{
|
|
desc: "completions for program-defined --help/-h and --version/-v flags even when DisableFlagParsing=true",
|
|
args: []string{"child5", "-"},
|
|
expectedOutput: strings.Join([]string{
|
|
"--help",
|
|
"-h",
|
|
"--version",
|
|
"-v",
|
|
"extra5",
|
|
":4",
|
|
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
c := getCmd()
|
|
args := []string{ShellCompNoDescRequestCmd}
|
|
args = append(args, tc.args...)
|
|
output, err := executeCommand(c, args...)
|
|
switch {
|
|
case err == nil && output != tc.expectedOutput:
|
|
t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
|
|
case err != nil:
|
|
t.Errorf("Unexpected error %q", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestArgsNotDetectedAsFlagsCompletionInGo(t *testing.T) {
|
|
// Regression test that ensures the bug described in
|
|
// https://github.com/spf13/cobra/issues/1816 does not occur anymore.
|
|
|
|
root := Command{
|
|
Use: "root",
|
|
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"service", "1-123", "11-123"}, ShellCompDirectiveNoFileComp
|
|
},
|
|
}
|
|
|
|
completion := `service
|
|
1-123
|
|
11-123
|
|
:4
|
|
Completion ended with directive: ShellCompDirectiveNoFileComp
|
|
`
|
|
|
|
testcases := []struct {
|
|
desc string
|
|
args []string
|
|
expectedOutput string
|
|
}{
|
|
{
|
|
desc: "empty",
|
|
args: []string{""},
|
|
expectedOutput: completion,
|
|
},
|
|
{
|
|
desc: "service only",
|
|
args: []string{"service", ""},
|
|
expectedOutput: completion,
|
|
},
|
|
{
|
|
desc: "service last",
|
|
args: []string{"1-123", "service", ""},
|
|
expectedOutput: completion,
|
|
},
|
|
{
|
|
desc: "two digit prefixed dash last",
|
|
args: []string{"service", "11-123", ""},
|
|
expectedOutput: completion,
|
|
},
|
|
{
|
|
desc: "one digit prefixed dash last",
|
|
args: []string{"service", "1-123", ""},
|
|
expectedOutput: completion,
|
|
},
|
|
}
|
|
for _, tc := range testcases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
args := []string{ShellCompNoDescRequestCmd}
|
|
args = append(args, tc.args...)
|
|
output, err := executeCommand(&root, args...)
|
|
switch {
|
|
case err == nil && output != tc.expectedOutput:
|
|
t.Errorf("expected: %q, got: %q", tc.expectedOutput, output)
|
|
case err != nil:
|
|
t.Errorf("Unexpected error %q", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetFlagCompletion(t *testing.T) {
|
|
rootCmd := &Command{Use: "root", Run: emptyRun}
|
|
|
|
rootCmd.Flags().String("rootflag", "", "root flag")
|
|
_ = rootCmd.RegisterFlagCompletionFunc("rootflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"rootvalue"}, ShellCompDirectiveKeepOrder
|
|
})
|
|
|
|
rootCmd.PersistentFlags().String("persistentflag", "", "persistent flag")
|
|
_ = rootCmd.RegisterFlagCompletionFunc("persistentflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"persistentvalue"}, ShellCompDirectiveDefault
|
|
})
|
|
|
|
childCmd := &Command{Use: "child", Run: emptyRun}
|
|
|
|
childCmd.Flags().String("childflag", "", "child flag")
|
|
_ = childCmd.RegisterFlagCompletionFunc("childflag", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
return []string{"childvalue"}, ShellCompDirectiveNoFileComp | ShellCompDirectiveNoSpace
|
|
})
|
|
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
testcases := []struct {
|
|
desc string
|
|
cmd *Command
|
|
flagName string
|
|
exists bool
|
|
comps []string
|
|
directive ShellCompDirective
|
|
}{
|
|
{
|
|
desc: "get flag completion function for command",
|
|
cmd: rootCmd,
|
|
flagName: "rootflag",
|
|
exists: true,
|
|
comps: []string{"rootvalue"},
|
|
directive: ShellCompDirectiveKeepOrder,
|
|
},
|
|
{
|
|
desc: "get persistent flag completion function for command",
|
|
cmd: rootCmd,
|
|
flagName: "persistentflag",
|
|
exists: true,
|
|
comps: []string{"persistentvalue"},
|
|
directive: ShellCompDirectiveDefault,
|
|
},
|
|
{
|
|
desc: "get flag completion function for child command",
|
|
cmd: childCmd,
|
|
flagName: "childflag",
|
|
exists: true,
|
|
comps: []string{"childvalue"},
|
|
directive: ShellCompDirectiveNoFileComp | ShellCompDirectiveNoSpace,
|
|
},
|
|
{
|
|
desc: "get persistent flag completion function for child command",
|
|
cmd: childCmd,
|
|
flagName: "persistentflag",
|
|
exists: true,
|
|
comps: []string{"persistentvalue"},
|
|
directive: ShellCompDirectiveDefault,
|
|
},
|
|
{
|
|
desc: "cannot get flag completion function for local parent flag",
|
|
cmd: childCmd,
|
|
flagName: "rootflag",
|
|
exists: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
compFunc, exists := tc.cmd.GetFlagCompletionFunc(tc.flagName)
|
|
if tc.exists != exists {
|
|
t.Errorf("Unexpected result looking for flag completion function")
|
|
}
|
|
|
|
if exists {
|
|
comps, directive := compFunc(tc.cmd, []string{}, "")
|
|
if strings.Join(tc.comps, " ") != strings.Join(comps, " ") {
|
|
t.Errorf("Unexpected completions %q", comps)
|
|
}
|
|
if tc.directive != directive {
|
|
t.Errorf("Unexpected directive %q", directive)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetEnvConfig(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
use string
|
|
suffix string
|
|
cmdVar string
|
|
globalVar string
|
|
cmdVal string
|
|
globalVal string
|
|
expected string
|
|
}{
|
|
{
|
|
desc: "Command envvar overrides global",
|
|
use: "root",
|
|
suffix: "test",
|
|
cmdVar: "ROOT_TEST",
|
|
globalVar: "COBRA_TEST",
|
|
cmdVal: "cmd",
|
|
globalVal: "global",
|
|
expected: "cmd",
|
|
},
|
|
{
|
|
desc: "Missing/empty command envvar falls back to global",
|
|
use: "root",
|
|
suffix: "test",
|
|
cmdVar: "ROOT_TEST",
|
|
globalVar: "COBRA_TEST",
|
|
cmdVal: "",
|
|
globalVal: "global",
|
|
expected: "global",
|
|
},
|
|
{
|
|
desc: "Missing/empty command and global envvars fall back to empty",
|
|
use: "root",
|
|
suffix: "test",
|
|
cmdVar: "ROOT_TEST",
|
|
globalVar: "COBRA_TEST",
|
|
cmdVal: "",
|
|
globalVal: "",
|
|
expected: "",
|
|
},
|
|
{
|
|
desc: "Periods in command use transform to underscores in env var name",
|
|
use: "foo.bar",
|
|
suffix: "test",
|
|
cmdVar: "FOO_BAR_TEST",
|
|
globalVar: "COBRA_TEST",
|
|
cmdVal: "cmd",
|
|
globalVal: "global",
|
|
expected: "cmd",
|
|
},
|
|
{
|
|
desc: "Dashes in command use transform to underscores in env var name",
|
|
use: "quux-BAZ",
|
|
suffix: "test",
|
|
cmdVar: "QUUX_BAZ_TEST",
|
|
globalVar: "COBRA_TEST",
|
|
cmdVal: "cmd",
|
|
globalVal: "global",
|
|
expected: "cmd",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
// Could make env handling cleaner with t.Setenv with Go >= 1.17
|
|
err := os.Setenv(tc.cmdVar, tc.cmdVal)
|
|
defer func() {
|
|
assertNoErr(t, os.Unsetenv(tc.cmdVar))
|
|
}()
|
|
assertNoErr(t, err)
|
|
err = os.Setenv(tc.globalVar, tc.globalVal)
|
|
defer func() {
|
|
assertNoErr(t, os.Unsetenv(tc.globalVar))
|
|
}()
|
|
assertNoErr(t, err)
|
|
cmd := &Command{Use: tc.use}
|
|
got := getEnvConfig(cmd, tc.suffix)
|
|
if got != tc.expected {
|
|
t.Errorf("expected: %q, got: %q", tc.expected, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDisableDescriptions(t *testing.T) {
|
|
rootCmd := &Command{
|
|
Use: "root",
|
|
Run: emptyRun,
|
|
}
|
|
|
|
childCmd := &Command{
|
|
Use: "thechild",
|
|
Short: "The child command",
|
|
Run: emptyRun,
|
|
}
|
|
rootCmd.AddCommand(childCmd)
|
|
|
|
specificDescriptionsEnvVar := configEnvVar(rootCmd.Name(), configEnvVarSuffixDescriptions)
|
|
globalDescriptionsEnvVar := configEnvVar(configEnvVarGlobalPrefix, configEnvVarSuffixDescriptions)
|
|
|
|
const (
|
|
descLineWithDescription = "first\tdescription"
|
|
descLineWithoutDescription = "first"
|
|
)
|
|
childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
comps := []string{descLineWithDescription}
|
|
return comps, ShellCompDirectiveDefault
|
|
}
|
|
|
|
testCases := []struct {
|
|
desc string
|
|
globalEnvValue string
|
|
specificEnvValue string
|
|
expectedLine string
|
|
}{
|
|
{
|
|
"No env variables set",
|
|
"",
|
|
"",
|
|
descLineWithDescription,
|
|
},
|
|
{
|
|
"Global value false",
|
|
"false",
|
|
"",
|
|
descLineWithoutDescription,
|
|
},
|
|
{
|
|
"Specific value false",
|
|
"",
|
|
"false",
|
|
descLineWithoutDescription,
|
|
},
|
|
{
|
|
"Both values false",
|
|
"false",
|
|
"false",
|
|
descLineWithoutDescription,
|
|
},
|
|
{
|
|
"Both values true",
|
|
"true",
|
|
"true",
|
|
descLineWithDescription,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
if err := os.Setenv(specificDescriptionsEnvVar, tc.specificEnvValue); err != nil {
|
|
t.Errorf("Unexpected error setting %s: %v", specificDescriptionsEnvVar, err)
|
|
}
|
|
if err := os.Setenv(globalDescriptionsEnvVar, tc.globalEnvValue); err != nil {
|
|
t.Errorf("Unexpected error setting %s: %v", globalDescriptionsEnvVar, err)
|
|
}
|
|
|
|
var run = func() {
|
|
output, err := executeCommand(rootCmd, ShellCompRequestCmd, "thechild", "")
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
|
|
expected := strings.Join([]string{
|
|
tc.expectedLine,
|
|
":0",
|
|
"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
|
|
if output != expected {
|
|
t.Errorf("expected: %q, got: %q", expected, output)
|
|
}
|
|
}
|
|
|
|
run()
|
|
|
|
// For empty cases, test also unset state
|
|
if tc.specificEnvValue == "" {
|
|
if err := os.Unsetenv(specificDescriptionsEnvVar); err != nil {
|
|
t.Errorf("Unexpected error unsetting %s: %v", specificDescriptionsEnvVar, err)
|
|
}
|
|
run()
|
|
}
|
|
if tc.globalEnvValue == "" {
|
|
if err := os.Unsetenv(globalDescriptionsEnvVar); err != nil {
|
|
t.Errorf("Unexpected error unsetting %s: %v", globalDescriptionsEnvVar, err)
|
|
}
|
|
run()
|
|
}
|
|
})
|
|
}
|
|
}
|