diff --git a/fmt.go b/fmt.go index 367de8b..93be2d9 100644 --- a/fmt.go +++ b/fmt.go @@ -9,6 +9,7 @@ import ( "regexp" "strconv" "strings" + "sync" "text/template" "time" "unicode" @@ -221,6 +222,16 @@ func (f stepResult) scenarioLine() string { return f.line() // was not expecting different owner } +func newBaseFmt(suite string, out io.Writer) *basefmt { + return &basefmt{ + suiteName: suite, + started: timeNowFunc(), + indent: 2, + out: out, + lock: new(sync.Mutex), + } +} + type basefmt struct { suiteName string @@ -235,9 +246,14 @@ type basefmt struct { skipped []*stepResult undefined []*stepResult pending []*stepResult + + lock *sync.Mutex } func (f *basefmt) Node(n interface{}) { + f.lock.Lock() + defer f.lock.Unlock() + switch t := n.(type) { case *gherkin.Scenario: f.owner = t @@ -265,14 +281,21 @@ func (f *basefmt) Node(n interface{}) { } func (f *basefmt) Defined(*gherkin.Step, *StepDef) { - + f.lock.Lock() + defer f.lock.Unlock() } func (f *basefmt) Feature(ft *gherkin.Feature, p string, c []byte) { + f.lock.Lock() + defer f.lock.Unlock() + f.features = append(f.features, &feature{Path: p, Feature: ft, time: timeNowFunc()}) } func (f *basefmt) Passed(step *gherkin.Step, match *StepDef) { + f.lock.Lock() + defer f.lock.Unlock() + s := &stepResult{ owner: f.owner, feature: f.features[len(f.features)-1], @@ -287,6 +310,9 @@ func (f *basefmt) Passed(step *gherkin.Step, match *StepDef) { } func (f *basefmt) Skipped(step *gherkin.Step, match *StepDef) { + f.lock.Lock() + defer f.lock.Unlock() + s := &stepResult{ owner: f.owner, feature: f.features[len(f.features)-1], @@ -301,6 +327,9 @@ func (f *basefmt) Skipped(step *gherkin.Step, match *StepDef) { } func (f *basefmt) Undefined(step *gherkin.Step, match *StepDef) { + f.lock.Lock() + defer f.lock.Unlock() + s := &stepResult{ owner: f.owner, feature: f.features[len(f.features)-1], @@ -315,6 +344,9 @@ func (f *basefmt) Undefined(step *gherkin.Step, match *StepDef) { } func (f *basefmt) Failed(step *gherkin.Step, match *StepDef, err error) { + f.lock.Lock() + defer f.lock.Unlock() + s := &stepResult{ owner: f.owner, feature: f.features[len(f.features)-1], @@ -330,6 +362,9 @@ func (f *basefmt) Failed(step *gherkin.Step, match *StepDef, err error) { } func (f *basefmt) Pending(step *gherkin.Step, match *StepDef) { + f.lock.Lock() + defer f.lock.Unlock() + s := &stepResult{ owner: f.owner, feature: f.features[len(f.features)-1], @@ -438,6 +473,35 @@ func (f *basefmt) Summary() { } } +func (f *basefmt) Sync(cf ConcurrentFormatter) { + if source, ok := cf.(*basefmt); ok { + f.lock = source.lock + } +} + +func (f *basefmt) Copy(cf ConcurrentFormatter) { + if source, ok := cf.(*basefmt); ok { + for _, v := range source.features { + f.features = append(f.features, v) + } + for _, v := range source.failed { + f.failed = append(f.failed, v) + } + for _, v := range source.passed { + f.passed = append(f.passed, v) + } + for _, v := range source.skipped { + f.skipped = append(f.skipped, v) + } + for _, v := range source.undefined { + f.undefined = append(f.undefined, v) + } + for _, v := range source.pending { + f.pending = append(f.pending, v) + } + } +} + func (s *undefinedSnippet) Args() (ret string) { var ( args []string diff --git a/fmt_cucumber.go b/fmt_cucumber.go index 86c43fa..5f46376 100644 --- a/fmt_cucumber.go +++ b/fmt_cucumber.go @@ -27,15 +27,7 @@ func init() { } func cucumberFunc(suite string, out io.Writer) Formatter { - formatter := &cukefmt{ - basefmt: basefmt{ - started: timeNowFunc(), - indent: 2, - out: out, - }, - } - - return formatter + return &cukefmt{basefmt: newBaseFmt(suite, out)} } // Replace spaces with - This function is used to create the "id" fields of the cucumber output. @@ -108,7 +100,7 @@ type cukeFeatureJSON struct { } type cukefmt struct { - basefmt + *basefmt // currently running feature path, to be part of id. // this is sadly not passed by gherkin nodes. diff --git a/fmt_events.go b/fmt_events.go index 1bd4744..19ffa92 100644 --- a/fmt_events.go +++ b/fmt_events.go @@ -16,13 +16,7 @@ func init() { } func eventsFunc(suite string, out io.Writer) Formatter { - formatter := &events{ - basefmt: basefmt{ - started: timeNowFunc(), - indent: 2, - out: out, - }, - } + formatter := &events{basefmt: newBaseFmt(suite, out)} formatter.event(&struct { Event string `json:"event"` @@ -40,7 +34,7 @@ func eventsFunc(suite string, out io.Writer) Formatter { } type events struct { - basefmt + *basefmt // currently running feature path, to be part of id. // this is sadly not passed by gherkin nodes. diff --git a/fmt_junit.go b/fmt_junit.go index 22f6772..b5b827a 100644 --- a/fmt_junit.go +++ b/fmt_junit.go @@ -7,10 +7,7 @@ import ( "os" "sort" "strconv" - "sync" "time" - - "github.com/cucumber/godog/gherkin" ) func init() { @@ -18,32 +15,11 @@ func init() { } func junitFunc(suite string, out io.Writer) Formatter { - return &junitFormatter{ - basefmt: basefmt{ - suiteName: suite, - started: timeNowFunc(), - indent: 2, - out: out, - }, - lock: new(sync.Mutex), - } + return &junitFormatter{basefmt: newBaseFmt(suite, out)} } type junitFormatter struct { - basefmt - lock *sync.Mutex -} - -func (f *junitFormatter) Node(n interface{}) { - f.lock.Lock() - defer f.lock.Unlock() - f.basefmt.Node(n) -} - -func (f *junitFormatter) Feature(ft *gherkin.Feature, p string, c []byte) { - f.lock.Lock() - defer f.lock.Unlock() - f.basefmt.Feature(ft, p, c) + *basefmt } func (f *junitFormatter) Summary() { @@ -61,62 +37,15 @@ func (f *junitFormatter) Summary() { } } -func (f *junitFormatter) Passed(step *gherkin.Step, match *StepDef) { - f.lock.Lock() - defer f.lock.Unlock() - f.basefmt.Passed(step, match) -} - -func (f *junitFormatter) Skipped(step *gherkin.Step, match *StepDef) { - f.lock.Lock() - defer f.lock.Unlock() - f.basefmt.Skipped(step, match) -} - -func (f *junitFormatter) Undefined(step *gherkin.Step, match *StepDef) { - f.lock.Lock() - defer f.lock.Unlock() - f.basefmt.Undefined(step, match) -} - -func (f *junitFormatter) Failed(step *gherkin.Step, match *StepDef, err error) { - f.lock.Lock() - defer f.lock.Unlock() - f.basefmt.Failed(step, match, err) -} - -func (f *junitFormatter) Pending(step *gherkin.Step, match *StepDef) { - f.lock.Lock() - defer f.lock.Unlock() - f.basefmt.Pending(step, match) -} - func (f *junitFormatter) Sync(cf ConcurrentFormatter) { if source, ok := cf.(*junitFormatter); ok { - f.lock = source.lock + f.basefmt.Sync(source.basefmt) } } func (f *junitFormatter) Copy(cf ConcurrentFormatter) { if source, ok := cf.(*junitFormatter); ok { - for _, v := range source.features { - f.features = append(f.features, v) - } - for _, v := range source.failed { - f.failed = append(f.failed, v) - } - for _, v := range source.passed { - f.passed = append(f.passed, v) - } - for _, v := range source.skipped { - f.skipped = append(f.skipped, v) - } - for _, v := range source.undefined { - f.undefined = append(f.undefined, v) - } - for _, v := range source.pending { - f.pending = append(f.pending, v) - } + f.basefmt.Copy(source.basefmt) } } diff --git a/fmt_pretty.go b/fmt_pretty.go index e0f240f..bef7db0 100644 --- a/fmt_pretty.go +++ b/fmt_pretty.go @@ -17,20 +17,14 @@ func init() { } func prettyFunc(suite string, out io.Writer) Formatter { - return &pretty{ - basefmt: basefmt{ - started: timeNowFunc(), - indent: 2, - out: out, - }, - } + return &pretty{basefmt: newBaseFmt(suite, out)} } var outlinePlaceholderRegexp = regexp.MustCompile("<[^>]+>") // a built in default pretty formatter type pretty struct { - basefmt + *basefmt // currently processed feature *gherkin.Feature diff --git a/fmt_progress.go b/fmt_progress.go index 9f66301..1fb19db 100644 --- a/fmt_progress.go +++ b/fmt_progress.go @@ -5,7 +5,6 @@ import ( "io" "math" "strings" - "sync" "github.com/cucumber/godog/gherkin" ) @@ -17,36 +16,18 @@ func init() { func progressFunc(suite string, out io.Writer) Formatter { steps := 0 return &progress{ - basefmt: basefmt{ - started: timeNowFunc(), - indent: 2, - out: out, - }, + basefmt: newBaseFmt(suite, out), stepsPerRow: 70, - lock: new(sync.Mutex), steps: &steps, } } type progress struct { - basefmt - lock *sync.Mutex + *basefmt stepsPerRow int steps *int } -func (f *progress) Node(n interface{}) { - f.lock.Lock() - defer f.lock.Unlock() - f.basefmt.Node(n) -} - -func (f *progress) Feature(ft *gherkin.Feature, p string, c []byte) { - f.lock.Lock() - defer f.lock.Unlock() - f.basefmt.Feature(ft, p, c) -} - func (f *progress) Summary() { left := math.Mod(float64(*f.steps), float64(f.stepsPerRow)) if left != 0 { @@ -82,73 +63,63 @@ func (f *progress) step(res *stepResult) { case pending: fmt.Fprint(f.out, yellow("P")) } + *f.steps++ + if math.Mod(float64(*f.steps), float64(f.stepsPerRow)) == 0 { fmt.Fprintf(f.out, " %d\n", *f.steps) } } func (f *progress) Passed(step *gherkin.Step, match *StepDef) { + f.basefmt.Passed(step, match) + f.lock.Lock() defer f.lock.Unlock() - f.basefmt.Passed(step, match) f.step(f.passed[len(f.passed)-1]) } func (f *progress) Skipped(step *gherkin.Step, match *StepDef) { + f.basefmt.Skipped(step, match) + f.lock.Lock() defer f.lock.Unlock() - f.basefmt.Skipped(step, match) f.step(f.skipped[len(f.skipped)-1]) } func (f *progress) Undefined(step *gherkin.Step, match *StepDef) { + f.basefmt.Undefined(step, match) + f.lock.Lock() defer f.lock.Unlock() - f.basefmt.Undefined(step, match) f.step(f.undefined[len(f.undefined)-1]) } func (f *progress) Failed(step *gherkin.Step, match *StepDef, err error) { + f.basefmt.Failed(step, match, err) + f.lock.Lock() defer f.lock.Unlock() - f.basefmt.Failed(step, match, err) f.step(f.failed[len(f.failed)-1]) } func (f *progress) Pending(step *gherkin.Step, match *StepDef) { + f.basefmt.Pending(step, match) + f.lock.Lock() defer f.lock.Unlock() - f.basefmt.Pending(step, match) f.step(f.pending[len(f.pending)-1]) } func (f *progress) Sync(cf ConcurrentFormatter) { if source, ok := cf.(*progress); ok { - f.lock = source.lock + f.basefmt.Sync(source.basefmt) f.steps = source.steps } } func (f *progress) Copy(cf ConcurrentFormatter) { if source, ok := cf.(*progress); ok { - for _, v := range source.features { - f.features = append(f.features, v) - } - for _, v := range source.failed { - f.failed = append(f.failed, v) - } - for _, v := range source.passed { - f.passed = append(f.passed, v) - } - for _, v := range source.skipped { - f.skipped = append(f.skipped, v) - } - for _, v := range source.undefined { - f.undefined = append(f.undefined, v) - } - for _, v := range source.pending { - f.pending = append(f.pending, v) - } + f.basefmt.Copy(source.basefmt) } } diff --git a/suite_context.go b/suite_context.go index e4cc1c3..679fa9c 100644 --- a/suite_context.go +++ b/suite_context.go @@ -529,18 +529,12 @@ func (s *suiteContext) theRenderXMLWillBe(docstring *gherkin.DocString) error { } type testFormatter struct { - basefmt + *basefmt scenarios []interface{} } func testFormatterFunc(suite string, out io.Writer) Formatter { - return &testFormatter{ - basefmt: basefmt{ - started: timeNowFunc(), - indent: 2, - out: out, - }, - } + return &testFormatter{basefmt: newBaseFmt(suite, out)} } func (f *testFormatter) Node(node interface{}) {