2018-01-11 22:19:25 +00:00
|
|
|
package mcfg
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2019-04-04 16:20:40 +00:00
|
|
|
"fmt"
|
2019-04-04 18:21:44 +00:00
|
|
|
"regexp"
|
2018-08-13 18:17:53 +00:00
|
|
|
"strings"
|
2018-01-11 22:19:25 +00:00
|
|
|
. "testing"
|
2018-08-13 18:17:53 +00:00
|
|
|
"time"
|
2018-01-11 22:19:25 +00:00
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
"github.com/mediocregopher/mediocre-go-lib/mcmp"
|
2018-07-03 00:20:00 +00:00
|
|
|
"github.com/mediocregopher/mediocre-go-lib/mrand"
|
2019-04-03 03:21:16 +00:00
|
|
|
"github.com/mediocregopher/mediocre-go-lib/mtest/massert"
|
2018-08-13 19:03:30 +00:00
|
|
|
"github.com/mediocregopher/mediocre-go-lib/mtest/mchk"
|
2018-01-11 22:19:25 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestSourceCLIHelp(t *T) {
|
2019-06-15 22:45:53 +00:00
|
|
|
assertHelp := func(cmp *mcmp.Component, subCmdPrefix []string, exp string) {
|
2019-04-04 18:21:44 +00:00
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
src := &SourceCLI{}
|
2019-06-15 22:45:53 +00:00
|
|
|
pM, err := src.cliParams(CollectParams(cmp))
|
2019-04-04 18:21:44 +00:00
|
|
|
require.NoError(t, err)
|
2019-06-15 22:45:53 +00:00
|
|
|
src.printHelp(cmp, buf, subCmdPrefix, pM)
|
2019-04-04 18:21:44 +00:00
|
|
|
|
|
|
|
out := buf.String()
|
|
|
|
ok := regexp.MustCompile(exp).MatchString(out)
|
|
|
|
assert.True(t, ok, "exp:%s (%q)\ngot:%s (%q)", exp, exp, out, out)
|
|
|
|
}
|
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
cmp := new(mcmp.Component)
|
|
|
|
assertHelp(cmp, nil, `^Usage: \S+
|
2019-04-04 18:21:44 +00:00
|
|
|
|
|
|
|
$`)
|
2019-06-15 22:45:53 +00:00
|
|
|
assertHelp(cmp, []string{"foo", "bar"}, `^Usage: \S+ foo bar
|
2019-04-04 18:21:44 +00:00
|
|
|
|
|
|
|
$`)
|
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
Int(cmp, "foo", ParamDefault(5), ParamUsage("Test int param ")) // trailing space should be trimmed
|
|
|
|
Bool(cmp, "bar", ParamUsage("Test bool param."))
|
|
|
|
String(cmp, "baz", ParamDefault("baz"), ParamUsage("Test string param"))
|
|
|
|
String(cmp, "baz2", ParamUsage("Required string param"), ParamRequired())
|
|
|
|
String(cmp, "baz3", ParamRequired())
|
2018-01-11 22:19:25 +00:00
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
assertHelp(cmp, nil, `^Usage: \S+ \[options\]
|
2019-04-04 18:21:44 +00:00
|
|
|
|
|
|
|
Options:
|
|
|
|
|
|
|
|
--baz2 \(Required\)
|
2019-06-15 22:45:53 +00:00
|
|
|
Required string param.
|
2019-04-04 18:21:44 +00:00
|
|
|
|
|
|
|
--baz3 \(Required\)
|
|
|
|
|
|
|
|
--bar \(Flag\)
|
|
|
|
Test bool param.
|
|
|
|
|
|
|
|
--baz \(Default: "baz"\)
|
|
|
|
Test string param.
|
|
|
|
|
|
|
|
--foo \(Default: 5\)
|
|
|
|
Test int param.
|
|
|
|
|
|
|
|
$`)
|
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
assertHelp(cmp, []string{"foo", "bar"}, `^Usage: \S+ foo bar \[options\]
|
2019-04-04 18:21:44 +00:00
|
|
|
|
|
|
|
Options:
|
|
|
|
|
|
|
|
--baz2 \(Required\)
|
2019-06-15 22:45:53 +00:00
|
|
|
Required string param.
|
2019-04-04 18:21:44 +00:00
|
|
|
|
|
|
|
--baz3 \(Required\)
|
|
|
|
|
|
|
|
--bar \(Flag\)
|
|
|
|
Test bool param.
|
|
|
|
|
|
|
|
--baz \(Default: "baz"\)
|
|
|
|
Test string param.
|
|
|
|
|
|
|
|
--foo \(Default: 5\)
|
|
|
|
Test int param.
|
|
|
|
|
|
|
|
$`)
|
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
CLISubCommand(cmp, "first", "First sub-command", nil)
|
|
|
|
CLISubCommand(cmp, "second", "Second sub-command", nil)
|
|
|
|
assertHelp(cmp, []string{"foo", "bar"}, `^Usage: \S+ foo bar <sub-command> \[options\]
|
2019-04-04 18:21:44 +00:00
|
|
|
|
|
|
|
Sub-commands:
|
|
|
|
|
|
|
|
first First sub-command
|
|
|
|
second Second sub-command
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
|
|
|
--baz2 \(Required\)
|
2019-06-15 22:45:53 +00:00
|
|
|
Required string param.
|
2019-04-04 18:21:44 +00:00
|
|
|
|
|
|
|
--baz3 \(Required\)
|
2018-01-11 22:19:25 +00:00
|
|
|
|
2019-04-04 18:21:44 +00:00
|
|
|
--bar \(Flag\)
|
|
|
|
Test bool param.
|
2019-01-25 03:02:04 +00:00
|
|
|
|
2019-04-04 18:21:44 +00:00
|
|
|
--baz \(Default: "baz"\)
|
|
|
|
Test string param.
|
2019-01-25 03:02:04 +00:00
|
|
|
|
2019-04-04 18:21:44 +00:00
|
|
|
--foo \(Default: 5\)
|
|
|
|
Test int param.
|
2018-01-11 22:19:25 +00:00
|
|
|
|
2019-04-06 19:07:37 +00:00
|
|
|
$`)
|
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
CLITail(cmp, "[arg...]")
|
|
|
|
assertHelp(cmp, nil, `^Usage: \S+ <sub-command> \[options\] \[arg\.\.\.\]
|
2019-04-06 19:07:37 +00:00
|
|
|
|
|
|
|
Sub-commands:
|
|
|
|
|
|
|
|
first First sub-command
|
|
|
|
second Second sub-command
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
|
|
|
--baz2 \(Required\)
|
2019-06-15 22:45:53 +00:00
|
|
|
Required string param.
|
2019-04-06 19:07:37 +00:00
|
|
|
|
|
|
|
--baz3 \(Required\)
|
|
|
|
|
|
|
|
--bar \(Flag\)
|
|
|
|
Test bool param.
|
|
|
|
|
|
|
|
--baz \(Default: "baz"\)
|
|
|
|
Test string param.
|
|
|
|
|
|
|
|
--foo \(Default: 5\)
|
|
|
|
Test int param.
|
|
|
|
|
2019-04-04 18:21:44 +00:00
|
|
|
$`)
|
2018-01-11 22:19:25 +00:00
|
|
|
}
|
2018-08-13 18:17:53 +00:00
|
|
|
|
2018-08-13 19:03:30 +00:00
|
|
|
func TestSourceCLI(t *T) {
|
|
|
|
type state struct {
|
2018-08-13 23:40:41 +00:00
|
|
|
srcCommonState
|
2019-04-04 14:57:37 +00:00
|
|
|
*SourceCLI
|
2018-08-13 18:17:53 +00:00
|
|
|
}
|
|
|
|
|
2018-08-13 19:03:30 +00:00
|
|
|
type params struct {
|
2018-08-13 23:40:41 +00:00
|
|
|
srcCommonParams
|
|
|
|
nonBoolWEq bool // use equal sign when setting value
|
2018-08-13 18:17:53 +00:00
|
|
|
}
|
|
|
|
|
2018-08-13 19:03:30 +00:00
|
|
|
chk := mchk.Checker{
|
|
|
|
Init: func() mchk.State {
|
|
|
|
var s state
|
2018-08-13 23:40:41 +00:00
|
|
|
s.srcCommonState = newSrcCommonState()
|
2019-04-04 14:57:37 +00:00
|
|
|
s.SourceCLI = &SourceCLI{
|
|
|
|
Args: make([]string, 0, 16),
|
|
|
|
}
|
2018-08-13 18:17:53 +00:00
|
|
|
return s
|
|
|
|
},
|
2018-08-13 19:03:30 +00:00
|
|
|
Next: func(ss mchk.State) mchk.Action {
|
|
|
|
s := ss.(state)
|
|
|
|
var p params
|
2018-08-13 23:40:41 +00:00
|
|
|
p.srcCommonParams = s.srcCommonState.next()
|
|
|
|
// if the param is a bool or unset this won't get used, but w/e
|
2018-08-13 19:03:30 +00:00
|
|
|
p.nonBoolWEq = mrand.Intn(2) == 0
|
|
|
|
return mchk.Action{Params: p}
|
|
|
|
},
|
|
|
|
Apply: func(ss mchk.State, a mchk.Action) (mchk.State, error) {
|
|
|
|
s := ss.(state)
|
|
|
|
p := a.Params.(params)
|
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
s.srcCommonState = s.srcCommonState.applyCmpAndPV(p.srcCommonParams)
|
2018-08-13 19:03:30 +00:00
|
|
|
if !p.unset {
|
|
|
|
arg := cliKeyPrefix
|
2019-06-15 22:45:53 +00:00
|
|
|
if path := p.cmp.Path(); len(path) > 0 {
|
|
|
|
arg += strings.Join(path, cliKeyJoin) + cliKeyJoin
|
2018-08-13 19:03:30 +00:00
|
|
|
}
|
|
|
|
arg += p.name
|
|
|
|
if !p.isBool {
|
|
|
|
if p.nonBoolWEq {
|
|
|
|
arg += "="
|
|
|
|
} else {
|
|
|
|
s.SourceCLI.Args = append(s.SourceCLI.Args, arg)
|
|
|
|
arg = ""
|
|
|
|
}
|
|
|
|
arg += p.nonBoolVal
|
|
|
|
}
|
|
|
|
s.SourceCLI.Args = append(s.SourceCLI.Args, arg)
|
|
|
|
}
|
|
|
|
|
2018-08-13 23:40:41 +00:00
|
|
|
err := s.srcCommonState.assert(s.SourceCLI)
|
|
|
|
return s, err
|
2018-08-13 18:17:53 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-08-13 23:40:41 +00:00
|
|
|
if err := chk.RunFor(2 * time.Second); err != nil {
|
2018-08-13 18:17:53 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
2019-04-03 03:21:16 +00:00
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
func TestCLITail(t *T) {
|
|
|
|
cmp := new(mcmp.Component)
|
|
|
|
Int(cmp, "foo", ParamDefault(5))
|
|
|
|
Bool(cmp, "bar")
|
2019-04-03 03:21:16 +00:00
|
|
|
|
|
|
|
type testCase struct {
|
|
|
|
args []string
|
|
|
|
expTail []string
|
|
|
|
}
|
|
|
|
|
|
|
|
cases := []testCase{
|
|
|
|
{
|
|
|
|
args: []string{"--foo", "5"},
|
2019-04-04 16:20:40 +00:00
|
|
|
expTail: nil,
|
2019-04-03 03:21:16 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
args: []string{"--foo", "5", "a", "b", "c"},
|
|
|
|
expTail: []string{"a", "b", "c"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
args: []string{"--foo=5", "a", "b", "c"},
|
|
|
|
expTail: []string{"a", "b", "c"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
args: []string{"--foo", "5", "--bar"},
|
2019-04-04 16:20:40 +00:00
|
|
|
expTail: nil,
|
2019-04-03 03:21:16 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
args: []string{"--foo", "5", "--bar", "a", "b", "c"},
|
|
|
|
expTail: []string{"a", "b", "c"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
2019-06-15 22:45:53 +00:00
|
|
|
tail := CLITail(cmp, "foo")
|
|
|
|
err := Populate(cmp, &SourceCLI{Args: tc.args})
|
2019-04-03 03:21:16 +00:00
|
|
|
massert.Require(t, massert.Comment(massert.All(
|
|
|
|
massert.Nil(err),
|
2019-04-04 16:20:40 +00:00
|
|
|
massert.Equal(tc.expTail, *tail),
|
2019-04-03 03:21:16 +00:00
|
|
|
), "tc: %#v", tc))
|
|
|
|
}
|
|
|
|
}
|
2019-04-04 16:20:40 +00:00
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
func ExampleCLITail() {
|
|
|
|
cmp := new(mcmp.Component)
|
|
|
|
foo := Int(cmp, "foo", ParamDefault(1), ParamUsage("Description of foo."))
|
|
|
|
tail := CLITail(cmp, "[arg...]")
|
|
|
|
bar := String(cmp, "bar", ParamDefault("defaultVal"), ParamUsage("Description of bar."))
|
2019-04-04 16:20:40 +00:00
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
err := Populate(cmp, &SourceCLI{
|
2019-04-06 19:07:37 +00:00
|
|
|
Args: []string{"--foo=100", "arg1", "arg2", "arg3"},
|
2019-04-04 16:20:40 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
fmt.Printf("err:%v foo:%v bar:%v tail:%#v\n", err, *foo, *bar, *tail)
|
2019-04-06 19:07:37 +00:00
|
|
|
// Output: err:<nil> foo:100 bar:defaultVal tail:[]string{"arg1", "arg2", "arg3"}
|
2019-04-04 16:20:40 +00:00
|
|
|
}
|
2019-04-04 18:21:44 +00:00
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
func TestCLISubCommand(t *T) {
|
2019-04-04 18:21:44 +00:00
|
|
|
var (
|
2019-06-15 22:45:53 +00:00
|
|
|
cmp *mcmp.Component
|
2019-04-06 18:20:01 +00:00
|
|
|
foo *int
|
|
|
|
bar *int
|
|
|
|
baz *int
|
|
|
|
aFlag *bool
|
|
|
|
bFlag *bool
|
2019-04-04 18:21:44 +00:00
|
|
|
)
|
|
|
|
reset := func() {
|
2019-04-06 18:20:01 +00:00
|
|
|
foo, bar, baz, aFlag, bFlag = nil, nil, nil, nil, nil
|
2019-06-15 22:45:53 +00:00
|
|
|
cmp = new(mcmp.Component)
|
|
|
|
foo = Int(cmp, "foo")
|
|
|
|
aFlag = CLISubCommand(cmp, "a", "Description of a.",
|
|
|
|
func(cmp *mcmp.Component) {
|
|
|
|
bar = Int(cmp, "bar")
|
2019-04-04 18:21:44 +00:00
|
|
|
})
|
2019-06-15 22:45:53 +00:00
|
|
|
bFlag = CLISubCommand(cmp, "b", "Description of b.",
|
|
|
|
func(cmp *mcmp.Component) {
|
|
|
|
baz = Int(cmp, "baz")
|
2019-04-04 18:21:44 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
reset()
|
2019-06-15 22:45:53 +00:00
|
|
|
err := Populate(cmp, &SourceCLI{
|
2019-04-04 18:21:44 +00:00
|
|
|
Args: []string{"a", "--foo=1", "--bar=2"},
|
|
|
|
})
|
|
|
|
massert.Require(t,
|
|
|
|
massert.Comment(massert.Nil(err), "%v", err),
|
|
|
|
massert.Equal(1, *foo),
|
|
|
|
massert.Equal(2, *bar),
|
|
|
|
massert.Nil(baz),
|
|
|
|
massert.Equal(true, *aFlag),
|
2019-04-06 18:20:01 +00:00
|
|
|
massert.Equal(false, *bFlag),
|
2019-04-04 18:21:44 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
reset()
|
2019-06-15 22:45:53 +00:00
|
|
|
err = Populate(cmp, &SourceCLI{
|
2019-04-06 18:20:01 +00:00
|
|
|
Args: []string{"b", "--foo=1", "--baz=3"},
|
2019-04-04 18:21:44 +00:00
|
|
|
})
|
|
|
|
massert.Require(t,
|
|
|
|
massert.Comment(massert.Nil(err), "%v", err),
|
|
|
|
massert.Equal(1, *foo),
|
|
|
|
massert.Nil(bar),
|
|
|
|
massert.Equal(3, *baz),
|
|
|
|
massert.Equal(false, *aFlag),
|
2019-04-06 18:20:01 +00:00
|
|
|
massert.Equal(true, *bFlag),
|
2019-04-04 18:21:44 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
func ExampleCLISubCommand() {
|
|
|
|
var (
|
|
|
|
cmp *mcmp.Component
|
|
|
|
foo, bar, baz *int
|
|
|
|
aFlag, bFlag *bool
|
|
|
|
)
|
|
|
|
|
|
|
|
// resetExample re-initializes all variables used in this example. We'll
|
|
|
|
// call it multiple times to show different behaviors depending on what
|
|
|
|
// arguments are passed in.
|
|
|
|
resetExample := func() {
|
|
|
|
// Create a new Component with a parameter "foo", which can be used across
|
|
|
|
// all sub-commands.
|
|
|
|
cmp = new(mcmp.Component)
|
|
|
|
foo = Int(cmp, "foo")
|
|
|
|
|
|
|
|
// Create a sub-command "a", which has a parameter "bar" specific to it.
|
|
|
|
aFlag = CLISubCommand(cmp, "a", "Description of a.",
|
|
|
|
func(cmp *mcmp.Component) {
|
|
|
|
bar = Int(cmp, "bar")
|
|
|
|
})
|
|
|
|
|
|
|
|
// Create a sub-command "b", which has a parameter "baz" specific to it.
|
|
|
|
bFlag = CLISubCommand(cmp, "b", "Description of b.",
|
|
|
|
func(cmp *mcmp.Component) {
|
|
|
|
baz = Int(cmp, "baz")
|
|
|
|
})
|
|
|
|
}
|
2019-04-04 18:21:44 +00:00
|
|
|
|
2019-05-18 18:50:04 +00:00
|
|
|
// Use Populate with manually generated CLI arguments, calling the "a"
|
|
|
|
// sub-command.
|
2019-06-15 22:45:53 +00:00
|
|
|
resetExample()
|
2019-04-04 18:21:44 +00:00
|
|
|
args := []string{"a", "--foo=1", "--bar=2"}
|
2019-06-15 22:45:53 +00:00
|
|
|
if err := Populate(cmp, &SourceCLI{Args: args}); err != nil {
|
2019-04-04 18:21:44 +00:00
|
|
|
panic(err)
|
|
|
|
}
|
2019-04-06 18:20:01 +00:00
|
|
|
fmt.Printf("foo:%d bar:%d aFlag:%v bFlag:%v\n", *foo, *bar, *aFlag, *bFlag)
|
2019-04-04 18:21:44 +00:00
|
|
|
|
2019-06-15 22:45:53 +00:00
|
|
|
// reset for another Populate, this time calling the "b" sub-command.
|
|
|
|
resetExample()
|
2019-04-06 18:20:01 +00:00
|
|
|
args = []string{"b", "--foo=1", "--baz=3"}
|
2019-06-15 22:45:53 +00:00
|
|
|
if err := Populate(cmp, &SourceCLI{Args: args}); err != nil {
|
2019-04-04 18:21:44 +00:00
|
|
|
panic(err)
|
|
|
|
}
|
2019-04-06 18:20:01 +00:00
|
|
|
fmt.Printf("foo:%d baz:%d aFlag:%v bFlag:%v\n", *foo, *baz, *aFlag, *bFlag)
|
2019-04-04 18:21:44 +00:00
|
|
|
|
2019-04-06 18:20:01 +00:00
|
|
|
// Output: foo:1 bar:2 aFlag:true bFlag:false
|
|
|
|
// foo:1 baz:3 aFlag:false bFlag:true
|
2019-04-04 18:21:44 +00:00
|
|
|
}
|