From ac5a1cf76fdfac14e07ac4e401b3652310483252 Mon Sep 17 00:00:00 2001 From: Horacio Duran Date: Sat, 18 Nov 2023 23:57:25 +0100 Subject: [PATCH 1/5] Draft attempt at extra args --- mage/main.go | 22 +++++++++++++++++++++- mage/template.go | 19 ++++++++++++++++++- mg/extra_args.go | 3 +++ parse/parse.go | 5 +++++ 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 mg/extra_args.go diff --git a/mage/main.go b/mage/main.go index 0062bd35..2fd6fa7c 100644 --- a/mage/main.go +++ b/mage/main.go @@ -119,6 +119,10 @@ type Invocation struct { GoCmd string // the go binary command to run CacheDir string // the directory where we should store compiled binaries HashFast bool // don't rely on GOCACHE, just hash the magefiles + + // all the arguments that came after --, we do not do any kind of parsing on them as + // they are intended for a subprocess and we know nothing about them + ExtraArgs []string } // MagefilesDirName is the name of the default folder to look for if no directory was specified, @@ -296,7 +300,23 @@ Options: return inv, cmd, errors.New("-goos and -goarch only apply when running with -compile") } - inv.Args = fs.Args() + var extraArgs []string + extraArgsFound := false + for _, arg := range fs.Args() { + // we do not really care about args before this point, this is where extra args begin + if arg == "--" { + extraArgsFound = true + continue + } + // all args before -- are the positional args that go to mage + if !extraArgsFound { + inv.Args = append(inv.Args, arg) + continue + } + // now we parse all that comes after -- + extraArgs = append(extraArgs, arg) + } + inv.ExtraArgs = extraArgs if inv.Help && len(inv.Args) > 1 { return inv, cmd, errors.New("-h can only show help for a single target") } diff --git a/mage/template.go b/mage/template.go index e6dc1306..fa7becba 100644 --- a/mage/template.go +++ b/mage/template.go @@ -35,6 +35,7 @@ func main() { Help bool // print out help for a specific target Timeout time.Duration // set a timeout to running the targets Args []string // args contain the non-flag command-line arguments + ExtraArgs []string // extraArgs contain the command-line arguments after the "--" separator } parseBool := func(env string) bool { @@ -90,7 +91,23 @@ Options: // flag will have printed out an error already. return } - args.Args = fs.Args() + var extraArgs []string + extraArgsFound := false + for _, arg := range fs.Args() { + // we do not really care about args before this point, this is where extra args begin + if arg == "--" { + extraArgsFound = true + continue + } + // all args before -- are the positional args that go to mage + if !extraArgsFound { + args.Args = append(args.Args, arg) + continue + } + // now we parse all that comes after -- + extraArgs = append(extraArgs, arg) + } + args.ExtraArgs = extraArgs if args.Help && len(args.Args) == 0 { fs.Usage() return diff --git a/mg/extra_args.go b/mg/extra_args.go new file mode 100644 index 00000000..a5729d19 --- /dev/null +++ b/mg/extra_args.go @@ -0,0 +1,3 @@ +package mg + +type ExtraArgs []string diff --git a/parse/parse.go b/parse/parse.go index 34216690..9b53600e 100644 --- a/parse/parse.go +++ b/parse/parse.go @@ -116,6 +116,10 @@ func (f Function) ExecCode() string { var parseargs string for x, arg := range f.Args { switch arg.Type { + case "mg.ExtraArgs": + parseargs += fmt.Sprintf(` + arg%d := args.ExtraArgs + x++`, x) case "string": parseargs += fmt.Sprintf(` arg%d := args.Args[x] @@ -847,4 +851,5 @@ var argTypes = map[string]string{ "int": "int", "&{time Duration}": "time.Duration", "bool": "bool", + "mg.ExtraArgs": "mg.ExtraArgs", } From 810ac6242e3019d5b9600948efcacef811724ced Mon Sep 17 00:00:00 2001 From: Horacio Duran Date: Sun, 19 Nov 2023 08:31:57 +0100 Subject: [PATCH 2/5] Correct extra args accounting and test Now extra args are treated as separate but merged to call the function. Extra testing is needing to try them in conjunction with other tests. Every function receives the same extra args --- mage/main.go | 9 +++++- mage/main_test.go | 20 ++++++++++++ mage/template.go | 4 +-- mage/testdata/extra_args/extra.go | 14 +++++++++ parse/parse.go | 51 +++++++++++++++++++++++-------- 5 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 mage/testdata/extra_args/extra.go diff --git a/mage/main.go b/mage/main.go index 2fd6fa7c..cd80c1a9 100644 --- a/mage/main.go +++ b/mage/main.go @@ -724,7 +724,14 @@ func generateInit(dir string) error { // RunCompiled runs an already-compiled mage command with the given args, func RunCompiled(inv Invocation, exePath string, errlog *log.Logger) int { debug.Println("running binary", exePath) - c := exec.Command(exePath, inv.Args...) + args := make([]string, 0, len(inv.Args)+len(inv.ExtraArgs)+1) + copy(args, inv.Args) + if len(inv.ExtraArgs) > 0 { + + args = append(append(inv.Args, "--"), inv.ExtraArgs...) + } + + c := exec.Command(exePath, args...) c.Stderr = inv.Stderr c.Stdout = inv.Stdout c.Stdin = inv.Stdin diff --git a/mage/main_test.go b/mage/main_test.go index 07e598e5..8f0c7cd1 100644 --- a/mage/main_test.go +++ b/mage/main_test.go @@ -1877,6 +1877,26 @@ func TestWrongDependency(t *testing.T) { } } +func TestExtraArgs(t *testing.T) { + stdout := &bytes.Buffer{} + inv := Invocation{ + Dir: "./testdata/extra_args", + Stderr: os.Stderr, + Stdout: stdout, + Args: []string{"targetOne"}, + ExtraArgs: []string{"--", "-baz", "foo", "bar"}, + } + + code := Invoke(inv) + if code != 0 { + t.Fatalf("expected 0, but got %v", code) + } + expected := "mg.ExtraArgs{\"-baz\", \"foo\", \"bar\"}\n" + if stdout.String() != expected { + t.Fatalf("expected %q, but got %q", expected, stdout.String()) + } +} + // / This code liberally borrowed from https://github.com/rsc/goversion/blob/master/version/exe.go type ( diff --git a/mage/template.go b/mage/template.go index fa7becba..b10cc5c7 100644 --- a/mage/template.go +++ b/mage/template.go @@ -462,7 +462,7 @@ Options: switch _strings.ToLower(target) { {{range .Funcs }} case "{{lower .TargetName}}": - expected := x + {{len .Args}} + expected := x + {{len .Args}} - {{.ExtraArgsPresent}} if expected > len(args.Args) { // note that expected and args at this point include the arg for the target itself // so we subtract 1 here to show the number of args without the target. @@ -479,7 +479,7 @@ Options: {{$imp := .}} {{range .Info.Funcs }} case "{{lower .TargetName}}": - expected := x + {{len .Args}} + expected := x + {{len .Args}} - {{.ExtraArgsPresent}} if expected > len(args.Args) { // note that expected and args at this point include the arg for the target itself // so we subtract 1 here to show the number of args without the target. diff --git a/mage/testdata/extra_args/extra.go b/mage/testdata/extra_args/extra.go new file mode 100644 index 00000000..a035d398 --- /dev/null +++ b/mage/testdata/extra_args/extra.go @@ -0,0 +1,14 @@ +//go:build mage +// +build mage + +package main + +import ( + "fmt" + + "github.com/magefile/mage/mg" +) + +func TargetOne(extra mg.ExtraArgs) { + fmt.Printf("%#v\n", extra) +} diff --git a/parse/parse.go b/parse/parse.go index 9b53600e..8638c1aa 100644 --- a/parse/parse.go +++ b/parse/parse.go @@ -40,16 +40,17 @@ type PkgInfo struct { // Function represents a job function from a mage file type Function struct { - PkgAlias string - Package string - ImportPath string - Name string - Receiver string - IsError bool - IsContext bool - Synopsis string - Comment string - Args []Arg + PkgAlias string + Package string + ImportPath string + Name string + Receiver string + IsError bool + IsContext bool + Synopsis string + Comment string + Args []Arg + ExtraArgsPresent int } var _ sort.Interface = (Functions)(nil) @@ -759,8 +760,27 @@ func getPackage(path string, files []string, fset *token.FileSet) (*ast.Package, } } -// hasContextParams returns whether or not the first parameter is a context.Context. If it -// determines that hte first parameter makes this function invalid for mage, it'll return a non-nil +func isExtraArgsField(field *ast.Field) bool { + sel, ok := field.Type.(*ast.SelectorExpr) + if !ok { + return false + } + pkg, ok := sel.X.(*ast.Ident) + if !ok { + return false + } + + if pkg.Name != "mg" { + return false + } + if sel.Sel.Name == "ExtraArgs" { + return true + } + return false +} + +// hasContextParams returns whether the first parameter is a context.Context. If it +// determines that the first parameter makes this function invalid for mage, it'll return a non-nil // error. func hasContextParam(ft *ast.FuncType) (bool, error) { if ft.Params.NumFields() < 1 { @@ -775,12 +795,14 @@ func hasContextParam(ft *ast.FuncType) (bool, error) { if !ok { return false, nil } + if pkg.Name != "context" { return false, nil } if sel.Sel.Name != "Context" { return false, nil } + if len(param.Names) > 1 { // something like foo, bar context.Context return false, errors.New("ETOOMANYCONTEXTS") @@ -836,6 +858,9 @@ func funcType(ft *ast.FuncType) (*Function, error) { } // support for foo, bar string for _, name := range param.Names { + if isExtraArgsField(param) { + f.ExtraArgsPresent++ + } f.Args = append(f.Args, Arg{Name: name.Name, Type: typ}) } } @@ -851,5 +876,5 @@ var argTypes = map[string]string{ "int": "int", "&{time Duration}": "time.Duration", "bool": "bool", - "mg.ExtraArgs": "mg.ExtraArgs", + "&{mg ExtraArgs}": "mg.ExtraArgs", } From 7934dd6e946c1126fac3241ff837eacc3aabc18a Mon Sep 17 00:00:00 2001 From: Horacio Duran Date: Sun, 19 Nov 2023 08:37:32 +0100 Subject: [PATCH 3/5] Cleanup args for running compiled mage --- mage/main.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mage/main.go b/mage/main.go index cd80c1a9..6f731733 100644 --- a/mage/main.go +++ b/mage/main.go @@ -724,11 +724,10 @@ func generateInit(dir string) error { // RunCompiled runs an already-compiled mage command with the given args, func RunCompiled(inv Invocation, exePath string, errlog *log.Logger) int { debug.Println("running binary", exePath) - args := make([]string, 0, len(inv.Args)+len(inv.ExtraArgs)+1) + args := make([]string, len(inv.Args), len(inv.Args)+len(inv.ExtraArgs)+1) copy(args, inv.Args) if len(inv.ExtraArgs) > 0 { - - args = append(append(inv.Args, "--"), inv.ExtraArgs...) + args = append(append(args, "--"), inv.ExtraArgs...) } c := exec.Command(exePath, args...) From 594383f6c98ad3f9502b212aacd33d40947b1ef6 Mon Sep 17 00:00:00 2001 From: Horacio Duran Date: Sun, 19 Nov 2023 16:30:19 +0100 Subject: [PATCH 4/5] Add more test cases and fix bug Remove increase in x index count when iterating args sin mage file. --- mage/main_test.go | 102 +++++++++++++++++++++++++++++- mage/testdata/extra_args/extra.go | 35 ++++++++++ parse/parse.go | 3 +- 3 files changed, 137 insertions(+), 3 deletions(-) diff --git a/mage/main_test.go b/mage/main_test.go index 8f0c7cd1..d8c6a0d0 100644 --- a/mage/main_test.go +++ b/mage/main_test.go @@ -1881,7 +1881,7 @@ func TestExtraArgs(t *testing.T) { stdout := &bytes.Buffer{} inv := Invocation{ Dir: "./testdata/extra_args", - Stderr: os.Stderr, + Stderr: ioutil.Discard, Stdout: stdout, Args: []string{"targetOne"}, ExtraArgs: []string{"--", "-baz", "foo", "bar"}, @@ -1897,6 +1897,106 @@ func TestExtraArgs(t *testing.T) { } } +func TestExtraArgsWithContext(t *testing.T) { + stdout := &bytes.Buffer{} + inv := Invocation{ + Dir: "./testdata/extra_args", + Stderr: ioutil.Discard, + Stdout: stdout, + Args: []string{"targetTwo"}, + ExtraArgs: []string{"--", "-baz", "foo", "bar"}, + } + + code := Invoke(inv) + if code != 0 { + t.Fatalf("expected 0, but got %v", code) + } + expected := "Context is nil: false\nmg.ExtraArgs{\"-baz\", \"foo\", \"bar\"}\n" + if stdout.String() != expected { + t.Fatalf("expected %q, but got %q", expected, stdout.String()) + } +} + +func TestExtraArgsWithContextAndString(t *testing.T) { + stdout := &bytes.Buffer{} + inv := Invocation{ + Dir: "./testdata/extra_args", + Stderr: ioutil.Discard, + Stdout: stdout, + Args: []string{"targetThree", "stringvalue"}, + ExtraArgs: []string{"--", "-baz", "foo", "bar"}, + } + + code := Invoke(inv) + if code != 0 { + t.Fatalf("expected 0, but got %v", code) + } + expected := "Context is nil: false\nmg.ExtraArgs{\"-baz\", \"foo\", \"bar\"}\nstringvalue\n" + if stdout.String() != expected { + t.Fatalf("expected %q, but got %q", expected, stdout.String()) + } +} + +func TestExtraArgsWithContextAndStringAndInt(t *testing.T) { + stdout := &bytes.Buffer{} + inv := Invocation{ + Dir: "./testdata/extra_args", + Stderr: ioutil.Discard, + Stdout: stdout, + Args: []string{"targetFour", "stringvalue", "2"}, + ExtraArgs: []string{"--", "-baz", "foo", "bar"}, + } + + code := Invoke(inv) + if code != 0 { + t.Fatalf("expected 0, but got %v", code) + } + expected := "Context is nil: false\nmg.ExtraArgs{\"-baz\", \"foo\", \"bar\"}\nstringvalue\n2\n" + if stdout.String() != expected { + t.Fatalf("expected %q, but got %q", expected, stdout.String()) + } +} + +func TestExtraArgsWithoutContextAndStringAndInt(t *testing.T) { + stdout := &bytes.Buffer{} + inv := Invocation{ + Dir: "./testdata/extra_args", + Stderr: ioutil.Discard, + Stdout: stdout, + Args: []string{"targetFive", "stringvalue", "2"}, + ExtraArgs: []string{"--", "-baz", "foo", "bar"}, + } + + code := Invoke(inv) + if code != 0 { + t.Fatalf("expected 0, but got %v", code) + } + expected := "mg.ExtraArgs{\"-baz\", \"foo\", \"bar\"}\nstringvalue\n2\n" + if stdout.String() != expected { + t.Fatalf("expected %q, but got %q", expected, stdout.String()) + } +} + +func TestExtraArgsWithoutContextAndStringAndIntNonOrdered(t *testing.T) { + stdout := &bytes.Buffer{} + inv := Invocation{ + Dir: "./testdata/extra_args", + Stderr: ioutil.Discard, + Stdout: stdout, + Args: []string{"targetSix", "stringvalue", "2"}, + ExtraArgs: []string{"--", "-baz", "foo", "bar"}, + } + + code := Invoke(inv) + if code != 0 { + t.Fatalf("expected 0, but got %v", code) + } + expected := "mg.ExtraArgs{\"-baz\", \"foo\", \"bar\"}\nstringvalue\n2\n" + if stdout.String() != expected { + t.Fatalf("expected %q, but got %q", expected, stdout.String()) + } +} + // / This code liberally borrowed from https://github.com/rsc/goversion/blob/master/version/exe.go type ( diff --git a/mage/testdata/extra_args/extra.go b/mage/testdata/extra_args/extra.go index a035d398..84910bbe 100644 --- a/mage/testdata/extra_args/extra.go +++ b/mage/testdata/extra_args/extra.go @@ -4,6 +4,7 @@ package main import ( + "context" "fmt" "github.com/magefile/mage/mg" @@ -12,3 +13,37 @@ import ( func TargetOne(extra mg.ExtraArgs) { fmt.Printf("%#v\n", extra) } + +func TargetTwo(ctx context.Context, extra mg.ExtraArgs) { + fmt.Printf("Context is nil: %t\n", ctx == nil) + fmt.Printf("%#v\n", extra) +} + +func TargetThree(ctx context.Context, extra mg.ExtraArgs, aString string) error { + fmt.Printf("Context is nil: %t\n", ctx == nil) + fmt.Printf("%#v\n", extra) + fmt.Printf("%s\n", aString) + return nil +} + +func TargetFour(ctx context.Context, extra mg.ExtraArgs, aString string, anInt int) error { + fmt.Printf("Context is nil: %t\n", ctx == nil) + fmt.Printf("%#v\n", extra) + fmt.Printf("%s\n", aString) + fmt.Printf("%d\n", anInt) + return nil +} + +func TargetFive(extra mg.ExtraArgs, aString string, anInt int) error { + fmt.Printf("%#v\n", extra) + fmt.Printf("%s\n", aString) + fmt.Printf("%d\n", anInt) + return nil +} + +func TargetSix(aString string, anInt int, extra mg.ExtraArgs) error { + fmt.Printf("%#v\n", extra) + fmt.Printf("%s\n", aString) + fmt.Printf("%d\n", anInt) + return nil +} diff --git a/parse/parse.go b/parse/parse.go index 8638c1aa..49cc0d1b 100644 --- a/parse/parse.go +++ b/parse/parse.go @@ -119,8 +119,7 @@ func (f Function) ExecCode() string { switch arg.Type { case "mg.ExtraArgs": parseargs += fmt.Sprintf(` - arg%d := args.ExtraArgs - x++`, x) + arg%d := args.ExtraArgs`, x) // do not advance x index, this is not part of .Args case "string": parseargs += fmt.Sprintf(` arg%d := args.Args[x] From 5602e124377229c84f7eb775b1bff2b072b7b5c2 Mon Sep 17 00:00:00 2001 From: Horacio Duran Date: Wed, 20 Dec 2023 23:18:57 +0100 Subject: [PATCH 5/5] Added the same test cases for ParseAndRun --- mage/main_test.go | 142 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/mage/main_test.go b/mage/main_test.go index d8c6a0d0..68049f24 100644 --- a/mage/main_test.go +++ b/mage/main_test.go @@ -1897,6 +1897,28 @@ func TestExtraArgs(t *testing.T) { } } +func TestExtraArgsParsing(t *testing.T) { + os.Setenv("MAGEFILE_VERBOSE", "1") + defer os.Unsetenv("MAGEFILE_VERBOSE") + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + code := ParseAndRun(stderr, stdout, nil, + []string{"-d", "./testdata/extra_args", "targetOne", "--", "-baz", "foo", "bar"}) + if code != 0 { + t.Fatal("unexpected code", code) + } + + targetOneExpectedOut := "Running target: TargetOne\n" + if stdout.String() != targetOneExpectedOut { + t.Fatalf("expected output %q, but got %q", targetOneExpectedOut, stdout.String()) + } + targetOneExpectedErr := `mg.ExtraArgs{"-baz", "foo", "bar"} +` + if stderr.String() != targetOneExpectedErr { + t.Fatalf("expected stderr output %q, but got %q", targetOneExpectedErr, stderr.String()) + } +} + func TestExtraArgsWithContext(t *testing.T) { stdout := &bytes.Buffer{} inv := Invocation{ @@ -1917,6 +1939,29 @@ func TestExtraArgsWithContext(t *testing.T) { } } +func TestExtraArgsWithContextParsing(t *testing.T) { + os.Setenv("MAGEFILE_VERBOSE", "1") + defer os.Unsetenv("MAGEFILE_VERBOSE") + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + code := ParseAndRun(stderr, stdout, nil, + []string{"-d", "./testdata/extra_args", "targetTwo", "--", "-baz", "foo", "bar"}) + if code != 0 { + t.Fatal("unexpected code", code) + } + + targetOneExpectedOut := "Running target: TargetTwo\n" + if stdout.String() != targetOneExpectedOut { + t.Fatalf("expected output %q, but got %q", targetOneExpectedOut, stdout.String()) + } + targetOneExpectedErr := `Context is nil: false +mg.ExtraArgs{"-baz", "foo", "bar"} +` + if stderr.String() != targetOneExpectedErr { + t.Fatalf("expected stderr output %q, but got %q", targetOneExpectedErr, stderr.String()) + } +} + func TestExtraArgsWithContextAndString(t *testing.T) { stdout := &bytes.Buffer{} inv := Invocation{ @@ -1937,6 +1982,30 @@ func TestExtraArgsWithContextAndString(t *testing.T) { } } +func TestExtraArgsWithContextAndStringParsing(t *testing.T) { + os.Setenv("MAGEFILE_VERBOSE", "1") + defer os.Unsetenv("MAGEFILE_VERBOSE") + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + code := ParseAndRun(stderr, stdout, nil, + []string{"-d", "./testdata/extra_args", "targetThree", "stringvalue", "--", "-baz", "foo", "bar"}) + if code != 0 { + t.Fatal("unexpected code", code) + } + + targetOneExpectedOut := "Running target: TargetThree\n" + if stdout.String() != targetOneExpectedOut { + t.Fatalf("expected output %q, but got %q", targetOneExpectedOut, stdout.String()) + } + targetOneExpectedErr := `Context is nil: false +mg.ExtraArgs{"-baz", "foo", "bar"} +stringvalue +` + if stderr.String() != targetOneExpectedErr { + t.Fatalf("expected stderr output %q, but got %q", targetOneExpectedErr, stderr.String()) + } +} + func TestExtraArgsWithContextAndStringAndInt(t *testing.T) { stdout := &bytes.Buffer{} inv := Invocation{ @@ -1957,6 +2026,31 @@ func TestExtraArgsWithContextAndStringAndInt(t *testing.T) { } } +func TestExtraArgsWithContextAndStringAndIntParsing(t *testing.T) { + os.Setenv("MAGEFILE_VERBOSE", "1") + defer os.Unsetenv("MAGEFILE_VERBOSE") + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + code := ParseAndRun(stderr, stdout, nil, + []string{"-d", "./testdata/extra_args", "targetFour", "stringvalue", "2", "--", "-baz", "foo", "bar"}) + if code != 0 { + t.Fatal("unexpected code", code) + } + + targetOneExpectedOut := "Running target: TargetFour\n" + if stdout.String() != targetOneExpectedOut { + t.Fatalf("expected output %q, but got %q", targetOneExpectedOut, stdout.String()) + } + targetOneExpectedErr := `Context is nil: false +mg.ExtraArgs{"-baz", "foo", "bar"} +stringvalue +2 +` + if stderr.String() != targetOneExpectedErr { + t.Fatalf("expected stderr output %q, but got %q", targetOneExpectedErr, stderr.String()) + } +} + func TestExtraArgsWithoutContextAndStringAndInt(t *testing.T) { stdout := &bytes.Buffer{} inv := Invocation{ @@ -1977,6 +2071,30 @@ func TestExtraArgsWithoutContextAndStringAndInt(t *testing.T) { } } +func TestExtraArgsWithoutContextAndStringAndIntParsing(t *testing.T) { + os.Setenv("MAGEFILE_VERBOSE", "1") + defer os.Unsetenv("MAGEFILE_VERBOSE") + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + code := ParseAndRun(stderr, stdout, nil, + []string{"-d", "./testdata/extra_args", "targetFive", "stringvalue", "2", "--", "-baz", "foo", "bar"}) + if code != 0 { + t.Fatal("unexpected code", code) + } + + targetOneExpectedOut := "Running target: TargetFive\n" + if stdout.String() != targetOneExpectedOut { + t.Fatalf("expected output %q, but got %q", targetOneExpectedOut, stdout.String()) + } + targetOneExpectedErr := `mg.ExtraArgs{"-baz", "foo", "bar"} +stringvalue +2 +` + if stderr.String() != targetOneExpectedErr { + t.Fatalf("expected stderr output %q, but got %q", targetOneExpectedErr, stderr.String()) + } +} + func TestExtraArgsWithoutContextAndStringAndIntNonOrdered(t *testing.T) { stdout := &bytes.Buffer{} inv := Invocation{ @@ -1997,6 +2115,30 @@ func TestExtraArgsWithoutContextAndStringAndIntNonOrdered(t *testing.T) { } } +func TestExtraArgsWithoutContextAndStringAndIntNonOrderedParsing(t *testing.T) { + os.Setenv("MAGEFILE_VERBOSE", "1") + defer os.Unsetenv("MAGEFILE_VERBOSE") + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + code := ParseAndRun(stderr, stdout, nil, + []string{"-d", "./testdata/extra_args", "targetSix", "stringvalue", "2", "--", "-baz", "foo", "bar"}) + if code != 0 { + t.Fatal("unexpected code", code) + } + + targetOneExpectedOut := "Running target: TargetSix\n" + if stdout.String() != targetOneExpectedOut { + t.Fatalf("expected output %q, but got %q", targetOneExpectedOut, stdout.String()) + } + targetOneExpectedErr := `mg.ExtraArgs{"-baz", "foo", "bar"} +stringvalue +2 +` + if stderr.String() != targetOneExpectedErr { + t.Fatalf("expected stderr output %q, but got %q", targetOneExpectedErr, stderr.String()) + } +} + // / This code liberally borrowed from https://github.com/rsc/goversion/blob/master/version/exe.go type (