diff --git a/process.go b/process.go index 8d410a7d..80b5ce4c 100644 --- a/process.go +++ b/process.go @@ -1,6 +1,7 @@ package venom import ( + "context" "fmt" "io/ioutil" "os" @@ -255,7 +256,7 @@ func runTestStep(e *executorWrap, tc *TestCase, step TestStep, l *log.Entry, det time.Sleep(time.Duration(e.delay) * time.Second) } - result, err := e.executor.Run(l, aliases, step) + result, err := runTestStepExecutor(e, step, l) if err != nil { tc.Failures = append(tc.Failures, Failure{Value: err.Error()}) continue @@ -275,7 +276,29 @@ func runTestStep(e *executorWrap, tc *TestCase, step TestStep, l *log.Entry, det tc.Errors = append(tc.Errors, errors...) tc.Failures = append(tc.Failures, failures...) if retry > 0 && (len(failures) > 0 || len(errors) > 0) { - tc.Failures = append(tc.Failures, Failure{Value: fmt.Sprintf("It's a failure after %d attempt(s)", retry+1)}) + tc.Failures = append(tc.Failures, Failure{Value: fmt.Sprintf("It's a failure after %d attempt(s)", retry)}) + } +} + +func runTestStepExecutor(e *executorWrap, step TestStep, l *log.Entry) (ExecutorResult, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(e.timeout)*time.Second) + defer cancel() + + ch := make(chan ExecutorResult) + cherr := make(chan error) + go func(e *executorWrap, step TestStep, l *log.Entry) { + result, err := e.executor.Run(l, aliases, step) + cherr <- err + ch <- result + }(e, step, l) + + select { + case err := <-cherr: + return nil, err + case result := <-ch: + return result, nil + case <-ctx.Done(): + return nil, fmt.Errorf("Timeout after %d second(s)", e.timeout) } } diff --git a/types.go b/types.go index 0ca6ca8b..96835027 100644 --- a/types.go +++ b/types.go @@ -37,6 +37,7 @@ type executorWrap struct { executor Executor retry int // nb retry a test case if it is in failure. delay int // delay between two retries + timeout int // timeout on executor } // executorWithDefaultAssertions execute a testStep. diff --git a/venom.go b/venom.go index 44d74a21..158c99e2 100644 --- a/venom.go +++ b/venom.go @@ -18,7 +18,7 @@ func RegisterExecutor(name string, e Executor) { func getExecutorWrap(t map[string]interface{}) (*executorWrap, error) { var name string - var retry, delay int + var retry, delay, timeout int if itype, ok := t["type"]; ok { name = fmt.Sprintf("%s", itype) @@ -36,12 +36,20 @@ func getExecutorWrap(t map[string]interface{}) (*executorWrap, error) { if errDelay != nil { return nil, errDelay } + timeout, errTimeout := getAttrInt(t, "timeout") + if errTimeout != nil { + return nil, errTimeout + } + if timeout <= 0 { + timeout = 60 // 60 seconds default + } if e, ok := executors[name]; ok { ew := &executorWrap{ executor: e, retry: retry, delay: delay, + timeout: timeout, } return ew, nil } @@ -51,7 +59,7 @@ func getExecutorWrap(t map[string]interface{}) (*executorWrap, error) { func getAttrInt(t map[string]interface{}, name string) (int, error) { var out int - if i, ok := t["retry"]; ok { + if i, ok := t[name]; ok { var ok bool out, ok = i.(int) if !ok {