From d507e024bd6f112ccebc449b35b449b284f6c5fc Mon Sep 17 00:00:00 2001 From: jguer Date: Mon, 6 Jul 2020 01:16:51 +0200 Subject: [PATCH] fix(parser): add support for multiple overlapping global options --- pkg/settings/parser.go | 51 ++++----- pkg/settings/parser_test.go | 214 ++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+), 30 deletions(-) create mode 100644 pkg/settings/parser_test.go diff --git a/pkg/settings/parser.go b/pkg/settings/parser.go index 1c64fba7..9b27b939 100644 --- a/pkg/settings/parser.go +++ b/pkg/settings/parser.go @@ -52,13 +52,13 @@ func MakeArguments() *Arguments { } } -func (parser *Arguments) CopyGlobal() (cp *Arguments) { - cp = MakeArguments() +func (parser *Arguments) CopyGlobal() *Arguments { + cp := MakeArguments() for k, v := range parser.Globals { cp.Globals[k] = v } - return + return cp } func (parser *Arguments) Copy() (cp *Arguments) { @@ -143,51 +143,46 @@ func (parser *Arguments) NeedRoot(runtime *Runtime) bool { } } -func (parser *Arguments) addOP(op string) (err error) { +func (parser *Arguments) addOP(op string) error { if parser.Op != "" { - err = errors.New(gotext.Get("only one operation may be used at a time")) - return + return errors.New(gotext.Get("only one operation may be used at a time")) } parser.Op = op - return + return nil } -func (parser *Arguments) addParam(option, arg string) (err error) { +func (parser *Arguments) addParam(option, arg string) error { if !isArg(option) { return errors.New(gotext.Get("invalid option '%s'", option)) } if isOp(option) { - err = parser.addOP(option) - return + return parser.addOP(option) } - switch { - case isGlobal(option): + if isGlobal(option) { if parser.Globals[option] == nil { parser.Globals[option] = &Option{} } parser.Globals[option].Add(arg) - default: + } else { if parser.Options[option] == nil { parser.Options[option] = &Option{} } parser.Options[option].Add(arg) } - - return + return nil } -func (parser *Arguments) AddArg(options ...string) (err error) { +func (parser *Arguments) AddArg(options ...string) error { for _, option := range options { - err = parser.addParam(option, "") + err := parser.addParam(option, "") if err != nil { - return + return err } } - - return + return nil } // Multiple args acts as an OR operator @@ -210,14 +205,12 @@ func (parser *Arguments) GetArg(options ...string) (arg string, double, exists b for _, option := range options { value, exists := parser.Options[option] if exists { - arg = value.First() - return arg, len(value.Args) >= 2, len(value.Args) >= 1 + return value.First(), len(value.Args) >= 2, len(value.Args) >= 1 } value, exists = parser.Globals[option] if exists { - arg = value.First() - return arg, len(value.Args) >= 2, len(value.Args) >= 1 + return value.First(), len(value.Args) >= 2, len(value.Args) >= 1 } } @@ -279,14 +272,12 @@ func (parser *Arguments) FormatArgs() (args []string) { func (parser *Arguments) FormatGlobals() (args []string) { for option, arg := range parser.Globals { formattedOption := formatArg(option) - args = append(args, formattedOption) - if hasParam(option) { - args = append(args, arg.First()) - } - - if parser.ExistsDouble(option) { + for _, value := range arg.Args { args = append(args, formattedOption) + if hasParam(option) { + args = append(args, value) + } } } diff --git a/pkg/settings/parser_test.go b/pkg/settings/parser_test.go new file mode 100644 index 00000000..a627b609 --- /dev/null +++ b/pkg/settings/parser_test.go @@ -0,0 +1,214 @@ +package settings + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestOption_Add(t *testing.T) { + type fields struct { + Args []string + } + type args struct { + arg string + } + tests := []struct { + name string + fields fields + args args + want []string + }{ + {name: "simple add", fields: fields{ + Args: []string{"a", "b"}, + }, args: args{ + arg: "c", + }, want: []string{"a", "b", "c"}}, + {name: "null add", fields: fields{ + Args: nil, + }, args: args{ + arg: "c", + }, want: []string{"c"}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &Option{ + Args: tt.fields.Args, + } + o.Add(tt.args.arg) + assert.EqualValues(t, tt.want, o.Args) + }) + } +} + +func TestOption_Set(t *testing.T) { + type fields struct { + Args []string + } + type args struct { + arg string + } + tests := []struct { + name string + fields fields + args args + want []string + }{ + {name: "simple set", fields: fields{ + Args: []string{"a", "b"}, + }, args: args{ + arg: "c", + }, want: []string{"c"}}, + {name: "null set", fields: fields{ + Args: nil, + }, args: args{ + arg: "c", + }, want: []string{"c"}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &Option{ + Args: tt.fields.Args, + } + o.Set(tt.args.arg) + assert.EqualValues(t, tt.want, o.Args) + }) + } +} + +func TestOption_First(t *testing.T) { + type fields struct { + Args []string + } + tests := []struct { + name string + fields fields + want string + }{ + {name: "simple first", fields: fields{ + Args: []string{"a", "b"}, + }, want: "a"}, + {name: "null first", fields: fields{ + Args: nil, + }, want: ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &Option{ + Args: tt.fields.Args, + } + assert.Equal(t, tt.want, o.First()) + }) + } +} + +func TestMakeArguments(t *testing.T) { + args := MakeArguments() + assert.NotNil(t, args) + assert.Equal(t, "", args.Op) + assert.Empty(t, args.Globals) + assert.Empty(t, args.Options) + assert.Empty(t, args.Targets) +} + +func TestArguments_CopyGlobal(t *testing.T) { + type fields struct { + Op string + Options map[string]*Option + Globals map[string]*Option + Targets []string + } + tests := []struct { + name string + fields fields + want *Arguments + }{ + {name: "simple", fields: fields{ + Op: "Q", + Options: map[string]*Option{"a": {}}, + Globals: map[string]*Option{"arch": { + Args: []string{"x86_x64"}, + }, "boo": {Args: []string{"a", "b"}}, + }, + Targets: []string{"a", "b"}, + }, want: &Arguments{ + Op: "", + Options: map[string]*Option{}, + Globals: map[string]*Option{"arch": { + Args: []string{"x86_x64"}, + }, "boo": {Args: []string{"a", "b"}}, + }, + Targets: []string{}, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parser := &Arguments{ + Op: tt.fields.Op, + Options: tt.fields.Options, + Globals: tt.fields.Globals, + Targets: tt.fields.Targets, + } + got := parser.CopyGlobal() + assert.NotEqualValues(t, tt.fields.Options, got.Options) + assert.NotEqualValues(t, tt.fields.Targets, got.Targets) + assert.NotEqual(t, tt.fields.Op, got.Op) + assert.EqualValues(t, tt.fields.Globals, got.Globals) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestArguments_Copy(t *testing.T) { + type fields struct { + Op string + Options map[string]*Option + Globals map[string]*Option + Targets []string + } + tests := []struct { + name string + fields fields + want *Arguments + }{ + {name: "simple", fields: fields{ + Op: "Q", + Options: map[string]*Option{"a": {}}, + Globals: map[string]*Option{"arch": { + Args: []string{"x86_x64"}, + }, "boo": {Args: []string{"a", "b"}}, + }, + Targets: []string{"a", "b"}, + }, want: &Arguments{ + Op: "Q", + Options: map[string]*Option{"a": {}}, + Globals: map[string]*Option{"arch": { + Args: []string{"x86_x64"}, + }, "boo": {Args: []string{"a", "b"}}, + }, + Targets: []string{"a", "b"}, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parser := &Arguments{ + Op: tt.fields.Op, + Options: tt.fields.Options, + Globals: tt.fields.Globals, + Targets: tt.fields.Targets, + } + got := parser.Copy() + assert.Equal(t, parser, got) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestArguments_DelArg(t *testing.T) { + args := MakeArguments() + args.addParam("arch", "arg") + args.addParam("ask", "arg") + args.DelArg("arch", "ask") + assert.Empty(t, args.Options) + assert.Empty(t, args.Globals) +}