Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect deps that have already been invoked #346

Merged
merged 5 commits into from Jan 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions mg/deps.go
Expand Up @@ -134,6 +134,13 @@ func checkFns(fns []interface{}) []Fn {
funcs[i] = fn
continue
}

// Check if the target provided is a not function so we can give a clear warning
t := reflect.TypeOf(f)
if t == nil || t.Kind() != reflect.Func {
panic(fmt.Errorf("non-function used as a target dependency: %T. The mg.Deps, mg.SerialDeps and mg.CtxDeps functions accept function names, such as mg.Deps(TargetA, TargetB)", f))
}

funcs[i] = F(f)
}
return funcs
Expand Down
21 changes: 21 additions & 0 deletions mg/deps_internal_test.go
Expand Up @@ -2,6 +2,7 @@ package mg

import (
"bytes"
"fmt"
"log"
"os"
"strings"
Expand Down Expand Up @@ -33,3 +34,23 @@ func bar() {
}

func baz() {}

func TestDepWasNotInvoked(t *testing.T) {
fn1 := func() error {
return nil
}
defer func() {
err := recover()
if err == nil {
t.Fatal("expected panic, but didn't get one")
}
gotErr := fmt.Sprint(err)
wantErr := "non-function used as a target dependency: <nil>. The mg.Deps, mg.SerialDeps and mg.CtxDeps functions accept function names, such as mg.Deps(TargetA, TargetB)"
if !strings.Contains(gotErr, wantErr) {
t.Fatalf(`expected to get "%s" but got "%s"`, wantErr, gotErr)
}
}()
func(fns ...interface{}) {
checkFns(fns)
}(fn1())
}
4 changes: 2 additions & 2 deletions mg/fn.go
Expand Up @@ -100,8 +100,8 @@ func (f fn) Run(ctx context.Context) error {

func checkF(target interface{}, args []interface{}) (hasContext, isNamespace bool, _ error) {
t := reflect.TypeOf(target)
if t.Kind() != reflect.Func {
return false, false, fmt.Errorf("non-function passed to mg.F: %T", target)
if t == nil || t.Kind() != reflect.Func {
return false, false, fmt.Errorf("non-function passed to mg.F: %T. The mg.F function accepts function names, such as mg.F(TargetA, \"arg1\", \"arg2\")", target)
}

if t.NumOut() > 1 {
Expand Down
10 changes: 10 additions & 0 deletions mg/fn_test.go
Expand Up @@ -114,6 +114,16 @@ func TestFuncCheck(t *testing.T) {
if err == nil {
t.Error("expected func(*int) error to be invalid")
}

defer func() {
if r := recover(); r !=nil {
t.Error("expected a nil function argument to be handled gracefully")
}
}()
_, _, err = checkF(nil, []interface{}{1,2})
if err == nil {
t.Error("expected a nil function argument to be invalid")
}
}

func TestF(t *testing.T) {
Expand Down