919 lines
15 KiB
Go
919 lines
15 KiB
Go
package wand
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestCommandLine(t *testing.T) {
|
|
type f struct {
|
|
One string
|
|
SecondField int
|
|
}
|
|
ff := func(f f) string {
|
|
return f.One + fmt.Sprint(f.SecondField)
|
|
}
|
|
|
|
type b struct{ One, Two, Three, Four bool }
|
|
fb := func(b b) string {
|
|
return fmt.Sprintf("%t;%t;%t;%t", b.One, b.Two, b.Three, b.Four)
|
|
}
|
|
|
|
fbp := func(b b, p ...string) string {
|
|
o := fb(b)
|
|
if len(p) == 0 {
|
|
return o
|
|
}
|
|
|
|
s := []string{o}
|
|
for _, pi := range p {
|
|
s = append(s, pi)
|
|
}
|
|
|
|
return strings.Join(s, ";")
|
|
}
|
|
|
|
type m struct {
|
|
One, Two bool
|
|
Three string
|
|
}
|
|
|
|
fm := func(m m) string {
|
|
return fmt.Sprintf("%t;%t;%s", m.One, m.Two, m.Three)
|
|
}
|
|
|
|
type l struct {
|
|
One []bool
|
|
Two []string
|
|
}
|
|
fl := func(l l) string {
|
|
var sb []string
|
|
for _, b := range l.One {
|
|
sb = append(sb, fmt.Sprint(b))
|
|
}
|
|
|
|
return strings.Join([]string{strings.Join(sb, ","), strings.Join(l.Two, ",")}, ";")
|
|
}
|
|
|
|
type lb struct{ One, Two, Three []bool }
|
|
flb := func(lb lb) string {
|
|
var s []string
|
|
for _, b := range [][]bool{lb.One, lb.Two, lb.Three} {
|
|
var sb []string
|
|
for _, bi := range b {
|
|
sb = append(sb, fmt.Sprint(bi))
|
|
}
|
|
|
|
s = append(s, strings.Join(sb, ","))
|
|
}
|
|
|
|
return strings.Join(s, ";")
|
|
}
|
|
|
|
fp := func(f f, a ...string) string {
|
|
o := ff(f)
|
|
return fmt.Sprintf("%s;%s", o, strings.Join(a, ","))
|
|
}
|
|
|
|
type d struct{ One2 bool }
|
|
fd := func(d d) string {
|
|
return fmt.Sprint(d.One2)
|
|
}
|
|
|
|
t.Run("no args", testExec(testCase{impl: ff, command: "foo"}, "", "0"))
|
|
|
|
t.Run("basic options", func(t *testing.T) {
|
|
t.Run("space", testExec(testCase{impl: ff, command: "foo --one baz --second-field 42"}, "", "baz42"))
|
|
t.Run("eq", testExec(testCase{impl: ff, command: "foo --one=baz --second-field=42"}, "", "baz42"))
|
|
})
|
|
|
|
t.Run("short options combined, explicit last", func(t *testing.T) {
|
|
t.Run(
|
|
"bool last",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one", "b", "two", "c", "three"),
|
|
command: "foo -abc true",
|
|
},
|
|
"",
|
|
"true;true;true;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"string last",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fm), "a", "one", "b", "two", "c", "three"),
|
|
command: "foo -abc bar",
|
|
},
|
|
"",
|
|
"true;true;bar",
|
|
),
|
|
)
|
|
})
|
|
|
|
t.Run("multiple values", func(t *testing.T) {
|
|
t.Run(
|
|
"bools, short",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fl), "a", "one"),
|
|
command: "foo -a -a -a",
|
|
},
|
|
"",
|
|
"true,true,true;",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"bools, short, combined",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fl), "a", "one"),
|
|
command: "foo -aaa",
|
|
},
|
|
"",
|
|
"true,true,true;",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"bools, short, explicit",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fl), "a", "one"),
|
|
command: "foo -a true -a true -a true",
|
|
},
|
|
"",
|
|
"true,true,true;",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"bools, short, combined, last explicit",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fl), "a", "one"),
|
|
command: "foo -aaa true",
|
|
},
|
|
"",
|
|
"true,true,true;",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"bools, long",
|
|
testExec(
|
|
testCase{
|
|
impl: fl,
|
|
command: "foo --one --one --one",
|
|
},
|
|
"",
|
|
"true,true,true;",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"bools, long, explicit",
|
|
testExec(
|
|
testCase{
|
|
impl: fl,
|
|
command: "foo --one true --one true --one true",
|
|
},
|
|
"",
|
|
"true,true,true;"),
|
|
)
|
|
|
|
t.Run(
|
|
"mixd, short",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fl), "a", "one", "b", "two"),
|
|
command: "foo -a -b bar",
|
|
},
|
|
"",
|
|
"true;bar",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"mixed, short, combined",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fl), "a", "one", "b", "two"),
|
|
command: "foo -ab bar",
|
|
},
|
|
"",
|
|
"true;bar",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"mixed, long",
|
|
testExec(
|
|
testCase{
|
|
impl: fl,
|
|
command: "foo --one --two bar",
|
|
},
|
|
"",
|
|
"true;bar",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"mixed, long, explicit",
|
|
testExec(
|
|
testCase{
|
|
impl: fl,
|
|
command: "foo --one true --two bar",
|
|
},
|
|
"",
|
|
"true;bar",
|
|
),
|
|
)
|
|
})
|
|
|
|
t.Run("implicit bool option", func(t *testing.T) {
|
|
t.Run(
|
|
"short",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one"),
|
|
command: "foo -a",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, multiple",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one", "b", "two", "c", "three"),
|
|
command: "foo -a -b -c",
|
|
},
|
|
"",
|
|
"true;true;true;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, combined",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one", "b", "two", "c", "three"),
|
|
command: "foo -abc",
|
|
},
|
|
"",
|
|
"true;true;true;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, combined, multiple",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one", "b", "two", "c", "three", "d", "four"),
|
|
command: "foo -ab -cd",
|
|
},
|
|
"",
|
|
"true;true;true;true",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, multiple values",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", flb), "a", "one", "b", "two", "c", "three"),
|
|
command: "foo -aba -cab",
|
|
},
|
|
"",
|
|
"true,true,true;true,true;true",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"long",
|
|
testExec(
|
|
testCase{
|
|
impl: fb,
|
|
command: "foo --one",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"long, multiple",
|
|
testExec(
|
|
testCase{
|
|
impl: fb,
|
|
command: "foo --one --two --three",
|
|
},
|
|
"",
|
|
"true;true;true;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"long, multiple values",
|
|
testExec(
|
|
testCase{
|
|
impl: flb,
|
|
command: "foo --one --two --one",
|
|
},
|
|
"",
|
|
"true,true;true;",
|
|
),
|
|
)
|
|
})
|
|
|
|
t.Run("explicit bool option", func(t *testing.T) {
|
|
t.Run(
|
|
"short, true",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one"),
|
|
command: "foo -a true",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, false",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one"),
|
|
command: "foo -a false",
|
|
},
|
|
"",
|
|
"false;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, with eq",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one"),
|
|
command: "foo -a=true",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, true variant, capital",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one"),
|
|
command: "foo -a True",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, true variant, 1",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one"),
|
|
command: "foo -a 1",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, false variant, 0",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one"),
|
|
command: "foo -a 0",
|
|
},
|
|
"",
|
|
"false;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, combined",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one", "b", "two"),
|
|
command: "foo -ab true",
|
|
},
|
|
"",
|
|
"true;true;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, combined, multiple",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one", "b", "two", "c", "three", "d", "four"),
|
|
command: "foo -ab true -cd true",
|
|
},
|
|
"",
|
|
"true;true;true;true",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"long",
|
|
testExec(
|
|
testCase{
|
|
impl: fb,
|
|
command: "foo --one true",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"long, false",
|
|
testExec(
|
|
testCase{
|
|
impl: fb,
|
|
command: "foo --one false",
|
|
},
|
|
"",
|
|
"false;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"logn, with eq",
|
|
testExec(
|
|
testCase{
|
|
impl: fb,
|
|
command: "foo --one=true",
|
|
},
|
|
"",
|
|
"true;false;false;false"),
|
|
)
|
|
|
|
t.Run(
|
|
"long, mixed, first",
|
|
testExec(
|
|
testCase{
|
|
impl: fb,
|
|
command: "foo --one false --two",
|
|
},
|
|
"",
|
|
"false;true;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"long, mixed, last",
|
|
testExec(
|
|
testCase{
|
|
impl: fb,
|
|
command: "foo --one --two false",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
})
|
|
|
|
t.Run("expected bool option", func(t *testing.T) {
|
|
t.Run(
|
|
"short, implicit",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one"),
|
|
command: "foo -a",
|
|
},
|
|
"",
|
|
"true;false;false;false"),
|
|
)
|
|
|
|
t.Run(
|
|
"short, explicit",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one"),
|
|
command: "foo -a true",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, automatic positional",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fbp), "a", "one"),
|
|
command: "foo -a bar",
|
|
},
|
|
"",
|
|
"true;false;false;false;bar",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, combined",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fb), "a", "one", "b", "two"),
|
|
command: "foo -ab true",
|
|
},
|
|
"",
|
|
"true;true;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short, combined, automatic positional",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fbp), "a", "one", "b", "two"),
|
|
command: "foo -ab bar",
|
|
},
|
|
"",
|
|
"true;true;false;false;bar"),
|
|
)
|
|
|
|
t.Run(
|
|
"long, implicit",
|
|
testExec(
|
|
testCase{
|
|
impl: fb,
|
|
command: "foo --one",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"long, explicit",
|
|
testExec(
|
|
testCase{
|
|
impl: fb,
|
|
command: "foo --one true",
|
|
},
|
|
"",
|
|
"true;false;false;false",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"long, automatic positional",
|
|
testExec(
|
|
testCase{
|
|
impl: fbp,
|
|
command: "foo --one bar",
|
|
},
|
|
"",
|
|
"true;false;false;false;bar",
|
|
),
|
|
)
|
|
})
|
|
|
|
t.Run("positional", func(t *testing.T) {
|
|
t.Run(
|
|
"basic",
|
|
testExec(
|
|
testCase{
|
|
impl: fp,
|
|
command: "foo bar baz",
|
|
},
|
|
"",
|
|
"0;bar,baz",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"explicit",
|
|
testExec(
|
|
testCase{
|
|
impl: fp,
|
|
command: "foo -- bar baz",
|
|
},
|
|
"",
|
|
"0;bar,baz",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"mixed",
|
|
testExec(
|
|
testCase{
|
|
impl: fp,
|
|
command: "foo bar -- baz",
|
|
},
|
|
"",
|
|
"0;bar,baz",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"with option",
|
|
testExec(
|
|
testCase{
|
|
impl: fp,
|
|
command: "foo bar --second-field 42 baz",
|
|
},
|
|
"",
|
|
"42;bar,baz",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"with bool option at the end",
|
|
testExec(
|
|
testCase{
|
|
impl: fbp,
|
|
command: "foo bar baz --one",
|
|
},
|
|
"",
|
|
"true;false;false;false;bar;baz",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"with expected bool, implicit",
|
|
testExec(
|
|
testCase{
|
|
impl: fbp,
|
|
command: "foo bar --one baz",
|
|
},
|
|
"",
|
|
"true;false;false;false;bar;baz",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"with expected bool, explicit",
|
|
testExec(
|
|
testCase{
|
|
impl: fbp,
|
|
command: "foo bar --one true baz",
|
|
},
|
|
"",
|
|
"true;false;false;false;bar;baz",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"option format",
|
|
testExec(
|
|
testCase{
|
|
impl: fbp,
|
|
command: "foo -- --one",
|
|
},
|
|
"",
|
|
"false;false;false;false;--one",
|
|
),
|
|
)
|
|
})
|
|
|
|
t.Run("example", func(t *testing.T) {
|
|
type s struct {
|
|
Foo bool
|
|
Bar []bool
|
|
Qux bool
|
|
Quux string
|
|
}
|
|
|
|
fs := func(s s, a1, a2 string) string {
|
|
var sbar []string
|
|
for _, b := range s.Bar {
|
|
sbar = append(sbar, fmt.Sprint(b))
|
|
}
|
|
|
|
return fmt.Sprintf("%t;%s;%t;%s;%s;%s", s.Foo, strings.Join(sbar, ","), s.Qux, s.Quux, a1, a2)
|
|
}
|
|
|
|
t.Run(
|
|
"full",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fs), "a", "foo", "b", "bar"),
|
|
command: "foo -ab --bar baz -b --qux --quux corge -- grault",
|
|
},
|
|
"",
|
|
"true;true,true,true;true;corge;baz;grault",
|
|
),
|
|
)
|
|
})
|
|
|
|
t.Run("expected or unexpected", func(t *testing.T) {
|
|
t.Run(
|
|
"capital letters",
|
|
testExec(
|
|
testCase{
|
|
impl: fp,
|
|
command: "foo --One bar",
|
|
},
|
|
"",
|
|
"0;--One,bar",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"digit in option name",
|
|
testExec(
|
|
testCase{
|
|
impl: fd,
|
|
command: "foo --one-2",
|
|
},
|
|
"",
|
|
"true",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"dash in option name",
|
|
testExec(
|
|
testCase{
|
|
impl: ff,
|
|
command: "foo --second-field 42",
|
|
},
|
|
"",
|
|
"42",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"unpexpected character",
|
|
testExec(
|
|
testCase{
|
|
impl: fp,
|
|
command: "foo --one#",
|
|
},
|
|
"",
|
|
"0;--one#",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"invalid short option set",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fp), "a", "one", "b", "one", "c", "second-field"),
|
|
command: "foo -aBc",
|
|
},
|
|
"",
|
|
"0;-aBc",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"positional separator, no value",
|
|
testExec(
|
|
testCase{
|
|
impl: fp,
|
|
command: "foo --one bar --",
|
|
},
|
|
"",
|
|
"bar0;",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"positional separator, expecting value",
|
|
testExec(
|
|
testCase{
|
|
impl: fp,
|
|
command: "foo --one --",
|
|
},
|
|
"--one",
|
|
"",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"shot flag set, expecting value",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fp), "b", "second-field"),
|
|
command: "foo --one -b",
|
|
},
|
|
"--one",
|
|
"",
|
|
),
|
|
)
|
|
})
|
|
|
|
t.Run("preserve order", func(t *testing.T) {
|
|
t.Run(
|
|
"bools",
|
|
testExec(
|
|
testCase{
|
|
impl: fl,
|
|
command: "foo --one --one false --one",
|
|
},
|
|
"",
|
|
"true,false,true;",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"strings",
|
|
testExec(
|
|
testCase{
|
|
impl: fl,
|
|
command: "foo --two 1 --two 2 --two 3",
|
|
},
|
|
"",
|
|
";1,2,3",
|
|
),
|
|
)
|
|
})
|
|
|
|
t.Run("select subcommand", func(t *testing.T) {
|
|
t.Run(
|
|
"named",
|
|
testExec(
|
|
testCase{
|
|
impl: Group("foo", Command("bar", ff), Command("baz", ff)),
|
|
command: "foo baz",
|
|
},
|
|
"",
|
|
"0",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"default",
|
|
testExec(
|
|
testCase{
|
|
impl: Group("foo", Command("bar", ff), Default(Command("baz", ff))),
|
|
command: "foo",
|
|
},
|
|
"",
|
|
"0",
|
|
),
|
|
)
|
|
})
|
|
|
|
t.Run("help option", func(t *testing.T) {
|
|
t.Run(
|
|
"short form not defined",
|
|
testExec(
|
|
testCase{
|
|
impl: fm,
|
|
command: "foo -h",
|
|
},
|
|
"foo help",
|
|
"",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short form not help",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fm), "h", "one"),
|
|
command: "foo -h",
|
|
},
|
|
"",
|
|
"true;false;",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"short form",
|
|
testExec(
|
|
testCase{
|
|
impl: ShortForm(Command("foo", fm), "h", "help"),
|
|
command: "foo -h",
|
|
contains: true,
|
|
},
|
|
"",
|
|
"Synopsis",
|
|
),
|
|
)
|
|
|
|
t.Run(
|
|
"long form",
|
|
testExec(
|
|
testCase{
|
|
impl: fm,
|
|
command: "foo --help",
|
|
contains: true,
|
|
},
|
|
"",
|
|
"Synopsis",
|
|
),
|
|
)
|
|
})
|
|
}
|