diff --git a/flags/flags.go b/flags/flags.go index 8a1cec1a..9464bba2 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -102,6 +102,7 @@ func (f *Flags) ToOpts() ([]runn.Option, error) { runn.Scopes(f.Scopes...), runn.HostRules(f.HostRules...), runn.RunLabel(f.RunLabels...), + runn.FailFast(f.FailFast), runn.Attach(f.Attach), } diff --git a/operator.go b/operator.go index 854a1848..333437eb 100644 --- a/operator.go +++ b/operator.go @@ -65,7 +65,6 @@ type operator struct { force bool trace bool // Enable tracing ( e.g. add trace header to HTTP request ) waitTimeout time.Duration - failFast bool included bool ifCond string skipTest bool @@ -477,7 +476,6 @@ func New(opts ...Option) (*operator, error) { force: bk.force, trace: bk.trace, waitTimeout: bk.waitTimeout, - failFast: bk.failFast, included: bk.included, ifCond: bk.ifCond, skipTest: bk.skipTest, @@ -1153,6 +1151,17 @@ func (o *operator) runInternal(ctx context.Context) (rerr error) { } }() + // context done + select { + case <-ctx.Done(): + if err := o.skip(); err != nil { + rerr = err + return + } + return nil + default: + } + // if if o.ifCond != "" { tf, err := o.expandCondBeforeRecord(o.ifCond) @@ -1350,6 +1359,7 @@ type operators struct { random int waitTimeout time.Duration // waitTimout is the time to wait for sub-processes to complete after the Run or RunN context is canceled. concmax int + failFast bool opts []Option results []*runNResult runCount int64 @@ -1386,6 +1396,7 @@ func Load(pathp string, opts ...Option) (*operators, error) { sample: bk.runSample, random: bk.runRandom, waitTimeout: bk.waitTimeout, + failFast: bk.failFast, concmax: 1, opts: opts, kv: newKV(), @@ -1688,11 +1699,6 @@ func (ops *operators) runN(ctx context.Context) (*runNResult, error) { for _, o := range selected { o := o cg.GoMulti(o.concurrency, func() error { - select { - case <-cctx.Done(): - return errors.New("context canceled") - default: - } defer func() { r := o.Result() o.capturers.captureResult(o.trails(), r) @@ -1704,7 +1710,7 @@ func (ops *operators) runN(ctx context.Context) (*runNResult, error) { }() o.capturers.captureStart(o.trails(), o.bookPath, o.desc) if err := o.run(cctx); err != nil { - if o.failFast { + if ops.failFast { return err } } diff --git a/operator_test.go b/operator_test.go index 7e8e41fe..d9016aaf 100644 --- a/operator_test.go +++ b/operator_test.go @@ -434,6 +434,20 @@ func TestRunN(t *testing.T) { Err: ErrDummy, StepResults: []*StepResult{{ID: "b6d90c331b04ab198ca95b13c5f656fd2522e53b?step=0", Key: "0", Err: ErrDummy}}, }, + { + ID: "faeec884c284f9c2527f840372fc01ed8351a377", + Path: "testdata/book/runn_2_success.yml", + Err: nil, + Skipped: true, + StepResults: []*StepResult{{ID: "faeec884c284f9c2527f840372fc01ed8351a377?step=0", Key: "0", Err: nil, Skipped: true}}, + }, + { + ID: "15519f515b984b9b25dae1cfde43597cd035dc3d", + Path: "testdata/book/runn_3.skip.yml", + Err: nil, + Skipped: true, + StepResults: []*StepResult{{ID: "15519f515b984b9b25dae1cfde43597cd035dc3d?step=0", Key: "0", Err: nil, Skipped: true}}, + }, })}, {"testdata/book/runn_*", "runn_0", false, newRunNResult(t, 1, []*RunResult{ { @@ -734,6 +748,46 @@ func TestSkipTest(t *testing.T) { } } +func TestFailFast(t *testing.T) { + tests := []struct { + failFast bool + wantSuccess int + wantFailure int + wantSkipped int + }{ + {false, 2, 1, 1}, + {true, 1, 1, 2}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%v", tt.failFast), func(t *testing.T) { + ops, err := Load("testdata/book/runn_*.yml", FailFast(tt.failFast)) + if err != nil { + t.Fatal(err) + } + _ = ops.RunN(context.Background()) + + { + got := int(ops.Result().simplify().Success) + if got != tt.wantSuccess { + t.Errorf("got %v\nwant %v", got, tt.wantSuccess) + } + } + { + got := int(ops.Result().simplify().Failure) + if got != tt.wantFailure { + t.Errorf("got %v\nwant %v", got, tt.wantFailure) + } + } + { + got := int(ops.Result().simplify().Skipped) + if got != tt.wantSkipped { + t.Errorf("got %v\nwant %v", got, tt.wantSkipped) + } + } + }) + } +} + func TestHookFuncTest(t *testing.T) { count := 0 tests := []struct {