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

Improve hooks invocation flow #568

Merged
merged 2 commits into from
Jul 13, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
### Added
- Support for reading feature files from an `fs.FS` ([550](https://github.com/cucumber/godog/pull/550) - [tigh-latte](https://github.com/tigh-latte))
- Added keyword functions. ([509](https://github.com/cucumber/godog/pull/509) - [otrava7](https://github.com/otrava7))
- prefer go test to use of godog cli in README ([548](https://github.com/cucumber/godog/pull/548) - [danielhelfand](https://github.com/danielhelfand)
- Prefer go test to use of godog cli in README ([548](https://github.com/cucumber/godog/pull/548) - [danielhelfand](https://github.com/danielhelfand))

### Fixed
- Improve hooks invocation flow ([568](https://github.com/cucumber/godog/pull/568) - [vearutop](https://github.com/vearutop))

## [v0.12.6]
### Changed
Expand Down
109 changes: 80 additions & 29 deletions suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import (
"context"
"errors"
"fmt"
"reflect"
"strings"
Expand All @@ -27,6 +28,9 @@
// step implementation is pending
var ErrPending = fmt.Errorf("step implementation is pending")

// ErrSkip should be returned by step definition or a hook if scenario and further steps are to be skipped.
var ErrSkip = fmt.Errorf("skipped")

// StepResultStatus describes step result.
type StepResultStatus = models.StepResultStatus

Expand Down Expand Up @@ -72,34 +76,53 @@
return def
}

func (s *suite) runStep(ctx context.Context, pickle *Scenario, step *Step, prevStepErr error, isFirst, isLast bool) (rctx context.Context, err error) {
func (s *suite) runStep(ctx context.Context, pickle *Scenario, step *Step, scenarioErr error, isFirst, isLast bool) (rctx context.Context, err error) {
var (
match *models.StepDefinition
sr = models.PickleStepResult{Status: models.Undefined}
sr = models.NewStepResult(pickle.Id, step.Id, match)
)

rctx = ctx
sr.Status = StepUndefined

// user multistep definitions may panic
defer func() {
if e := recover(); e != nil {
err = &traceError{
msg: fmt.Sprintf("%v", e),
stack: callStack(),
if err != nil {
err = &traceError{
msg: fmt.Sprintf("%s: %v", err.Error(), e),
stack: callStack(),
}

Check warning on line 95 in suite.go

View check run for this annotation

Codecov / codecov/patch

suite.go#L92-L95

Added lines #L92 - L95 were not covered by tests
} else {
err = &traceError{
msg: fmt.Sprintf("%v", e),
stack: callStack(),
}
}
}

earlyReturn := prevStepErr != nil || err == ErrUndefined

if !earlyReturn {
sr = models.NewStepResult(pickle.Id, step.Id, match)
earlyReturn := scenarioErr != nil || err == ErrUndefined

switch {
case errors.Is(err, ErrPending):
sr.Status = StepPending
case errors.Is(err, ErrSkip) || (err == nil && scenarioErr != nil):
sr.Status = StepSkipped
case errors.Is(err, ErrUndefined):
sr.Status = StepUndefined
case err != nil:
sr.Status = StepFailed
case err == nil && scenarioErr == nil:
sr.Status = StepPassed
}

// Run after step handlers.
rctx, err = s.runAfterStepHooks(ctx, step, sr.Status, err)

shouldFail := s.shouldFail(err)

// Trigger after scenario on failing or last step to attach possible hook error to step.
if isLast || (sr.Status != StepSkipped && sr.Status != StepUndefined && err != nil) {
if !s.shouldFail(scenarioErr) && (isLast || shouldFail) {
rctx, err = s.runAfterScenarioHooks(rctx, pickle, err)
}

Expand Down Expand Up @@ -137,6 +160,7 @@

match = s.matchStep(step)
s.storage.MustInsertStepDefintionMatch(step.AstNodeIds[0], match)
sr.Def = match
s.fmt.Defined(pickle, step, match.GetInternalStepDefinition())

if err != nil {
Expand All @@ -162,6 +186,7 @@
Nested: match.Nested,
Undefined: undef,
}
sr.Def = match
}

sr = models.NewStepResult(pickle.Id, step.Id, match)
Expand All @@ -172,7 +197,7 @@
return ctx, ErrUndefined
}

if prevStepErr != nil {
if scenarioErr != nil {
sr = models.NewStepResult(pickle.Id, step.Id, match)
sr.Status = models.Skipped
s.storage.MustInsertPickleStepResult(sr)
Expand Down Expand Up @@ -344,18 +369,53 @@

steps, ok := result.(Steps)
if !ok {
return ctx, fmt.Errorf("unexpected error, should have been []string: %T - %+v", result, result)
return ctx, fmt.Errorf("unexpected error, should have been godog.Steps: %T - %+v", result, result)

Check warning on line 372 in suite.go

View check run for this annotation

Codecov / codecov/patch

suite.go#L372

Added line #L372 was not covered by tests
}

var err error

for _, text := range steps {
if def := s.matchStepTextAndType(text, messages.PickleStepType_UNKNOWN); def == nil {
return ctx, ErrUndefined
} else if ctx, err = s.maybeSubSteps(def.Run(ctx)); err != nil {
return ctx, fmt.Errorf("%s: %+v", text, err)
} else {
ctx, err = s.runSubStep(ctx, text, def)
if err != nil {
return ctx, err
}
}
}
return ctx, nil
}

func (s *suite) runSubStep(ctx context.Context, text string, def *models.StepDefinition) (_ context.Context, err error) {
st := &Step{}
st.Text = text
st.Type = messages.PickleStepType_ACTION

defer func() {
status := StepPassed

switch {
case errors.Is(err, ErrUndefined):
status = StepUndefined
case errors.Is(err, ErrPending):
status = StepPending

Check warning on line 402 in suite.go

View check run for this annotation

Codecov / codecov/patch

suite.go#L399-L402

Added lines #L399 - L402 were not covered by tests
case err != nil:
status = StepFailed
}

ctx, err = s.runAfterStepHooks(ctx, st, status, err)
}()

ctx, err = s.runBeforeStepHooks(ctx, st, nil)
if err != nil {
return ctx, fmt.Errorf("%s: %+v", text, err)
}

Check warning on line 413 in suite.go

View check run for this annotation

Codecov / codecov/patch

suite.go#L412-L413

Added lines #L412 - L413 were not covered by tests

if ctx, err = s.maybeSubSteps(def.Run(ctx)); err != nil {
return ctx, fmt.Errorf("%s: %+v", text, err)
}

return ctx, nil
}

Expand Down Expand Up @@ -405,32 +465,23 @@

func (s *suite) runSteps(ctx context.Context, pickle *Scenario, steps []*Step) (context.Context, error) {
var (
stepErr, err error
stepErr, scenarioErr error
)

for i, step := range steps {
isLast := i == len(steps)-1
isFirst := i == 0
ctx, stepErr = s.runStep(ctx, pickle, step, err, isFirst, isLast)
switch stepErr {
case ErrUndefined:
// do not overwrite failed error
if err == ErrUndefined || err == nil {
err = stepErr
}
case ErrPending:
err = stepErr
case nil:
default:
err = stepErr
ctx, stepErr = s.runStep(ctx, pickle, step, scenarioErr, isFirst, isLast)
if scenarioErr == nil || s.shouldFail(stepErr) {
scenarioErr = stepErr
}
}

return ctx, err
return ctx, scenarioErr
}

func (s *suite) shouldFail(err error) bool {
if err == nil {
if err == nil || err == ErrSkip {
return false
}

Expand Down
Loading
Loading