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(t, ff, "", "foo", "", "0")) t.Run("basic options", func(t *testing.T) { t.Run("space", testExec(t, ff, "", "foo --one baz --second-field 42", "", "baz42")) t.Run("eq", testExec(t, ff, "", "foo --one=baz --second-field=42", "", "baz42")) }) t.Run("short options combined, explicit last", func(t *testing.T) { t.Run("bool last", testExec(t, ShortForm(fb, "one", "a", "two", "b", "three", "c"), "", "foo -abc true", "", "true;true;true;false")) t.Run("string last", testExec(t, ShortForm(fm, "one", "a", "two", "b", "three", "c"), "", "foo -abc bar", "", "true;true;bar")) }) t.Run("multiple values", func(t *testing.T) { t.Run("bools, short", testExec(t, ShortForm(fl, "one", "a"), "", "foo -a -a -a", "", "true,true,true;")) t.Run("bools, short, combined", testExec(t, ShortForm(fl, "one", "a"), "", "foo -aaa", "", "true,true,true;")) t.Run("bools, short, explicit", testExec(t, ShortForm(fl, "one", "a"), "", "foo -a true -a true -a true", "", "true,true,true;")) t.Run("bools, short, combined, last explicit", testExec(t, ShortForm(fl, "one", "a"), "", "foo -aaa true", "", "true,true,true;")) t.Run("bools, long", testExec(t, fl, "", "foo --one --one --one", "", "true,true,true;")) t.Run("bools, long, explicit", testExec(t, fl, "", "foo --one true --one true --one true", "", "true,true,true;")) t.Run("mixd, short", testExec(t, ShortForm(fl, "one", "a", "two", "b"), "", "foo -a -b bar", "", "true;bar")) t.Run("mixed, short, combined", testExec(t, ShortForm(fl, "one", "a", "two", "b"), "", "foo -ab bar", "", "true;bar")) t.Run("mixed, long", testExec(t, fl, "", "foo --one --two bar", "", "true;bar")) t.Run("mixed, long, explicit", testExec(t, fl, "", "foo --one true --two bar", "", "true;bar")) }) t.Run("implicit bool option", func(t *testing.T) { t.Run("short", testExec(t, ShortForm(fb, "one", "a"), "", "foo -a", "", "true;false;false;false")) t.Run( "short, multiple", testExec(t, ShortForm(fb, "one", "a", "two", "b", "three", "c"), "", "foo -a -b -c", "", "true;true;true;false"), ) t.Run( "short, combined", testExec(t, ShortForm(fb, "one", "a", "two", "b", "three", "c"), "", "foo -abc", "", "true;true;true;false"), ) t.Run( "short, combined, multiple", testExec(t, ShortForm(fb, "one", "a", "two", "b", "three", "c", "four", "d"), "", "foo -ab -cd", "", "true;true;true;true"), ) t.Run( "short, multiple values", testExec(t, ShortForm(flb, "one", "a", "two", "b", "three", "c"), "", "foo -aba -cab", "", "true,true,true;true,true;true"), ) t.Run("long", testExec(t, fb, "", "foo --one", "", "true;false;false;false")) t.Run("long, multiple", testExec(t, fb, "", "foo --one --two --three", "", "true;true;true;false")) t.Run("long, multiple values", testExec(t, flb, "", "foo --one --two --one", "", "true,true;true;")) }) t.Run("explicit bool option", func(t *testing.T) { t.Run("short, true", testExec(t, ShortForm(fb, "one", "a"), "", "foo -a true", "", "true;false;false;false")) t.Run("short, false", testExec(t, ShortForm(fb, "one", "a"), "", "foo -a false", "", "false;false;false;false")) t.Run("short, with eq", testExec(t, ShortForm(fb, "one", "a"), "", "foo -a=true", "", "true;false;false;false")) t.Run("short, true variant, capital", testExec(t, ShortForm(fb, "one", "a"), "", "foo -a True", "", "true;false;false;false")) t.Run("short, true variant, 1", testExec(t, ShortForm(fb, "one", "a"), "", "foo -a 1", "", "true;false;false;false")) t.Run("short, false variant, 0", testExec(t, ShortForm(fb, "one", "a"), "", "foo -a 0", "", "false;false;false;false")) t.Run("short, combined", testExec(t, ShortForm(fb, "one", "a", "two", "b"), "", "foo -ab true", "", "true;true;false;false")) t.Run( "short, combined, multiple", testExec( t, ShortForm(fb, "one", "a", "two", "b", "three", "c", "four", "d"), "", "foo -ab true -cd true", "", "true;true;true;true", ), ) t.Run("long", testExec(t, fb, "", "foo --one true", "", "true;false;false;false")) t.Run("long, false", testExec(t, fb, "", "foo --one false", "", "false;false;false;false")) t.Run("logn, with eq", testExec(t, fb, "", "foo --one=true", "", "true;false;false;false")) t.Run("long, mixed, first", testExec(t, fb, "", "foo --one false --two", "", "false;true;false;false")) t.Run("long, mixed, last", testExec(t, fb, "", "foo --one --two false", "", "true;false;false;false")) }) t.Run("expected bool option", func(t *testing.T) { t.Run("short, implicit", testExec(t, ShortForm(fb, "one", "a"), "", "foo -a", "", "true;false;false;false")) t.Run("short, explicit", testExec(t, ShortForm(fb, "one", "a"), "", "foo -a true", "", "true;false;false;false")) t.Run("short, automatic positional", testExec(t, ShortForm(fbp, "one", "a"), "", "foo -a bar", "", "true;false;false;false;bar")) t.Run("short, combined", testExec(t, ShortForm(fb, "one", "a", "two", "b"), "", "foo -ab true", "", "true;true;false;false")) t.Run( "short, combined, automatic positional", testExec(t, ShortForm(fbp, "one", "a", "two", "b"), "", "foo -ab bar", "", "true;true;false;false;bar"), ) t.Run("long, implicit", testExec(t, fb, "", "foo --one", "", "true;false;false;false")) t.Run("long, explicit", testExec(t, fb, "", "foo --one true", "", "true;false;false;false")) t.Run("long, automatic positional", testExec(t, fbp, "", "foo --one bar", "", "true;false;false;false;bar")) }) t.Run("positional", func(t *testing.T) { t.Run("basic", testExec(t, fp, "", "foo bar baz", "", "0;bar,baz")) t.Run("explicit", testExec(t, fp, "", "foo -- bar baz", "", "0;bar,baz")) t.Run("mixed", testExec(t, fp, "", "foo bar -- baz", "", "0;bar,baz")) t.Run("with option", testExec(t, fp, "", "foo bar --second-field 42 baz", "", "42;bar,baz")) t.Run("with bool option at the end", testExec(t, fbp, "", "foo bar baz --one", "", "true;false;false;false;bar;baz")) t.Run("with expected bool, implicit", testExec(t, fbp, "", "foo bar --one baz", "", "true;false;false;false;bar;baz")) t.Run("with expected bool, explicit", testExec(t, fbp, "", "foo bar --one true baz", "", "true;false;false;false;bar;baz")) t.Run("option format", testExec(t, fbp, "", "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( t, ShortForm(fs, "foo", "a", "bar", "b"), "", "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(t, fp, "", "foo --One bar", "", "0;--One,bar")) t.Run("digit in option name", testExec(t, fd, "", "foo --one-2", "", "true")) t.Run("dash in option name", testExec(t, ff, "", "foo --second-field 42", "", "42")) t.Run("unpexpected character", testExec(t, fp, "", "foo --one#", "", "0;--one#")) t.Run( "invalid short option set", testExec(t, ShortForm(fp, "one", "a", "one", "b", "second-field", "c"), "", "foo -aBc", "", "0;-aBc"), ) t.Run("positional separator, no value", testExec(t, fp, "", "foo --one bar --", "", "bar0;")) t.Run("positional separator, expecting value", testExec(t, fp, "", "foo --one --", "--one", "")) t.Run("shot flag set, expecting value", testExec(t, ShortForm(fp, "second-field", "b"), "", "foo --one -b", "--one", "")) }) t.Run("preserve order", func(t *testing.T) { t.Run("bools", testExec(t, fl, "", "foo --one --one false --one", "", "true,false,true;")) t.Run("strings", testExec(t, fl, "", "foo --two 1 --two 2 --two 3", "", ";1,2,3")) }) t.Run("select subcommand", func(t *testing.T) { t.Run("named", testExec(t, Command("", nil, Command("bar", ff), Command("baz", ff)), "", "foo baz", "", "0")) t.Run("default", testExec(t, Command("", nil, Command("bar", ff), Default(Command("baz", ff))), "", "foo", "", "0")) }) }