mirror of https://github.com/tailscale/tailscale/
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
5.6 KiB
Go
220 lines
5.6 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package internal_test
|
|
|
|
import (
|
|
_ "embed"
|
|
"flag"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/peterbourgon/ff/v3/ffcli"
|
|
"tailscale.com/cmd/tailscale/cli/ffcomplete"
|
|
"tailscale.com/cmd/tailscale/cli/ffcomplete/internal"
|
|
)
|
|
|
|
func newFlagSet(name string, errh flag.ErrorHandling, flags func(fs *flag.FlagSet)) *flag.FlagSet {
|
|
fs := flag.NewFlagSet(name, errh)
|
|
if flags != nil {
|
|
flags(fs)
|
|
}
|
|
return fs
|
|
}
|
|
|
|
func TestComplete(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Build our test program in testdata.
|
|
root := &ffcli.Command{
|
|
Name: "prog",
|
|
FlagSet: newFlagSet("prog", flag.ContinueOnError, func(fs *flag.FlagSet) {
|
|
fs.Bool("v", false, "verbose")
|
|
fs.Bool("root-bool", false, "root `bool`")
|
|
fs.String("root-str", "", "some `text`")
|
|
}),
|
|
Subcommands: []*ffcli.Command{
|
|
{
|
|
Name: "debug",
|
|
ShortHelp: "Debug data",
|
|
FlagSet: newFlagSet("prog debug", flag.ExitOnError, func(fs *flag.FlagSet) {
|
|
fs.String("cpu-profile", "", "write cpu profile to `file`")
|
|
fs.Bool("debug-bool", false, "debug bool")
|
|
fs.Int("level", 0, "a number")
|
|
fs.String("enum", "", "a flag that takes several specific values")
|
|
ffcomplete.Flag(fs, "enum", ffcomplete.Fixed("alpha", "beta", "charlie"))
|
|
}),
|
|
},
|
|
func() *ffcli.Command {
|
|
cmd := &ffcli.Command{
|
|
Name: "ping",
|
|
FlagSet: newFlagSet("prog ping", flag.ContinueOnError, func(fs *flag.FlagSet) {
|
|
fs.String("until", "", "when pinging should end")
|
|
ffcomplete.Flag(fs, "until", ffcomplete.Fixed("forever", "direct"))
|
|
}),
|
|
}
|
|
ffcomplete.Args(cmd, ffcomplete.Fixed("jupiter", "neptune", "venus"))
|
|
return cmd
|
|
}(),
|
|
},
|
|
}
|
|
|
|
tests := []struct {
|
|
args []string
|
|
showFlags bool
|
|
showDescs bool
|
|
wantComp []string
|
|
wantDir ffcomplete.ShellCompDirective
|
|
}{
|
|
{
|
|
args: []string{"deb"},
|
|
wantComp: []string{"debug"},
|
|
},
|
|
{
|
|
args: []string{"deb"},
|
|
showDescs: true,
|
|
wantComp: []string{"debug\tDebug data"},
|
|
},
|
|
{
|
|
args: []string{"-"},
|
|
wantComp: []string{"--root-bool", "--root-str", "-v"},
|
|
},
|
|
{
|
|
args: []string{"--"},
|
|
wantComp: []string{"--root-bool", "--root-str", "--v"},
|
|
},
|
|
{
|
|
args: []string{"-r"},
|
|
wantComp: []string{"-root-bool", "-root-str"},
|
|
},
|
|
{
|
|
args: []string{"--r"},
|
|
wantComp: []string{"--root-bool", "--root-str"},
|
|
},
|
|
{
|
|
args: []string{"--root-str=s", "--r"},
|
|
wantComp: []string{"--root-bool"}, // omits --root-str which is already set
|
|
},
|
|
{
|
|
// '--' disables flag parsing, so we shouldn't suggest flags.
|
|
args: []string{"--", "--root"},
|
|
wantComp: nil,
|
|
},
|
|
{
|
|
// '--' is used as the value of '--root-str'.
|
|
args: []string{"--root-str", "--", "--r"},
|
|
wantComp: []string{"--root-bool"},
|
|
},
|
|
{
|
|
// '--' here is a flag value, so doesn't disable flag parsing.
|
|
args: []string{"--root-str", "--", "--root"},
|
|
wantComp: []string{"--root-bool"},
|
|
},
|
|
{
|
|
// Equivalent to '--root-str=-- -- --r' meaning '--r' is not
|
|
// a flag because it's preceded by a '--' argument:
|
|
// https://go.dev/play/p/UCtftQqVhOD.
|
|
args: []string{"--root-str", "--", "--", "--r"},
|
|
wantComp: nil,
|
|
},
|
|
{
|
|
args: []string{"--root-bool="},
|
|
wantComp: []string{"true", "false"},
|
|
},
|
|
{
|
|
args: []string{"--root-bool=t"},
|
|
wantComp: []string{"true"},
|
|
},
|
|
{
|
|
args: []string{"--root-bool=T"},
|
|
wantComp: []string{"TRUE"},
|
|
},
|
|
{
|
|
args: []string{"debug", "--de"},
|
|
wantComp: []string{"--debug-bool"},
|
|
},
|
|
{
|
|
args: []string{"debug", "--enum="},
|
|
wantComp: []string{"alpha", "beta", "charlie"},
|
|
wantDir: ffcomplete.ShellCompDirectiveNoFileComp,
|
|
},
|
|
{
|
|
args: []string{"debug", "--enum=al"},
|
|
wantComp: []string{"alpha"},
|
|
wantDir: ffcomplete.ShellCompDirectiveNoFileComp,
|
|
},
|
|
{
|
|
args: []string{"debug", "--level", ""},
|
|
wantComp: nil,
|
|
},
|
|
{
|
|
args: []string{"debug", "--enum", "b"},
|
|
wantComp: []string{"beta"},
|
|
wantDir: ffcomplete.ShellCompDirectiveNoFileComp,
|
|
},
|
|
{
|
|
args: []string{"debug", "--enum", "al"},
|
|
wantComp: []string{"alpha"},
|
|
wantDir: ffcomplete.ShellCompDirectiveNoFileComp,
|
|
},
|
|
{
|
|
args: []string{"ping", ""},
|
|
showFlags: true,
|
|
wantComp: []string{"--until", "jupiter", "neptune", "venus"},
|
|
wantDir: ffcomplete.ShellCompDirectiveNoFileComp,
|
|
},
|
|
{
|
|
args: []string{"ping", ""},
|
|
showFlags: true,
|
|
showDescs: true,
|
|
wantComp: []string{
|
|
"--until\twhen pinging should end",
|
|
"jupiter",
|
|
"neptune",
|
|
"venus",
|
|
},
|
|
wantDir: ffcomplete.ShellCompDirectiveNoFileComp,
|
|
},
|
|
{
|
|
args: []string{"ping", ""},
|
|
wantComp: []string{"jupiter", "neptune", "venus"},
|
|
wantDir: ffcomplete.ShellCompDirectiveNoFileComp,
|
|
},
|
|
{
|
|
args: []string{"ping", "j"},
|
|
wantComp: []string{"jupiter"},
|
|
wantDir: ffcomplete.ShellCompDirectiveNoFileComp,
|
|
},
|
|
}
|
|
|
|
// Run the tests.
|
|
for _, test := range tests {
|
|
test := test
|
|
name := strings.Join(test.args, "␣")
|
|
if test.showFlags {
|
|
name += "+flags"
|
|
}
|
|
if test.showDescs {
|
|
name += "+descs"
|
|
}
|
|
t.Run(name, func(t *testing.T) {
|
|
// Capture the binary
|
|
complete, dir, err := internal.Complete(root, test.args, test.showFlags, test.showDescs)
|
|
if err != nil {
|
|
t.Fatalf("completion error: %s", err)
|
|
}
|
|
|
|
// Test the results match our expectation.
|
|
if test.wantComp != nil {
|
|
if diff := cmp.Diff(test.wantComp, complete); diff != "" {
|
|
t.Errorf("unexpected completion directives (-want +got):\n%s", diff)
|
|
}
|
|
}
|
|
if test.wantDir != dir {
|
|
t.Errorf("got shell completion directive %[1]d (%[1]s), want %[2]d (%[2]s)", dir, test.wantDir)
|
|
}
|
|
})
|
|
}
|
|
}
|