Added concurrency support to the events formatter
Этот коммит содержится в:
родитель
8cd177247a
коммит
172b91ea58
3 изменённых файлов: 88 добавлений и 41 удалений
126
fmt_events.go
126
fmt_events.go
|
@ -16,33 +16,11 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func eventsFunc(suite string, out io.Writer) Formatter {
|
func eventsFunc(suite string, out io.Writer) Formatter {
|
||||||
formatter := &events{basefmt: newBaseFmt(suite, out)}
|
return &events{basefmt: newBaseFmt(suite, out)}
|
||||||
|
|
||||||
formatter.event(&struct {
|
|
||||||
Event string `json:"event"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
Timestamp int64 `json:"timestamp"`
|
|
||||||
Suite string `json:"suite"`
|
|
||||||
}{
|
|
||||||
"TestRunStarted",
|
|
||||||
spec,
|
|
||||||
timeNowFunc().UnixNano() / nanoSec,
|
|
||||||
suite,
|
|
||||||
})
|
|
||||||
|
|
||||||
return formatter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type events struct {
|
type events struct {
|
||||||
*basefmt
|
*basefmt
|
||||||
|
|
||||||
// currently running feature path, to be part of id.
|
|
||||||
// this is sadly not passed by gherkin nodes.
|
|
||||||
// it restricts this formatter to run only in synchronous single
|
|
||||||
// threaded execution. Unless running a copy of formatter for each feature
|
|
||||||
path string
|
|
||||||
status stepResultStatus // last step status, before skipped
|
|
||||||
outlineSteps int // number of current outline scenario steps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *events) event(ev interface{}) {
|
func (f *events) event(ev interface{}) {
|
||||||
|
@ -56,13 +34,16 @@ func (f *events) event(ev interface{}) {
|
||||||
func (f *events) Pickle(pickle *messages.Pickle) {
|
func (f *events) Pickle(pickle *messages.Pickle) {
|
||||||
f.basefmt.Pickle(pickle)
|
f.basefmt.Pickle(pickle)
|
||||||
|
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
f.event(&struct {
|
f.event(&struct {
|
||||||
Event string `json:"event"`
|
Event string `json:"event"`
|
||||||
Location string `json:"location"`
|
Location string `json:"location"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
}{
|
}{
|
||||||
"TestCaseStarted",
|
"TestCaseStarted",
|
||||||
f.scenarioLocation(pickle.AstNodeIds),
|
f.scenarioLocation(pickle),
|
||||||
timeNowFunc().UnixNano() / nanoSec,
|
timeNowFunc().UnixNano() / nanoSec,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -76,16 +57,38 @@ func (f *events) Pickle(pickle *messages.Pickle) {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
}{
|
}{
|
||||||
"TestCaseFinished",
|
"TestCaseFinished",
|
||||||
f.scenarioLocation(pickle.AstNodeIds),
|
f.scenarioLocation(pickle),
|
||||||
timeNowFunc().UnixNano() / nanoSec,
|
timeNowFunc().UnixNano() / nanoSec,
|
||||||
"undefined",
|
"undefined",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *events) TestRunStarted() {
|
||||||
|
f.basefmt.TestRunStarted()
|
||||||
|
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
f.event(&struct {
|
||||||
|
Event string `json:"event"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
Suite string `json:"suite"`
|
||||||
|
}{
|
||||||
|
"TestRunStarted",
|
||||||
|
spec,
|
||||||
|
timeNowFunc().UnixNano() / nanoSec,
|
||||||
|
f.suiteName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) {
|
func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) {
|
||||||
f.basefmt.Feature(ft, p, c)
|
f.basefmt.Feature(ft, p, c)
|
||||||
f.path = p
|
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
f.event(&struct {
|
f.event(&struct {
|
||||||
Event string `json:"event"`
|
Event string `json:"event"`
|
||||||
Location string `json:"location"`
|
Location string `json:"location"`
|
||||||
|
@ -130,6 +133,18 @@ func (f *events) Summary() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *events) Sync(cf ConcurrentFormatter) {
|
||||||
|
if source, ok := cf.(*events); ok {
|
||||||
|
f.basefmt.Sync(source.basefmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *events) Copy(cf ConcurrentFormatter) {
|
||||||
|
if source, ok := cf.(*events); ok {
|
||||||
|
f.basefmt.Copy(source.basefmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (f *events) step(res *stepResult) {
|
func (f *events) step(res *stepResult) {
|
||||||
step := f.findStep(res.step.AstNodeIds[0])
|
step := f.findStep(res.step.AstNodeIds[0])
|
||||||
|
|
||||||
|
@ -145,13 +160,28 @@ func (f *events) step(res *stepResult) {
|
||||||
Summary string `json:"summary,omitempty"`
|
Summary string `json:"summary,omitempty"`
|
||||||
}{
|
}{
|
||||||
"TestStepFinished",
|
"TestStepFinished",
|
||||||
fmt.Sprintf("%s:%d", f.path, step.Location.Line),
|
fmt.Sprintf("%s:%d", res.owner.Uri, step.Location.Line),
|
||||||
timeNowFunc().UnixNano() / nanoSec,
|
timeNowFunc().UnixNano() / nanoSec,
|
||||||
res.status.String(),
|
res.status.String(),
|
||||||
errMsg,
|
errMsg,
|
||||||
})
|
})
|
||||||
|
|
||||||
if isLastStep(res.owner, res.step) {
|
if isLastStep(res.owner, res.step) {
|
||||||
|
var status string
|
||||||
|
|
||||||
|
for _, stepResult := range f.lastFeature().lastPickleResult().stepResults {
|
||||||
|
switch stepResult.status {
|
||||||
|
case passed:
|
||||||
|
status = passed.String()
|
||||||
|
case failed:
|
||||||
|
status = failed.String()
|
||||||
|
case undefined:
|
||||||
|
status = undefined.String()
|
||||||
|
case pending:
|
||||||
|
status = pending.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
f.event(&struct {
|
f.event(&struct {
|
||||||
Event string `json:"event"`
|
Event string `json:"event"`
|
||||||
Location string `json:"location"`
|
Location string `json:"location"`
|
||||||
|
@ -159,14 +189,19 @@ func (f *events) step(res *stepResult) {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
}{
|
}{
|
||||||
"TestCaseFinished",
|
"TestCaseFinished",
|
||||||
f.scenarioLocation(res.owner.AstNodeIds),
|
f.scenarioLocation(res.owner),
|
||||||
timeNowFunc().UnixNano() / nanoSec,
|
timeNowFunc().UnixNano() / nanoSec,
|
||||||
f.status.String(),
|
status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep, def *StepDefinition) {
|
func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep, def *StepDefinition) {
|
||||||
|
f.basefmt.Defined(pickle, pickleStep, def)
|
||||||
|
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
step := f.findStep(pickleStep.AstNodeIds[0])
|
step := f.findStep(pickleStep.AstNodeIds[0])
|
||||||
|
|
||||||
if def != nil {
|
if def != nil {
|
||||||
|
@ -191,7 +226,7 @@ func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_Pi
|
||||||
Args [][2]int `json:"arguments"`
|
Args [][2]int `json:"arguments"`
|
||||||
}{
|
}{
|
||||||
"StepDefinitionFound",
|
"StepDefinitionFound",
|
||||||
fmt.Sprintf("%s:%d", f.path, step.Location.Line),
|
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
|
||||||
def.definitionID(),
|
def.definitionID(),
|
||||||
args,
|
args,
|
||||||
})
|
})
|
||||||
|
@ -203,7 +238,7 @@ func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_Pi
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
}{
|
}{
|
||||||
"TestStepStarted",
|
"TestStepStarted",
|
||||||
fmt.Sprintf("%s:%d", f.path, step.Location.Line),
|
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
|
||||||
timeNowFunc().UnixNano() / nanoSec,
|
timeNowFunc().UnixNano() / nanoSec,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -211,44 +246,55 @@ func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_Pi
|
||||||
func (f *events) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
|
func (f *events) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
|
||||||
f.basefmt.Passed(pickle, step, match)
|
f.basefmt.Passed(pickle, step, match)
|
||||||
|
|
||||||
f.status = passed
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
f.step(f.lastStepResult())
|
f.step(f.lastStepResult())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
|
func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
|
||||||
f.basefmt.Skipped(pickle, step, match)
|
f.basefmt.Skipped(pickle, step, match)
|
||||||
|
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
f.step(f.lastStepResult())
|
f.step(f.lastStepResult())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
|
func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
|
||||||
f.basefmt.Undefined(pickle, step, match)
|
f.basefmt.Undefined(pickle, step, match)
|
||||||
|
|
||||||
f.status = undefined
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
f.step(f.lastStepResult())
|
f.step(f.lastStepResult())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
|
func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
|
||||||
f.basefmt.Failed(pickle, step, match, err)
|
f.basefmt.Failed(pickle, step, match, err)
|
||||||
|
|
||||||
f.status = failed
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
f.step(f.lastStepResult())
|
f.step(f.lastStepResult())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
|
func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
|
||||||
f.basefmt.Pending(pickle, step, match)
|
f.basefmt.Pending(pickle, step, match)
|
||||||
|
|
||||||
f.status = pending
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
f.step(f.lastStepResult())
|
f.step(f.lastStepResult())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *events) scenarioLocation(astNodeIds []string) string {
|
func (f *events) scenarioLocation(pickle *messages.Pickle) string {
|
||||||
scenario := f.findScenario(astNodeIds[0])
|
scenario := f.findScenario(pickle.AstNodeIds[0])
|
||||||
line := scenario.Location.Line
|
line := scenario.Location.Line
|
||||||
if len(astNodeIds) == 2 {
|
if len(pickle.AstNodeIds) == 2 {
|
||||||
_, row := f.findExample(astNodeIds[1])
|
_, row := f.findExample(pickle.AstNodeIds[1])
|
||||||
line = row.Location.Line
|
line = row.Location.Line
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s:%d", f.path, line)
|
return fmt.Sprintf("%s:%d", pickle.Uri, line)
|
||||||
}
|
}
|
||||||
|
|
2
run.go
2
run.go
|
@ -281,7 +281,7 @@ func supportsConcurrency(format string) bool {
|
||||||
case "progress", "junit":
|
case "progress", "junit":
|
||||||
return true
|
return true
|
||||||
case "events":
|
case "events":
|
||||||
return false
|
return true
|
||||||
case "cucumber":
|
case "cucumber":
|
||||||
return false
|
return false
|
||||||
case "pretty":
|
case "pretty":
|
||||||
|
|
|
@ -252,6 +252,7 @@ func TestFormatterConcurrencyRun(t *testing.T) {
|
||||||
"progress",
|
"progress",
|
||||||
"junit",
|
"junit",
|
||||||
"pretty",
|
"pretty",
|
||||||
|
"events",
|
||||||
}
|
}
|
||||||
|
|
||||||
featurePaths := []string{"formatter-tests/features"}
|
featurePaths := []string{"formatter-tests/features"}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче