You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
argh/parser2_test.go

470 lines
11 KiB

package argh_test
import (
"testing"
"git.meatballhat.com/x/box-o-sand/argh"
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/assert"
)
func TestParser2(t *testing.T) {
for _, tc := range []struct {
name string
args []string
cfg *argh.ParserConfig
expErr error
expPT []argh.Node
expAST []argh.Node
skip bool
}{
{
name: "basic",
args: []string{
"pies", "-eat", "--wat", "hello", "mario",
},
cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{
"hello": argh.CommandConfig{
NValue: 1,
ValueNames: []string{"name"},
},
},
},
},
expPT: []argh.Node{
&argh.Command{
Name: "pies",
Values: map[string]string{},
Nodes: []argh.Node{
&argh.ArgDelimiter{},
&argh.CompoundShortFlag{
Nodes: []argh.Node{
&argh.Flag{Name: "e"},
&argh.Flag{Name: "a"},
&argh.Flag{Name: "t"},
},
},
&argh.Flag{Name: "wat"},
&argh.Command{
Name: "hello",
Values: map[string]string{
"name": "mario",
},
Nodes: []argh.Node{
&argh.ArgDelimiter{},
},
},
},
},
},
/*
expAST: []argh.Node{
&argh.Command{
Name: "pies",
Values: map[string]string{},
Nodes: []argh.Node{
&argh.CompoundShortFlag{
Nodes: []argh.Node{
&argh.Flag{Name: "e"},
&argh.Flag{Name: "a"},
&argh.Flag{Name: "t"},
},
},
&argh.Flag{Name: "wat"},
&argh.Command{
Name: "hello",
Values: map[string]string{
"name": "mario",
},
Nodes: []argh.Node{},
},
},
},
},
*/
},
{
name: "bare",
args: []string{"pizzas"},
expPT: []argh.Node{
&argh.Command{
Name: "pizzas",
Values: map[string]string{},
Nodes: []argh.Node{},
},
},
expAST: []argh.Node{
&argh.Command{
Name: "pizzas",
Values: map[string]string{},
Nodes: []argh.Node{},
},
},
},
{
skip: true,
name: "one positional arg",
args: []string{"pizzas", "excel"},
cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{NValue: 1},
},
expPT: []argh.Node{
argh.Command{Name: "pizzas", Values: map[string]string{"0": "excel"}},
},
expAST: []argh.Node{
argh.Command{Name: "pizzas", Values: map[string]string{"0": "excel"}},
},
},
{
skip: true,
name: "many positional args",
args: []string{"pizzas", "excel", "wildly", "when", "feral"},
cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{
NValue: argh.OneOrMoreValue,
ValueNames: []string{"word"},
},
},
expPT: []argh.Node{
argh.Command{
Name: "pizzas",
Values: map[string]string{
"word": "excel",
"word.1": "wildly",
"word.2": "when",
"word.3": "feral",
},
},
},
expAST: []argh.Node{
argh.Command{
Name: "pizzas",
Values: map[string]string{
"word": "excel",
"word.1": "wildly",
"word.2": "when",
"word.3": "feral",
},
},
},
},
{
skip: true,
name: "long value-less flags",
args: []string{"pizzas", "--tasty", "--fresh", "--super-hot-right-now"},
expPT: []argh.Node{
argh.Command{Name: "pizzas"},
argh.ArgDelimiter{},
argh.Flag{Name: "tasty"},
argh.ArgDelimiter{},
argh.Flag{Name: "fresh"},
argh.ArgDelimiter{},
argh.Flag{Name: "super-hot-right-now"},
},
expAST: []argh.Node{
argh.Command{Name: "pizzas"},
argh.Flag{Name: "tasty"},
argh.Flag{Name: "fresh"},
argh.Flag{Name: "super-hot-right-now"},
},
},
{
skip: true,
name: "long flags mixed",
args: []string{
"pizzas",
"--tasty",
"--fresh", "soon",
"--super-hot-right-now",
"--box", "square", "shaped", "hot",
"--please",
},
cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{},
Flags: map[string]argh.FlagConfig{
"fresh": argh.FlagConfig{NValue: 1},
"box": argh.FlagConfig{NValue: argh.OneOrMoreValue},
},
},
},
expPT: []argh.Node{
argh.Command{Name: "pizzas"},
argh.ArgDelimiter{},
argh.Flag{Name: "tasty"},
argh.ArgDelimiter{},
argh.Flag{Name: "fresh", Values: map[string]string{"0": "soon"}},
argh.ArgDelimiter{},
argh.Flag{Name: "super-hot-right-now"},
argh.ArgDelimiter{},
argh.Flag{Name: "box", Values: map[string]string{"0": "square", "1": "shaped", "2": "hot"}},
argh.ArgDelimiter{},
argh.Flag{Name: "please"},
},
expAST: []argh.Node{
argh.Command{Name: "pizzas"},
argh.Flag{Name: "tasty"},
argh.Flag{Name: "fresh", Values: map[string]string{"0": "soon"}},
argh.Flag{Name: "super-hot-right-now"},
argh.Flag{Name: "box", Values: map[string]string{"0": "square", "1": "shaped", "2": "hot"}},
argh.Flag{Name: "please"},
},
},
{
skip: true,
name: "short value-less flags",
args: []string{"pizzas", "-t", "-f", "-s"},
expPT: []argh.Node{
argh.Command{Name: "pizzas"},
argh.ArgDelimiter{},
argh.Flag{Name: "t"},
argh.ArgDelimiter{},
argh.Flag{Name: "f"},
argh.ArgDelimiter{},
argh.Flag{Name: "s"},
},
expAST: []argh.Node{
argh.Command{Name: "pizzas"},
argh.Flag{Name: "t"},
argh.Flag{Name: "f"},
argh.Flag{Name: "s"},
},
},
{
skip: true,
name: "compound short flags",
args: []string{"pizzas", "-aca", "-blol"},
expPT: []argh.Node{
argh.Command{Name: "pizzas"},
argh.ArgDelimiter{},
argh.CompoundShortFlag{
Nodes: []argh.Node{
argh.Flag{Name: "a"},
argh.Flag{Name: "c"},
argh.Flag{Name: "a"},
},
},
argh.ArgDelimiter{},
argh.CompoundShortFlag{
Nodes: []argh.Node{
argh.Flag{Name: "b"},
argh.Flag{Name: "l"},
argh.Flag{Name: "o"},
argh.Flag{Name: "l"},
},
},
},
expAST: []argh.Node{
argh.Command{Name: "pizzas"},
argh.Flag{Name: "a"},
argh.Flag{Name: "c"},
argh.Flag{Name: "a"},
argh.Flag{Name: "b"},
argh.Flag{Name: "l"},
argh.Flag{Name: "o"},
argh.Flag{Name: "l"},
},
},
{
skip: true,
name: "mixed long short value flags",
args: []string{"pizzas", "-a", "--ca", "-b", "1312", "-lol"},
cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{},
Flags: map[string]argh.FlagConfig{
"b": argh.FlagConfig{NValue: 1},
},
},
},
expPT: []argh.Node{
argh.Command{Name: "pizzas"},
argh.ArgDelimiter{},
argh.Flag{Name: "a"},
argh.ArgDelimiter{},
argh.Flag{Name: "ca"},
argh.ArgDelimiter{},
argh.Flag{Name: "b", Values: map[string]string{"0": "1312"}},
argh.ArgDelimiter{},
argh.CompoundShortFlag{
Nodes: []argh.Node{
argh.Flag{Name: "l"},
argh.Flag{Name: "o"},
argh.Flag{Name: "l"},
},
},
},
expAST: []argh.Node{
argh.Command{Name: "pizzas"},
argh.Flag{Name: "a"},
argh.Flag{Name: "ca"},
argh.Flag{Name: "b", Values: map[string]string{"0": "1312"}},
argh.Flag{Name: "l"},
argh.Flag{Name: "o"},
argh.Flag{Name: "l"},
},
},
{
skip: true,
name: "commands",
args: []string{"pizzas", "fly", "fry"},
cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{
"fly": argh.CommandConfig{},
"fry": argh.CommandConfig{},
},
Flags: map[string]argh.FlagConfig{},
},
},
expPT: []argh.Node{
argh.Command{Name: "pizzas"},
argh.ArgDelimiter{},
argh.Command{Name: "fly"},
argh.ArgDelimiter{},
argh.Command{Name: "fry"},
},
},
{
skip: true,
name: "command specific flags",
args: []string{"pizzas", "fly", "--freely", "fry", "--deeply", "-wAt"},
cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{
"fly": argh.CommandConfig{
Flags: map[string]argh.FlagConfig{
"freely": {},
},
},
"fry": argh.CommandConfig{
Flags: map[string]argh.FlagConfig{
"deeply": {},
"w": {},
"A": {},
"t": {},
},
},
},
Flags: map[string]argh.FlagConfig{},
},
},
expPT: []argh.Node{
argh.Command{Name: "pizzas"},
argh.ArgDelimiter{},
argh.Command{Name: "fly"},
argh.ArgDelimiter{},
argh.Flag{Name: "freely"},
argh.ArgDelimiter{},
argh.Command{Name: "fry"},
argh.ArgDelimiter{},
argh.Flag{Name: "deeply"},
argh.ArgDelimiter{},
argh.CompoundShortFlag{
Nodes: []argh.Node{
argh.Flag{Name: "w"},
argh.Flag{Name: "A"},
argh.Flag{Name: "t"},
},
},
},
},
{
skip: true,
name: "total weirdo",
args: []string{"PIZZAs", "^wAT@golf", "^^hecKing", "goose", "bonk", "^^FIERCENESS@-2"},
cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{
"goose": argh.CommandConfig{NValue: 1},
},
Flags: map[string]argh.FlagConfig{
"w": argh.FlagConfig{},
"A": argh.FlagConfig{},
"T": argh.FlagConfig{NValue: 1},
"hecking": argh.FlagConfig{},
"FIERCENESS": argh.FlagConfig{NValue: 1},
},
},
ScannerConfig: &argh.ScannerConfig{
AssignmentOperator: '@',
FlagPrefix: '^',
MultiValueDelim: ',',
},
},
expPT: []argh.Node{
argh.Command{Name: "PIZZAs"},
argh.ArgDelimiter{},
argh.CompoundShortFlag{
Nodes: []argh.Node{
argh.Flag{Name: "w"},
argh.Flag{Name: "A"},
argh.Flag{Name: "T", Values: map[string]string{"0": "golf"}},
},
},
argh.ArgDelimiter{},
argh.Flag{Name: "hecKing"},
argh.ArgDelimiter{},
argh.Command{Name: "goose", Values: map[string]string{"0": "bonk"}},
argh.ArgDelimiter{},
argh.Flag{Name: "FIERCENESS", Values: map[string]string{"0": "-2"}},
},
},
{
skip: true,
name: "invalid bare assignment",
args: []string{"pizzas", "=", "--wat"},
expErr: argh.ErrSyntax,
expPT: []argh.Node{
argh.Command{Name: "pizzas"},
},
},
} {
if tc.skip {
continue
}
if tc.expPT != nil {
t.Run(tc.name+" parse tree", func(ct *testing.T) {
pt, err := argh.ParseArgs2(tc.args, tc.cfg)
if err != nil {
assert.ErrorIs(ct, err, tc.expErr)
return
}
if !assert.Equal(ct, tc.expPT, pt.Nodes) {
spew.Dump(pt)
}
})
}
if tc.expAST != nil {
t.Run(tc.name+" ast", func(ct *testing.T) {
pt, err := argh.ParseArgs2(tc.args, tc.cfg)
if err != nil {
ct.Logf("err=%+#v", err)
return
}
ast := argh.NewQuerier(pt.Nodes).AST()
if !assert.Equal(ct, tc.expAST, ast) {
spew.Dump(ast)
}
})
}
}
}