2017-05-05 08:57:22 -05:00
|
|
|
package complete
|
|
|
|
|
|
|
|
import (
|
2017-11-04 03:21:41 -05:00
|
|
|
"bytes"
|
2018-10-19 12:10:37 -05:00
|
|
|
"fmt"
|
2017-05-05 08:57:22 -05:00
|
|
|
"os"
|
|
|
|
"sort"
|
2018-10-19 12:10:37 -05:00
|
|
|
"strconv"
|
2017-11-04 03:21:41 -05:00
|
|
|
"strings"
|
2017-05-05 08:57:22 -05:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestCompleter_Complete(t *testing.T) {
|
2017-05-06 15:06:58 -05:00
|
|
|
initTests()
|
2017-05-05 08:57:22 -05:00
|
|
|
|
2017-05-06 00:24:17 -05:00
|
|
|
c := Command{
|
2017-05-15 11:32:59 -05:00
|
|
|
Sub: Commands{
|
2017-05-06 00:24:17 -05:00
|
|
|
"sub1": {
|
2017-05-15 11:32:59 -05:00
|
|
|
Flags: Flags{
|
2017-05-06 00:24:17 -05:00
|
|
|
"-flag1": PredictAnything,
|
|
|
|
"-flag2": PredictNothing,
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
},
|
2017-05-06 00:24:17 -05:00
|
|
|
"sub2": {
|
2017-05-15 11:32:59 -05:00
|
|
|
Flags: Flags{
|
2017-05-06 00:24:17 -05:00
|
|
|
"-flag2": PredictNothing,
|
|
|
|
"-flag3": PredictSet("opt1", "opt2", "opt12"),
|
|
|
|
},
|
2017-05-11 13:57:44 -05:00
|
|
|
Args: PredictFiles("*.md"),
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
},
|
2017-05-15 11:32:59 -05:00
|
|
|
Flags: Flags{
|
|
|
|
"-o": PredictFiles("*.txt"),
|
|
|
|
},
|
|
|
|
GlobalFlags: Flags{
|
2017-05-06 00:24:17 -05:00
|
|
|
"-h": PredictNothing,
|
|
|
|
"-global1": PredictAnything,
|
|
|
|
},
|
2017-05-05 08:57:22 -05:00
|
|
|
}
|
2017-11-04 03:21:41 -05:00
|
|
|
cmp := New("cmd", c)
|
2017-05-05 08:57:22 -05:00
|
|
|
|
|
|
|
tests := []struct {
|
2018-10-19 12:10:37 -05:00
|
|
|
line string
|
|
|
|
point int // -1 indicates len(line)
|
|
|
|
want []string
|
2017-05-05 08:57:22 -05:00
|
|
|
}{
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"sub1", "sub2"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"-h", "-global1", "-o"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -h ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"sub1", "sub2"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -global1 ", // global1 is known follow flag
|
|
|
|
point: -1,
|
|
|
|
want: []string{},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"sub1", "sub2"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub1",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"sub1"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub2",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"sub2"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
2017-08-23 15:16:52 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub1 ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{},
|
2017-08-23 15:16:52 -05:00
|
|
|
},
|
2017-05-05 08:57:22 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub1 -",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"-flag1", "-flag2", "-h", "-global1"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub2 ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"./", "dir/", "outer/", "readme.md"},
|
2017-05-05 15:07:18 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub2 ./",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"./", "./readme.md", "./dir/", "./outer/"},
|
2017-05-05 15:07:18 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub2 re",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"readme.md"},
|
2017-05-15 15:52:04 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub2 ./re",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"./readme.md"},
|
2017-05-05 15:07:18 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub2 -flag2 ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"./", "dir/", "outer/", "readme.md"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub1 -fl",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"-flag1", "-flag2"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub1 -flag1",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"-flag1"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub1 -flag1 ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{}, // flag1 is unknown follow flag
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd sub1 -flag2 -",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"-flag1", "-flag2", "-h", "-global1"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -no-such-flag",
|
|
|
|
point: -1,
|
|
|
|
want: []string{},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -no-such-flag ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"sub1", "sub2"},
|
2017-08-24 15:22:31 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -no-such-flag -",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"-h", "-global1", "-o"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd no-such-command",
|
|
|
|
point: -1,
|
|
|
|
want: []string{},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd no-such-command ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"sub1", "sub2"},
|
2017-05-05 08:57:22 -05:00
|
|
|
},
|
2017-05-05 13:57:21 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"a.txt", "b.txt", "c.txt", ".dot.txt", "./", "dir/", "outer/"},
|
2017-05-05 15:07:18 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o ./no-su",
|
|
|
|
point: -1,
|
|
|
|
want: []string{},
|
2017-05-05 15:07:18 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o ./",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"},
|
2017-05-15 15:52:04 -05:00
|
|
|
},
|
2017-11-04 04:20:50 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o=./",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"},
|
2017-11-04 04:20:50 -05:00
|
|
|
},
|
2017-05-15 15:52:04 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o .",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"},
|
2017-05-05 13:57:21 -05:00
|
|
|
},
|
2017-11-04 04:20:50 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o ./b",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"./b.txt"},
|
2017-11-04 04:20:50 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o=./b",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"./b.txt"},
|
2017-11-04 04:20:50 -05:00
|
|
|
},
|
2017-05-05 13:57:21 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o ./read",
|
|
|
|
point: -1,
|
|
|
|
want: []string{},
|
2017-05-05 13:57:21 -05:00
|
|
|
},
|
2017-11-04 04:20:50 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o=./read",
|
|
|
|
point: -1,
|
|
|
|
want: []string{},
|
2017-11-04 04:20:50 -05:00
|
|
|
},
|
2017-05-05 13:57:21 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o ./readme.md",
|
|
|
|
point: -1,
|
|
|
|
want: []string{},
|
2017-05-05 15:07:18 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o ./readme.md ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"sub1", "sub2"},
|
2017-05-05 13:57:21 -05:00
|
|
|
},
|
2017-11-04 04:20:50 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o=./readme.md ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"sub1", "sub2"},
|
2017-11-04 04:20:50 -05:00
|
|
|
},
|
2017-05-05 16:53:03 -05:00
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o sub2 -flag3 ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"opt1", "opt2", "opt12"},
|
2017-05-05 16:53:03 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o sub2 -flag3 opt1",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"opt1", "opt12"},
|
2017-05-05 16:53:03 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -o sub2 -flag3 opt",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"opt1", "opt2", "opt12"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
line: "cmd -o ./b foo",
|
|
|
|
// ^
|
|
|
|
point: 10,
|
|
|
|
want: []string{"./b.txt"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
line: "cmd -o=./b foo",
|
|
|
|
// ^
|
|
|
|
point: 10,
|
|
|
|
want: []string{"./b.txt"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
line: "cmd -o sub2 -flag3 optfoo",
|
|
|
|
// ^
|
|
|
|
point: 22,
|
|
|
|
want: []string{"opt1", "opt2", "opt12"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
line: "cmd -o ",
|
|
|
|
// ^
|
|
|
|
point: 4,
|
|
|
|
want: []string{"sub1", "sub2"},
|
2017-05-05 16:53:03 -05:00
|
|
|
},
|
2017-05-05 08:57:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
2018-10-19 12:10:37 -05:00
|
|
|
t.Run(fmt.Sprintf("%s@%d", tt.line, tt.point), func(t *testing.T) {
|
|
|
|
got := runComplete(cmp, tt.line, tt.point)
|
2017-05-05 08:57:22 -05:00
|
|
|
|
|
|
|
sort.Strings(tt.want)
|
|
|
|
sort.Strings(got)
|
|
|
|
|
|
|
|
if !equalSlices(got, tt.want) {
|
2017-11-04 03:21:41 -05:00
|
|
|
t.Errorf("failed '%s'\ngot: %s\nwant: %s", t.Name(), got, tt.want)
|
2017-05-05 08:57:22 -05:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-23 15:58:31 -05:00
|
|
|
func TestCompleter_Complete_SharedPrefix(t *testing.T) {
|
|
|
|
initTests()
|
|
|
|
|
|
|
|
c := Command{
|
|
|
|
Sub: Commands{
|
|
|
|
"status": {
|
|
|
|
Flags: Flags{
|
|
|
|
"-f3": PredictNothing,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"job": {
|
|
|
|
Sub: Commands{
|
|
|
|
"status": {
|
|
|
|
Flags: Flags{
|
|
|
|
"-f4": PredictNothing,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Flags: Flags{
|
|
|
|
"-o": PredictFiles("*.txt"),
|
|
|
|
},
|
|
|
|
GlobalFlags: Flags{
|
|
|
|
"-h": PredictNothing,
|
|
|
|
"-global1": PredictAnything,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2017-11-04 03:21:41 -05:00
|
|
|
cmp := New("cmd", c)
|
|
|
|
|
2017-08-23 15:58:31 -05:00
|
|
|
tests := []struct {
|
2018-10-19 12:10:37 -05:00
|
|
|
line string
|
|
|
|
point int // -1 indicates len(line)
|
|
|
|
want []string
|
2017-08-23 15:58:31 -05:00
|
|
|
}{
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"status", "job"},
|
2017-08-23 15:58:31 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd -",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"-h", "-global1", "-o"},
|
2017-08-23 15:58:31 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd j",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"job"},
|
2017-08-23 15:58:31 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd job ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"status"},
|
2017-08-24 19:37:26 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd job -",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"-h", "-global1"},
|
2017-08-23 15:58:31 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd job status ",
|
|
|
|
point: -1,
|
|
|
|
want: []string{},
|
2017-08-24 19:37:26 -05:00
|
|
|
},
|
|
|
|
{
|
2018-10-19 12:10:37 -05:00
|
|
|
line: "cmd job status -",
|
|
|
|
point: -1,
|
|
|
|
want: []string{"-f4", "-h", "-global1"},
|
2017-08-23 15:58:31 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
2018-10-19 12:10:37 -05:00
|
|
|
t.Run(tt.line, func(t *testing.T) {
|
|
|
|
got := runComplete(cmp, tt.line, tt.point)
|
2017-08-23 15:58:31 -05:00
|
|
|
|
|
|
|
sort.Strings(tt.want)
|
|
|
|
sort.Strings(got)
|
|
|
|
|
|
|
|
if !equalSlices(got, tt.want) {
|
|
|
|
t.Errorf("failed '%s'\ngot = %s\nwant: %s", t.Name(), got, tt.want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-04 03:21:41 -05:00
|
|
|
// runComplete runs the complete login for test purposes
|
|
|
|
// it gets the complete struct and command line arguments and returns
|
|
|
|
// the complete options
|
2018-10-19 12:10:37 -05:00
|
|
|
func runComplete(c *Complete, line string, point int) (completions []string) {
|
|
|
|
if point == -1 {
|
|
|
|
point = len(line)
|
|
|
|
}
|
|
|
|
os.Setenv(envLine, line)
|
|
|
|
os.Setenv(envPoint, strconv.Itoa(point))
|
2017-11-04 03:21:41 -05:00
|
|
|
b := bytes.NewBuffer(nil)
|
|
|
|
c.Out = b
|
|
|
|
c.Complete()
|
|
|
|
completions = parseOutput(b.String())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseOutput(output string) []string {
|
|
|
|
lines := strings.Split(output, "\n")
|
|
|
|
options := []string{}
|
|
|
|
for _, l := range lines {
|
|
|
|
if l != "" {
|
|
|
|
options = append(options, l)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return options
|
|
|
|
}
|
|
|
|
|
2017-05-05 08:57:22 -05:00
|
|
|
func equalSlices(a, b []string) bool {
|
|
|
|
if len(a) != len(b) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := range a {
|
|
|
|
if a[i] != b[i] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|