wand/commandline_test.go

919 lines
14 KiB
Go

package wand
import (
"fmt"
"strings"
"testing"
)
func TestCommand(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",
),
)
})
}