Added Pickle and PickleStep results to the in-mem storage

Этот коммит содержится в:
Fredrik Lönnblad 2020-06-09 21:51:19 +02:00
родитель f50bd30ec0
коммит cd7663fccf
12 изменённых файлов: 508 добавлений и 384 удалений

156
fmt.go
Просмотреть файл

@ -139,25 +139,25 @@ func (st stepResultStatus) String() string {
} }
} }
type stepResult struct { type pickleStepResult struct {
status stepResultStatus Status stepResultStatus
time time.Time finishedAt time.Time
err error err error
pickleID string PickleID string
pickleStepID string PickleStepID string
def *StepDefinition def *StepDefinition
} }
func newStepResult(pickleID, pickleStepID string, match *StepDefinition) *stepResult { func newStepResult(pickleID, pickleStepID string, match *StepDefinition) pickleStepResult {
return &stepResult{time: timeNowFunc(), pickleID: pickleID, pickleStepID: pickleStepID, def: match} return pickleStepResult{finishedAt: timeNowFunc(), PickleID: pickleID, PickleStepID: pickleStepID, def: match}
} }
func newBaseFmt(suite string, out io.Writer) *basefmt { func newBaseFmt(suite string, out io.Writer) *basefmt {
return &basefmt{ return &basefmt{
suiteName: suite, suiteName: suite,
started: timeNowFunc(), startedAt: timeNowFunc(),
indent: 2, indent: 2,
out: out, out: out,
lock: new(sync.Mutex), lock: new(sync.Mutex),
@ -173,8 +173,8 @@ type basefmt struct {
storage *storage storage *storage
started time.Time startedAt time.Time
features []*feature features []*feature
firstFeature *bool firstFeature *bool
lock *sync.Mutex lock *sync.Mutex
@ -188,10 +188,6 @@ func (f *basefmt) lastFeature() *feature {
return f.features[len(f.features)-1] return f.features[len(f.features)-1]
} }
func (f *basefmt) lastStepResult() *stepResult {
return f.lastFeature().lastStepResult()
}
func (f *basefmt) findFeature(scenarioAstID string) *feature { func (f *basefmt) findFeature(scenarioAstID string) *feature {
for _, ft := range f.features { for _, ft := range f.features {
if sc := ft.findScenario(scenarioAstID); sc != nil { if sc := ft.findScenario(scenarioAstID); sc != nil {
@ -250,15 +246,7 @@ func (f *basefmt) TestRunStarted() {
f.firstFeature = &firstFeature f.firstFeature = &firstFeature
} }
func (f *basefmt) Pickle(p *messages.Pickle) { func (f *basefmt) Pickle(p *messages.Pickle) {}
f.lock.Lock()
defer f.lock.Unlock()
feature := f.features[len(f.features)-1]
pr := pickleResult{pickleID: p.Id, time: timeNowFunc()}
feature.pickleResults = append(feature.pickleResults, &pr)
}
func (f *basefmt) Defined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition) {} func (f *basefmt) Defined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition) {}
@ -268,94 +256,60 @@ func (f *basefmt) Feature(ft *messages.GherkinDocument, p string, c []byte) {
*f.firstFeature = false *f.firstFeature = false
f.features = append(f.features, &feature{path: p, GherkinDocument: ft, time: timeNowFunc()}) f.features = append(f.features, &feature{GherkinDocument: ft, time: timeNowFunc()})
} }
func (f *basefmt) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { func (f *basefmt) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.lock.Lock()
defer f.lock.Unlock()
s := newStepResult(pickle.Id, step.Id, match)
s.status = passed
f.lastFeature().appendStepResult(s)
} }
func (f *basefmt) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { func (f *basefmt) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.lock.Lock()
defer f.lock.Unlock()
s := newStepResult(pickle.Id, step.Id, match)
s.status = skipped
f.lastFeature().appendStepResult(s)
} }
func (f *basefmt) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { func (f *basefmt) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.lock.Lock()
defer f.lock.Unlock()
s := newStepResult(pickle.Id, step.Id, match)
s.status = undefined
f.lastFeature().appendStepResult(s)
} }
func (f *basefmt) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) { func (f *basefmt) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
f.lock.Lock()
defer f.lock.Unlock()
s := newStepResult(pickle.Id, step.Id, match)
s.status = failed
s.err = err
f.lastFeature().appendStepResult(s)
} }
func (f *basefmt) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { func (f *basefmt) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
f.lock.Lock()
defer f.lock.Unlock()
s := newStepResult(pickle.Id, step.Id, match)
s.status = pending
f.lastFeature().appendStepResult(s)
} }
func (f *basefmt) Summary() { func (f *basefmt) Summary() {
var totalSc, passedSc, undefinedSc int var totalSc, passedSc, undefinedSc int
var totalSt, passedSt, failedSt, skippedSt, pendingSt, undefinedSt int var totalSt, passedSt, failedSt, skippedSt, pendingSt, undefinedSt int
for _, feat := range f.features { pickleResults := f.storage.mustGetPickleResults()
for _, pr := range feat.pickleResults { for _, pr := range pickleResults {
var prStatus stepResultStatus var prStatus stepResultStatus
totalSc++ totalSc++
if len(pr.stepResults) == 0 { pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pr.PickleID)
if len(pickleStepResults) == 0 {
prStatus = undefined
}
for _, sr := range pickleStepResults {
totalSt++
switch sr.Status {
case passed:
prStatus = passed
passedSt++
case failed:
prStatus = failed
failedSt++
case skipped:
skippedSt++
case undefined:
prStatus = undefined prStatus = undefined
undefinedSt++
case pending:
prStatus = pending
pendingSt++
} }
}
for _, sr := range pr.stepResults { if prStatus == passed {
totalSt++ passedSc++
} else if prStatus == undefined {
switch sr.status { undefinedSc++
case passed:
prStatus = passed
passedSt++
case failed:
prStatus = failed
failedSt++
case skipped:
skippedSt++
case undefined:
prStatus = undefined
undefinedSt++
case pending:
prStatus = pending
pendingSt++
}
}
if prStatus == passed {
passedSc++
} else if prStatus == undefined {
undefinedSc++
}
} }
} }
@ -385,7 +339,7 @@ func (f *basefmt) Summary() {
scenarios = append(scenarios, green(fmt.Sprintf("%d passed", passedSc))) scenarios = append(scenarios, green(fmt.Sprintf("%d passed", passedSc)))
} }
scenarios = append(scenarios, parts...) scenarios = append(scenarios, parts...)
elapsed := timeNowFunc().Sub(f.started) elapsed := timeNowFunc().Sub(f.startedAt)
fmt.Fprintln(f.out, "") fmt.Fprintln(f.out, "")
@ -437,22 +391,8 @@ func (f *basefmt) Copy(cf ConcurrentFormatter) {
} }
} }
func (f *basefmt) findStepResults(status stepResultStatus) (res []*stepResult) {
for _, feat := range f.features {
for _, pr := range feat.pickleResults {
for _, sr := range pr.stepResults {
if sr.status == status {
res = append(res, sr)
}
}
}
}
return
}
func (f *basefmt) snippets() string { func (f *basefmt) snippets() string {
undefinedStepResults := f.findStepResults(undefined) undefinedStepResults := f.storage.mustGetPickleStepResultsByStatus(undefined)
if len(undefinedStepResults) == 0 { if len(undefinedStepResults) == 0 {
return "" return ""
} }
@ -461,7 +401,7 @@ func (f *basefmt) snippets() string {
var snips []undefinedSnippet var snips []undefinedSnippet
// build snippets // build snippets
for _, u := range undefinedStepResults { for _, u := range undefinedStepResults {
pickleStep := f.storage.mustGetPickleStep(u.pickleStepID) pickleStep := f.storage.mustGetPickleStep(u.PickleStepID)
steps := []string{pickleStep.Text} steps := []string{pickleStep.Text}
arg := pickleStep.Argument arg := pickleStep.Argument

Просмотреть файл

@ -17,6 +17,8 @@ import (
"io" "io"
"sort" "sort"
"strings" "strings"
"github.com/cucumber/messages-go/v10"
) )
func init() { func init() {
@ -61,7 +63,11 @@ func (f *cukefmt) buildCukeFeatures(features []*feature) (res []cukeFeatureJSON)
for idx, feat := range features { for idx, feat := range features {
cukeFeature := buildCukeFeature(feat) cukeFeature := buildCukeFeature(feat)
cukeFeature.Elements = f.buildCukeElements(feat.pickleResults)
pickles := f.storage.mustGetPickles(feat.Uri)
sort.Sort(sortPicklesByID(pickles))
cukeFeature.Elements = f.buildCukeElements(pickles)
for jdx, elem := range cukeFeature.Elements { for jdx, elem := range cukeFeature.Elements {
elem.ID = cukeFeature.ID + ";" + makeCukeID(elem.Name) + elem.ID elem.ID = cukeFeature.ID + ";" + makeCukeID(elem.Name) + elem.ID
@ -75,26 +81,29 @@ func (f *cukefmt) buildCukeFeatures(features []*feature) (res []cukeFeatureJSON)
return res return res
} }
func (f *cukefmt) buildCukeElements(pickleResults []*pickleResult) (res []cukeElement) { func (f *cukefmt) buildCukeElements(pickles []*messages.Pickle) (res []cukeElement) {
res = make([]cukeElement, len(pickleResults)) res = make([]cukeElement, len(pickles))
for idx, pickleResult := range pickleResults { for idx, pickle := range pickles {
pickle := f.storage.mustGetPickle(pickleResult.pickleID) pickleResult := f.storage.mustGetPickleResult(pickle.Id)
pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
cukeElement := f.buildCukeElement(pickle.Name, pickle.AstNodeIds) cukeElement := f.buildCukeElement(pickle.Name, pickle.AstNodeIds)
stepStartedAt := pickleResult.startedAt() stepStartedAt := pickleResult.StartedAt
cukeElement.Steps = make([]cukeStep, len(pickleResult.stepResults)) cukeElement.Steps = make([]cukeStep, len(pickleStepResults))
for jdx, stepResult := range pickleResult.stepResults { sort.Sort(sortPickleStepResultsByPickleStepID(pickleStepResults))
cukeStep := f.buildCukeStep(stepResult)
stepResultFinishedAt := stepResult.time for jdx, stepResult := range pickleStepResults {
cukeStep := f.buildCukeStep(pickle, stepResult)
stepResultFinishedAt := stepResult.finishedAt
d := int(stepResultFinishedAt.Sub(stepStartedAt).Nanoseconds()) d := int(stepResultFinishedAt.Sub(stepStartedAt).Nanoseconds())
stepStartedAt = stepResultFinishedAt stepStartedAt = stepResultFinishedAt
cukeStep.Result.Duration = &d cukeStep.Result.Duration = &d
if stepResult.status == undefined || stepResult.status == pending || stepResult.status == skipped { if stepResult.Status == undefined || stepResult.Status == pending || stepResult.Status == skipped {
cukeStep.Result.Duration = nil cukeStep.Result.Duration = nil
} }
@ -172,7 +181,7 @@ type cukeFeatureJSON struct {
func buildCukeFeature(feat *feature) cukeFeatureJSON { func buildCukeFeature(feat *feature) cukeFeatureJSON {
cukeFeature := cukeFeatureJSON{ cukeFeature := cukeFeatureJSON{
URI: feat.path, URI: feat.Uri,
ID: makeCukeID(feat.Feature.Name), ID: makeCukeID(feat.Feature.Name),
Keyword: feat.Feature.Keyword, Keyword: feat.Feature.Keyword,
Name: feat.Feature.Name, Name: feat.Feature.Name,
@ -238,9 +247,8 @@ func (f *cukefmt) buildCukeElement(pickleName string, pickleAstNodeIDs []string)
return cukeElement return cukeElement
} }
func (f *cukefmt) buildCukeStep(stepResult *stepResult) (cukeStep cukeStep) { func (f *cukefmt) buildCukeStep(pickle *messages.Pickle, stepResult pickleStepResult) (cukeStep cukeStep) {
pickle := f.storage.mustGetPickle(stepResult.pickleID) pickleStep := f.storage.mustGetPickleStep(stepResult.PickleStepID)
pickleStep := f.storage.mustGetPickleStep(stepResult.pickleStepID)
step := f.findStep(pickleStep.AstNodeIds[0]) step := f.findStep(pickleStep.AstNodeIds[0])
line := step.Location.Line line := step.Location.Line
@ -277,12 +285,12 @@ func (f *cukefmt) buildCukeStep(stepResult *stepResult) (cukeStep cukeStep) {
cukeStep.Match.Location = strings.Split(stepResult.def.definitionID(), " ")[0] cukeStep.Match.Location = strings.Split(stepResult.def.definitionID(), " ")[0]
} }
cukeStep.Result.Status = stepResult.status.String() cukeStep.Result.Status = stepResult.Status.String()
if stepResult.err != nil { if stepResult.err != nil {
cukeStep.Result.Error = stepResult.err.Error() cukeStep.Result.Error = stepResult.err.Error()
} }
if stepResult.status == undefined || stepResult.status == pending { if stepResult.Status == undefined || stepResult.Status == pending {
cukeStep.Match.Location = fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line) cukeStep.Match.Location = fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line)
} }

Просмотреть файл

@ -103,10 +103,13 @@ func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) {
func (f *events) Summary() { func (f *events) Summary() {
// @TODO: determine status // @TODO: determine status
status := passed status := passed
if len(f.findStepResults(failed)) > 0 {
f.storage.mustGetPickleStepResultsByStatus(failed)
if len(f.storage.mustGetPickleStepResultsByStatus(failed)) > 0 {
status = failed status = failed
} else if len(f.findStepResults(passed)) == 0 { } else if len(f.storage.mustGetPickleStepResultsByStatus(passed)) == 0 {
if len(f.findStepResults(undefined)) > len(f.findStepResults(pending)) { if len(f.storage.mustGetPickleStepResultsByStatus(undefined)) > len(f.storage.mustGetPickleStepResultsByStatus(pending)) {
status = undefined status = undefined
} else { } else {
status = pending status = pending
@ -145,13 +148,13 @@ func (f *events) Copy(cf ConcurrentFormatter) {
} }
} }
func (f *events) step(pickle *messages.Pickle, res *stepResult) { func (f *events) step(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep) {
pickleStep := f.storage.mustGetPickleStep(res.pickleStepID) pickleStepResult := f.storage.mustGetPickleStepResult(pickleStep.Id)
step := f.findStep(pickleStep.AstNodeIds[0]) step := f.findStep(pickleStep.AstNodeIds[0])
var errMsg string var errMsg string
if res.err != nil { if pickleStepResult.err != nil {
errMsg = res.err.Error() errMsg = pickleStepResult.err.Error()
} }
f.event(&struct { f.event(&struct {
Event string `json:"event"` Event string `json:"event"`
@ -163,15 +166,16 @@ func (f *events) step(pickle *messages.Pickle, res *stepResult) {
"TestStepFinished", "TestStepFinished",
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line), fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
timeNowFunc().UnixNano() / nanoSec, timeNowFunc().UnixNano() / nanoSec,
res.status.String(), pickleStepResult.Status.String(),
errMsg, errMsg,
}) })
if isLastStep(pickle, pickleStep) { if isLastStep(pickle, pickleStep) {
var status string var status string
for _, stepResult := range f.lastFeature().lastPickleResult().stepResults { pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
switch stepResult.status { for _, stepResult := range pickleStepResults {
switch stepResult.Status {
case passed: case passed:
status = passed.String() status = passed.String()
case failed: case failed:
@ -250,7 +254,7 @@ func (f *events) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.step(pickle, f.lastStepResult()) f.step(pickle, step)
} }
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) {
@ -259,7 +263,7 @@ func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.step(pickle, f.lastStepResult()) f.step(pickle, step)
} }
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) {
@ -268,7 +272,7 @@ func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.step(pickle, f.lastStepResult()) f.step(pickle, step)
} }
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) {
@ -277,7 +281,7 @@ func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.step(pickle, f.lastStepResult()) f.step(pickle, step)
} }
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) {
@ -286,7 +290,7 @@ func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.step(pickle, f.lastStepResult()) f.step(pickle, step)
} }
func (f *events) scenarioLocation(pickle *messages.Pickle) string { func (f *events) scenarioLocation(pickle *messages.Pickle) string {

Просмотреть файл

@ -57,30 +57,53 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
suite := junitPackageSuite{ suite := junitPackageSuite{
Name: f.suiteName, Name: f.suiteName,
TestSuites: make([]*junitTestSuite, len(f.features)), TestSuites: make([]*junitTestSuite, len(f.features)),
Time: junitTimeDuration(f.started, timeNowFunc()), Time: junitTimeDuration(f.startedAt, timeNowFunc()),
} }
sort.Sort(sortByName(f.features)) sort.Sort(sortByName(f.features))
for idx, feat := range f.features { for idx, feat := range f.features {
pickles := f.storage.mustGetPickles(feat.Uri)
sort.Sort(sortPicklesByID(pickles))
var finishedAt = feat.startedAt()
if len(pickles) > 0 {
lastPickle := pickles[len(pickles)-1]
if len(lastPickle.Steps) > 0 {
lastStep := lastPickle.Steps[len(lastPickle.Steps)-1]
lastPickleStepResult := f.storage.mustGetPickleStepResult(lastStep.Id)
finishedAt = lastPickleStepResult.finishedAt
}
}
ts := junitTestSuite{ ts := junitTestSuite{
Name: feat.GherkinDocument.Feature.Name, Name: feat.GherkinDocument.Feature.Name,
Time: junitTimeDuration(feat.startedAt(), feat.finishedAt()), Time: junitTimeDuration(feat.startedAt(), finishedAt),
TestCases: make([]*junitTestCase, len(feat.pickleResults)), TestCases: make([]*junitTestCase, len(pickles)),
} }
var testcaseNames = make(map[string]int) var testcaseNames = make(map[string]int)
for _, pickleResult := range feat.pickleResults { for _, pickle := range pickles {
pickle := f.storage.mustGetPickle(pickleResult.pickleID)
testcaseNames[pickle.Name] = testcaseNames[pickle.Name] + 1 testcaseNames[pickle.Name] = testcaseNames[pickle.Name] + 1
} }
var outlineNo = make(map[string]int) var outlineNo = make(map[string]int)
for idx, pickleResult := range feat.pickleResults { for idx, pickle := range pickles {
tc := junitTestCase{} tc := junitTestCase{}
tc.Time = junitTimeDuration(pickleResult.startedAt(), pickleResult.finishedAt())
pickle := f.storage.mustGetPickle(pickleResult.pickleID) pickleResult := f.storage.mustGetPickleResult(pickle.Id)
var finishedAt = pickleResult.StartedAt
if len(pickle.Steps) > 0 {
lastStep := pickle.Steps[len(pickle.Steps)-1]
lastPickleStepResult := f.storage.mustGetPickleStepResult(lastStep.Id)
finishedAt = lastPickleStepResult.finishedAt
}
tc.Time = junitTimeDuration(pickleResult.StartedAt, finishedAt)
tc.Name = pickle.Name tc.Name = pickle.Name
if testcaseNames[tc.Name] > 1 { if testcaseNames[tc.Name] > 1 {
@ -91,10 +114,11 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
ts.Tests++ ts.Tests++
suite.Tests++ suite.Tests++
for _, stepResult := range pickleResult.stepResults { pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
pickleStep := f.storage.mustGetPickleStep(stepResult.pickleStepID) for _, stepResult := range pickleStepResults {
pickleStep := f.storage.mustGetPickleStep(stepResult.PickleStepID)
switch stepResult.status { switch stepResult.Status {
case passed: case passed:
tc.Status = passed.String() tc.Status = passed.String()
case failed: case failed:

Просмотреть файл

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io" "io"
"regexp" "regexp"
"sort"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
@ -61,7 +62,7 @@ func (f *pretty) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.printStep(pickle, f.lastStepResult()) f.printStep(pickle, step)
} }
func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -70,7 +71,7 @@ func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.printStep(pickle, f.lastStepResult()) f.printStep(pickle, step)
} }
func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -79,7 +80,7 @@ func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.printStep(pickle, f.lastStepResult()) f.printStep(pickle, step)
} }
func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) { func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
@ -88,7 +89,7 @@ func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.printStep(pickle, f.lastStepResult()) f.printStep(pickle, step)
} }
func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -97,7 +98,7 @@ func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.printStep(pickle, f.lastStepResult()) f.printStep(pickle, step)
} }
func (f *pretty) Sync(cf ConcurrentFormatter) { func (f *pretty) Sync(cf ConcurrentFormatter) {
@ -145,7 +146,7 @@ func (f *pretty) scenarioLengths(scenarioAstID string) (scenarioHeaderLength int
func (f *pretty) printScenarioHeader(astScenario *messages.GherkinDocument_Feature_Scenario, spaceFilling int) { func (f *pretty) printScenarioHeader(astScenario *messages.GherkinDocument_Feature_Scenario, spaceFilling int) {
text := s(f.indent) + keywordAndName(astScenario.Keyword, astScenario.Name) text := s(f.indent) + keywordAndName(astScenario.Keyword, astScenario.Name)
text += s(spaceFilling) + f.line(f.lastFeature().path, astScenario.Location) text += s(spaceFilling) + f.line(f.lastFeature().Uri, astScenario.Location)
fmt.Fprintln(f.out, "\n"+text) fmt.Fprintln(f.out, "\n"+text)
} }
@ -192,12 +193,15 @@ func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
// Summary sumarize the feature formatter output // Summary sumarize the feature formatter output
func (f *pretty) Summary() { func (f *pretty) Summary() {
failedStepResults := f.findStepResults(failed) failedStepResults := f.storage.mustGetPickleStepResultsByStatus(failed)
if len(failedStepResults) > 0 { if len(failedStepResults) > 0 {
fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n") fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n")
sort.Sort(sortPickleStepResultsByPickleStepID(failedStepResults))
for _, fail := range failedStepResults { for _, fail := range failedStepResults {
pickle := f.storage.mustGetPickle(fail.pickleID) pickle := f.storage.mustGetPickle(fail.PickleID)
pickleStep := f.storage.mustGetPickleStep(fail.pickleStepID) pickleStep := f.storage.mustGetPickleStep(fail.PickleStepID)
feature := f.findFeature(pickle.AstNodeIds[0]) feature := f.findFeature(pickle.AstNodeIds[0])
@ -207,8 +211,8 @@ func (f *pretty) Summary() {
astStep := f.findStep(pickleStep.AstNodeIds[0]) astStep := f.findStep(pickleStep.AstNodeIds[0])
stepDesc := strings.TrimSpace(astStep.Keyword) + " " + pickleStep.Text stepDesc := strings.TrimSpace(astStep.Keyword) + " " + pickleStep.Text
fmt.Fprintln(f.out, s(f.indent)+red(scenarioDesc)+f.line(feature.path, astScenario.Location)) fmt.Fprintln(f.out, s(f.indent)+red(scenarioDesc)+f.line(feature.Uri, astScenario.Location))
fmt.Fprintln(f.out, s(f.indent*2)+red(stepDesc)+f.line(feature.path, astStep.Location)) fmt.Fprintln(f.out, s(f.indent*2)+red(stepDesc)+f.line(feature.Uri, astStep.Location))
fmt.Fprintln(f.out, s(f.indent*3)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.err))+"\n") fmt.Fprintln(f.out, s(f.indent*3)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.err))+"\n")
} }
} }
@ -227,7 +231,9 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
printExampleHeader := exampleTable.TableBody[0].Id == exampleRow.Id printExampleHeader := exampleTable.TableBody[0].Id == exampleRow.Id
firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line
firstExecutedScenarioStep := len(f.lastFeature().lastPickleResult().stepResults) == backgroundSteps+1 pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1
if firstExamplesTable && printExampleHeader && firstExecutedScenarioStep { if firstExamplesTable && printExampleHeader && firstExecutedScenarioStep {
f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength) f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength)
} }
@ -237,32 +243,31 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
return return
} }
lastStep := len(f.lastFeature().lastPickleResult().stepResults) == len(pickle.Steps) lastStep := len(pickleStepResults) == len(pickle.Steps)
if !lastStep { if !lastStep {
// do not print examples unless all steps has finished // do not print examples unless all steps has finished
return return
} }
lastPickleResult := f.lastFeature().lastPickleResult() for _, result := range pickleStepResults {
for _, result := range lastPickleResult.stepResults {
// determine example row status // determine example row status
switch { switch {
case result.status == failed: case result.Status == failed:
errorMsg = result.err.Error() errorMsg = result.err.Error()
clr = result.status.clr() clr = result.Status.clr()
case result.status == undefined || result.status == pending: case result.Status == undefined || result.Status == pending:
clr = result.status.clr() clr = result.Status.clr()
case result.status == skipped && clr == nil: case result.Status == skipped && clr == nil:
clr = cyan clr = cyan
} }
if firstExamplesTable && printExampleHeader { if firstExamplesTable && printExampleHeader {
// in first example, we need to print steps // in first example, we need to print steps
var text string
pickleStep := f.storage.mustGetPickleStep(result.pickleStepID) pickleStep := f.storage.mustGetPickleStep(result.PickleStepID)
astStep := f.findStep(pickleStep.AstNodeIds[0]) astStep := f.findStep(pickleStep.AstNodeIds[0])
var text = ""
if result.def != nil { if result.def != nil {
if m := outlinePlaceholderRegexp.FindAllStringIndex(astStep.Text, -1); len(m) > 0 { if m := outlinePlaceholderRegexp.FindAllStringIndex(astStep.Text, -1); len(m) > 0 {
var pos int var pos int
@ -277,16 +282,13 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
text = cyan(astStep.Text) text = cyan(astStep.Text)
} }
pickle := f.storage.mustGetPickle(lastPickleResult.pickleID)
_, maxLength := f.scenarioLengths(pickle.AstNodeIds[0]) _, maxLength := f.scenarioLengths(pickle.AstNodeIds[0])
stepLength := f.lengthPickleStep(astStep.Keyword, astStep.Text) stepLength := f.lengthPickleStep(astStep.Keyword, astStep.Text)
text += s(maxLength - stepLength) text += s(maxLength - stepLength)
text += " " + blackb("# "+result.def.definitionID()) text += " " + blackb("# "+result.def.definitionID())
} else {
text = cyan(astStep.Text)
} }
// print the step outline // print the step outline
fmt.Fprintln(f.out, s(f.indent*2)+cyan(strings.TrimSpace(astStep.Keyword))+" "+text) fmt.Fprintln(f.out, s(f.indent*2)+cyan(strings.TrimSpace(astStep.Keyword))+" "+text)
@ -333,9 +335,7 @@ func (f *pretty) printTableHeader(row *messages.GherkinDocument_Feature_TableRow
f.printTableRow(row, max, cyan) f.printTableRow(row, max, cyan)
} }
func (f *pretty) printStep(pickle *messages.Pickle, result *stepResult) { func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep) {
pickleStep := f.storage.mustGetPickleStep(result.pickleStepID)
astBackground := f.findBackground(pickle.AstNodeIds[0]) astBackground := f.findBackground(pickle.AstNodeIds[0])
astScenario := f.findScenario(pickle.AstNodeIds[0]) astScenario := f.findScenario(pickle.AstNodeIds[0])
astStep := f.findStep(pickleStep.AstNodeIds[0]) astStep := f.findStep(pickleStep.AstNodeIds[0])
@ -345,14 +345,25 @@ func (f *pretty) printStep(pickle *messages.Pickle, result *stepResult) {
backgroundSteps = len(astBackground.Steps) backgroundSteps = len(astBackground.Steps)
} }
astBackgroundStep := backgroundSteps > 0 && backgroundSteps >= len(f.lastFeature().lastPickleResult().stepResults) pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
astBackgroundStep := backgroundSteps > 0 && backgroundSteps >= len(pickleStepResults)
if astBackgroundStep { if astBackgroundStep {
if len(f.lastFeature().pickleResults) > 1 { pickles := f.storage.mustGetPickles(pickle.Uri)
var pickleResults []pickleResult
for _, pickle := range pickles {
pr, err := f.storage.getPickleResult(pickle.Id)
if err == nil {
pickleResults = append(pickleResults, pr)
}
}
if len(pickleResults) > 1 {
return return
} }
firstExecutedBackgroundStep := astBackground != nil && len(f.lastFeature().lastPickleResult().stepResults) == 1 firstExecutedBackgroundStep := astBackground != nil && len(pickleStepResults) == 1
if firstExecutedBackgroundStep { if firstExecutedBackgroundStep {
fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astBackground.Keyword, astBackground.Name)) fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astBackground.Keyword, astBackground.Name))
} }
@ -366,15 +377,16 @@ func (f *pretty) printStep(pickle *messages.Pickle, result *stepResult) {
scenarioHeaderLength, maxLength := f.scenarioLengths(pickle.AstNodeIds[0]) scenarioHeaderLength, maxLength := f.scenarioLengths(pickle.AstNodeIds[0])
stepLength := f.lengthPickleStep(astStep.Keyword, pickleStep.Text) stepLength := f.lengthPickleStep(astStep.Keyword, pickleStep.Text)
firstExecutedScenarioStep := len(f.lastFeature().lastPickleResult().stepResults) == backgroundSteps+1 firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1
if !astBackgroundStep && firstExecutedScenarioStep { if !astBackgroundStep && firstExecutedScenarioStep {
f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength) f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength)
} }
text := s(f.indent*2) + result.status.clr()(strings.TrimSpace(astStep.Keyword)) + " " + result.status.clr()(pickleStep.Text) pickleStepResult := f.storage.mustGetPickleStepResult(pickleStep.Id)
if result.def != nil { text := s(f.indent*2) + pickleStepResult.Status.clr()(strings.TrimSpace(astStep.Keyword)) + " " + pickleStepResult.Status.clr()(pickleStep.Text)
if pickleStepResult.def != nil {
text += s(maxLength - stepLength + 1) text += s(maxLength - stepLength + 1)
text += blackb("# " + result.def.definitionID()) text += blackb("# " + pickleStepResult.def.definitionID())
} }
fmt.Fprintln(f.out, text) fmt.Fprintln(f.out, text)
@ -386,11 +398,11 @@ func (f *pretty) printStep(pickle *messages.Pickle, result *stepResult) {
f.printDocString(docString) f.printDocString(docString)
} }
if result.err != nil { if pickleStepResult.err != nil {
fmt.Fprintln(f.out, s(f.indent*2)+redb(fmt.Sprintf("%+v", result.err))) fmt.Fprintln(f.out, s(f.indent*2)+redb(fmt.Sprintf("%+v", pickleStepResult.err)))
} }
if result.status == pending { if pickleStepResult.Status == pending {
fmt.Fprintln(f.out, s(f.indent*3)+yellow("TODO: write pending definition")) fmt.Fprintln(f.out, s(f.indent*3)+yellow("TODO: write pending definition"))
} }
} }

Просмотреть файл

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"sort"
"strings" "strings"
"github.com/cucumber/messages-go/v10" "github.com/cucumber/messages-go/v10"
@ -39,10 +40,14 @@ func (f *progress) Summary() {
} }
var failedStepsOutput []string var failedStepsOutput []string
for _, sr := range f.findStepResults(failed) {
if sr.status == failed { failedSteps := f.storage.mustGetPickleStepResultsByStatus(failed)
pickle := f.storage.mustGetPickle(sr.pickleID) sort.Sort(sortPickleStepResultsByPickleStepID(failedSteps))
pickleStep := f.storage.mustGetPickleStep(sr.pickleStepID)
for _, sr := range failedSteps {
if sr.Status == failed {
pickle := f.storage.mustGetPickle(sr.PickleID)
pickleStep := f.storage.mustGetPickleStep(sr.PickleStepID)
sc := f.findScenario(pickle.AstNodeIds[0]) sc := f.findScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, pickle.Name) scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, pickle.Name)
@ -71,8 +76,10 @@ func (f *progress) Summary() {
f.basefmt.Summary() f.basefmt.Summary()
} }
func (f *progress) step(res *stepResult) { func (f *progress) step(pickleStepID string) {
switch res.status { pickleStepResult := f.storage.mustGetPickleStepResult(pickleStepID)
switch pickleStepResult.Status {
case passed: case passed:
fmt.Fprint(f.out, green(".")) fmt.Fprint(f.out, green("."))
case skipped: case skipped:
@ -98,7 +105,7 @@ func (f *progress) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleS
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.step(f.lastStepResult()) f.step(step.Id)
} }
func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -107,7 +114,7 @@ func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.step(f.lastStepResult()) f.step(step.Id)
} }
func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -116,7 +123,7 @@ func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pick
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.step(f.lastStepResult()) f.step(step.Id)
} }
func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) { func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
@ -125,7 +132,7 @@ func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleS
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.step(f.lastStepResult()) f.step(step.Id)
} }
func (f *progress) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { func (f *progress) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -134,7 +141,7 @@ func (f *progress) Pending(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.lock.Lock() f.lock.Lock()
defer f.lock.Unlock() defer f.lock.Unlock()
f.step(f.lastStepResult()) f.step(step.Id)
} }
func (f *progress) Sync(cf ConcurrentFormatter) { func (f *progress) Sync(cf ConcurrentFormatter) {

19
run.go
Просмотреть файл

@ -26,13 +26,15 @@ type scenarioInitializer func(*ScenarioContext)
type runner struct { type runner struct {
randomSeed int64 randomSeed int64
stopOnFailure, strict bool stopOnFailure, strict bool
features []*feature
fmt Formatter features []*feature
initializer initializer
testSuiteInitializer testSuiteInitializer initializer initializer
scenarioInitializer scenarioInitializer testSuiteInitializer testSuiteInitializer
scenarioInitializer scenarioInitializer
storage *storage storage *storage
fmt Formatter
} }
func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool) { func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool) {
@ -67,21 +69,24 @@ func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool
go func(fail *bool, feat *feature) { go func(fail *bool, feat *feature) {
var fmtCopy Formatter var fmtCopy Formatter
defer func() { defer func() {
<-queue // free a space in queue <-queue // free a space in queue
}() }()
if r.stopOnFailure && *fail { if r.stopOnFailure && *fail {
return return
} }
suite := &Suite{ suite := &Suite{
fmt: r.fmt, fmt: r.fmt,
randomSeed: r.randomSeed, randomSeed: r.randomSeed,
stopOnFailure: r.stopOnFailure, stopOnFailure: r.stopOnFailure,
strict: r.strict, strict: r.strict,
features: []*feature{feat}, features: []*feature{feat},
storage: r.storage,
} }
suite.fmt = r.fmt
if useFmtCopy { if useFmtCopy {
fmtCopy = formatterFn() fmtCopy = formatterFn()
suite.fmt = fmtCopy suite.fmt = fmtCopy
@ -107,11 +112,13 @@ func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool
} }
suite.run() suite.run()
if suite.failed { if suite.failed {
copyLock.Lock() copyLock.Lock()
*fail = true *fail = true
copyLock.Unlock() copyLock.Unlock()
} }
if useFmtCopy { if useFmtCopy {
copyLock.Lock() copyLock.Lock()

Просмотреть файл

@ -1,6 +1,8 @@
package godog package godog
import ( import (
"fmt"
"github.com/cucumber/messages-go/v10" "github.com/cucumber/messages-go/v10"
"github.com/hashicorp/go-memdb" "github.com/hashicorp/go-memdb"
) )
@ -9,11 +11,20 @@ const (
writeMode bool = true writeMode bool = true
readMode bool = false readMode bool = false
tablePickle string = "pickle" tablePickle string = "pickle"
tablePickleIndexID string = "id" tablePickleIndexID string = "id"
tablePickleIndexURI string = "uri"
tablePickleStep string = "pickle_step" tablePickleStep string = "pickle_step"
tablePickleStepIndexID string = "id" tablePickleStepIndexID string = "id"
tablePickleResult string = "pickle_result"
tablePickleResultIndexPickleID string = "id"
tablePickleStepResult string = "pickle_step_result"
tablePickleStepResultIndexPickleStepID string = "id"
tablePickleStepResultIndexPickleID string = "pickle_id"
tablePickleStepResultIndexStatus string = "status"
) )
type storage struct { type storage struct {
@ -32,6 +43,11 @@ func newStorage() *storage {
Unique: true, Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "Id"}, Indexer: &memdb.StringFieldIndex{Field: "Id"},
}, },
tablePickleIndexURI: {
Name: tablePickleIndexURI,
Unique: false,
Indexer: &memdb.StringFieldIndex{Field: "Uri"},
},
}, },
}, },
tablePickleStep: { tablePickleStep: {
@ -44,6 +60,36 @@ func newStorage() *storage {
}, },
}, },
}, },
tablePickleResult: {
Name: tablePickleResult,
Indexes: map[string]*memdb.IndexSchema{
tablePickleResultIndexPickleID: {
Name: tablePickleResultIndexPickleID,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "PickleID"},
},
},
},
tablePickleStepResult: {
Name: tablePickleStepResult,
Indexes: map[string]*memdb.IndexSchema{
tablePickleStepResultIndexPickleStepID: {
Name: tablePickleStepResultIndexPickleStepID,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "PickleStepID"},
},
tablePickleStepResultIndexPickleID: {
Name: tablePickleStepResultIndexPickleID,
Unique: false,
Indexer: &memdb.StringFieldIndex{Field: "PickleID"},
},
tablePickleStepResultIndexStatus: {
Name: tablePickleStepResultIndexStatus,
Unique: false,
Indexer: &memdb.IntFieldIndex{Field: "Status"},
},
},
},
}, },
} }
@ -56,21 +102,20 @@ func newStorage() *storage {
return &storage{db} return &storage{db}
} }
func (s *storage) mustInsertPickle(p *messages.Pickle) (err error) { func (s *storage) mustInsertPickle(p *messages.Pickle) {
txn := s.db.Txn(writeMode) txn := s.db.Txn(writeMode)
if err = txn.Insert(tablePickle, p); err != nil { if err := txn.Insert(tablePickle, p); err != nil {
panic(err) panic(err)
} }
for _, step := range p.Steps { for _, step := range p.Steps {
if err = txn.Insert(tablePickleStep, step); err != nil { if err := txn.Insert(tablePickleStep, step); err != nil {
panic(err) panic(err)
} }
} }
txn.Commit() txn.Commit()
return
} }
func (s *storage) mustGetPickle(id string) *messages.Pickle { func (s *storage) mustGetPickle(id string) *messages.Pickle {
@ -88,6 +133,22 @@ func (s *storage) mustGetPickle(id string) *messages.Pickle {
return v.(*messages.Pickle) return v.(*messages.Pickle)
} }
func (s *storage) mustGetPickles(uri string) (ps []*messages.Pickle) {
txn := s.db.Txn(readMode)
defer txn.Abort()
it, err := txn.Get(tablePickle, tablePickleIndexURI, uri)
if err != nil {
panic(err)
}
for v := it.Next(); v != nil; v = it.Next() {
ps = append(ps, v.(*messages.Pickle))
}
return
}
func (s *storage) mustGetPickleStep(id string) *messages.Pickle_PickleStep { func (s *storage) mustGetPickleStep(id string) *messages.Pickle_PickleStep {
txn := s.db.Txn(readMode) txn := s.db.Txn(readMode)
defer txn.Abort() defer txn.Abort()
@ -102,3 +163,109 @@ func (s *storage) mustGetPickleStep(id string) *messages.Pickle_PickleStep {
return v.(*messages.Pickle_PickleStep) return v.(*messages.Pickle_PickleStep)
} }
func (s *storage) mustInsertPickleResult(pr pickleResult) {
txn := s.db.Txn(writeMode)
if err := txn.Insert(tablePickleResult, pr); err != nil {
panic(err)
}
txn.Commit()
}
func (s *storage) mustInsertPickleStepResult(psr pickleStepResult) {
txn := s.db.Txn(writeMode)
if err := txn.Insert(tablePickleStepResult, psr); err != nil {
panic(err)
}
txn.Commit()
}
func (s *storage) mustGetPickleResult(id string) pickleResult {
pr, err := s.getPickleResult(id)
if err != nil {
panic(err)
}
return pr
}
func (s *storage) getPickleResult(id string) (_ pickleResult, err error) {
txn := s.db.Txn(readMode)
defer txn.Abort()
v, err := txn.First(tablePickleResult, tablePickleResultIndexPickleID, id)
if err != nil {
return
} else if v == nil {
err = fmt.Errorf("Couldn't find pickle result with ID: %s", id)
return
}
return v.(pickleResult), nil
}
func (s *storage) mustGetPickleResults() (prs []pickleResult) {
txn := s.db.Txn(readMode)
defer txn.Abort()
it, err := txn.Get(tablePickleResult, tablePickleResultIndexPickleID)
if err != nil {
panic(err)
}
for v := it.Next(); v != nil; v = it.Next() {
prs = append(prs, v.(pickleResult))
}
return prs
}
func (s *storage) mustGetPickleStepResult(id string) pickleStepResult {
txn := s.db.Txn(readMode)
defer txn.Abort()
v, err := txn.First(tablePickleStepResult, tablePickleStepResultIndexPickleStepID, id)
if err != nil {
panic(err)
} else if v == nil {
panic("Couldn't find pickle step result with ID: " + id)
}
return v.(pickleStepResult)
}
func (s *storage) mustGetPickleStepResultsByPickleID(pickleID string) (psrs []pickleStepResult) {
txn := s.db.Txn(readMode)
defer txn.Abort()
it, err := txn.Get(tablePickleStepResult, tablePickleStepResultIndexPickleID, pickleID)
if err != nil {
panic(err)
}
for v := it.Next(); v != nil; v = it.Next() {
psrs = append(psrs, v.(pickleStepResult))
}
return psrs
}
func (s *storage) mustGetPickleStepResultsByStatus(status stepResultStatus) (psrs []pickleStepResult) {
txn := s.db.Txn(readMode)
defer txn.Abort()
it, err := txn.Get(tablePickleStepResult, tablePickleStepResultIndexStatus, status)
if err != nil {
panic(err)
}
for v := it.Next(); v != nil; v = it.Next() {
psrs = append(psrs, v.(pickleStepResult))
}
return psrs
}

138
suite.go
Просмотреть файл

@ -23,12 +23,10 @@ var typeOfBytes = reflect.TypeOf([]byte(nil))
type feature struct { type feature struct {
*messages.GherkinDocument *messages.GherkinDocument
pickles []*messages.Pickle pickles []*messages.Pickle
pickleResults []*pickleResult
time time.Time time time.Time
content []byte content []byte
path string
order int order int
} }
@ -100,51 +98,44 @@ func (f feature) startedAt() time.Time {
return f.time return f.time
} }
func (f feature) finishedAt() time.Time {
if len(f.pickleResults) == 0 {
return f.startedAt()
}
return f.pickleResults[len(f.pickleResults)-1].finishedAt()
}
func (f feature) appendStepResult(s *stepResult) {
pickles := f.pickleResults[len(f.pickleResults)-1]
pickles.stepResults = append(pickles.stepResults, s)
}
func (f feature) lastPickleResult() *pickleResult {
return f.pickleResults[len(f.pickleResults)-1]
}
func (f feature) lastStepResult() *stepResult {
last := f.lastPickleResult()
return last.stepResults[len(last.stepResults)-1]
}
type sortByName []*feature type sortByName []*feature
func (s sortByName) Len() int { return len(s) } func (s sortByName) Len() int { return len(s) }
func (s sortByName) Less(i, j int) bool { return s[i].Feature.Name < s[j].Feature.Name } func (s sortByName) Less(i, j int) bool { return s[i].Feature.Name < s[j].Feature.Name }
func (s sortByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s sortByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type pickleResult struct { type sortPicklesByID []*messages.Pickle
pickleID string
time time.Time func (s sortPicklesByID) Len() int { return len(s) }
stepResults []*stepResult func (s sortPicklesByID) Less(i, j int) bool {
iID := mustConvertStringToInt(s[i].Id)
jID := mustConvertStringToInt(s[j].Id)
return iID < jID
} }
func (s sortPicklesByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s pickleResult) startedAt() time.Time { type sortPickleStepResultsByPickleStepID []pickleStepResult
return s.time
func (s sortPickleStepResultsByPickleStepID) Len() int { return len(s) }
func (s sortPickleStepResultsByPickleStepID) Less(i, j int) bool {
iID := mustConvertStringToInt(s[i].PickleStepID)
jID := mustConvertStringToInt(s[j].PickleStepID)
return iID < jID
} }
func (s sortPickleStepResultsByPickleStepID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s pickleResult) finishedAt() time.Time { func mustConvertStringToInt(s string) int {
if len(s.stepResults) == 0 { i, err := strconv.Atoi(s)
return s.startedAt() if err != nil {
panic(err)
} }
return s.stepResults[len(s.stepResults)-1].time return i
}
type pickleResult struct {
PickleID string
StartedAt time.Time
} }
// ErrUndefined is returned in case if step definition was not found // ErrUndefined is returned in case if step definition was not found
@ -172,7 +163,9 @@ var ErrPending = fmt.Errorf("step implementation is pending")
type Suite struct { type Suite struct {
steps []*StepDefinition steps []*StepDefinition
features []*feature features []*feature
fmt Formatter
fmt Formatter
storage *storage
failed bool failed bool
randomSeed int64 randomSeed int64
@ -417,12 +410,24 @@ func (s *Suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleSte
return return
} }
sr := newStepResult(pickle.Id, step.Id, match)
switch err { switch err {
case nil: case nil:
sr.Status = passed
s.storage.mustInsertPickleStepResult(sr)
s.fmt.Passed(pickle, step, match) s.fmt.Passed(pickle, step, match)
case ErrPending: case ErrPending:
sr.Status = pending
s.storage.mustInsertPickleStepResult(sr)
s.fmt.Pending(pickle, step, match) s.fmt.Pending(pickle, step, match)
default: default:
sr.Status = failed
sr.err = err
s.storage.mustInsertPickleStepResult(sr)
s.fmt.Failed(pickle, step, match, err) s.fmt.Failed(pickle, step, match, err)
} }
@ -445,11 +450,20 @@ func (s *Suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleSte
undefined: undef, undefined: undef,
} }
} }
sr := newStepResult(pickle.Id, step.Id, match)
sr.Status = undefined
s.storage.mustInsertPickleStepResult(sr)
s.fmt.Undefined(pickle, step, match) s.fmt.Undefined(pickle, step, match)
return ErrUndefined return ErrUndefined
} }
if prevStepErr != nil { if prevStepErr != nil {
sr := newStepResult(pickle.Id, step.Id, match)
sr.Status = skipped
s.storage.mustInsertPickleStepResult(sr)
s.fmt.Skipped(pickle, step, match) s.fmt.Skipped(pickle, step, match)
return nil return nil
} }
@ -575,7 +589,7 @@ func (s *Suite) runFeature(f *feature) {
} }
} }
s.fmt.Feature(f.GherkinDocument, f.path, f.content) s.fmt.Feature(f.GherkinDocument, f.Uri, f.content)
defer func() { defer func() {
if !isEmptyFeature(f.pickles) { if !isEmptyFeature(f.pickles) {
@ -613,6 +627,9 @@ func isEmptyFeature(pickles []*messages.Pickle) bool {
func (s *Suite) runPickle(pickle *messages.Pickle) (err error) { func (s *Suite) runPickle(pickle *messages.Pickle) (err error) {
if len(pickle.Steps) == 0 { if len(pickle.Steps) == 0 {
pr := pickleResult{PickleID: pickle.Id, StartedAt: timeNowFunc()}
s.storage.mustInsertPickleResult(pr)
s.fmt.Pickle(pickle) s.fmt.Pickle(pickle)
return ErrUndefined return ErrUndefined
} }
@ -622,6 +639,9 @@ func (s *Suite) runPickle(pickle *messages.Pickle) (err error) {
f(pickle) f(pickle)
} }
pr := pickleResult{PickleID: pickle.Id, StartedAt: timeNowFunc()}
s.storage.mustInsertPickleResult(pr)
s.fmt.Pickle(pickle) s.fmt.Pickle(pickle)
// scenario // scenario
@ -673,6 +693,7 @@ func parseFeatureFile(path string, newIDFunc func() string) (*feature, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer reader.Close() defer reader.Close()
var buf bytes.Buffer var buf bytes.Buffer
@ -681,14 +702,11 @@ func parseFeatureFile(path string, newIDFunc func() string) (*feature, error) {
return nil, fmt.Errorf("%s - %v", path, err) return nil, fmt.Errorf("%s - %v", path, err)
} }
gherkinDocument.Uri = path
pickles := gherkin.Pickles(*gherkinDocument, path, newIDFunc) pickles := gherkin.Pickles(*gherkinDocument, path, newIDFunc)
return &feature{ f := feature{GherkinDocument: gherkinDocument, pickles: pickles, content: buf.Bytes()}
GherkinDocument: gherkinDocument, return &f, nil
pickles: pickles,
content: buf.Bytes(),
path: path,
}, nil
} }
func parseFeatureDir(dir string, newIDFunc func() string) ([]*feature, error) { func parseFeatureDir(dir string, newIDFunc func() string) ([]*feature, error) {
@ -710,6 +728,7 @@ func parseFeatureDir(dir string, newIDFunc func() string) ([]*feature, error) {
if err != nil { if err != nil {
return err return err
} }
features = append(features, feat) features = append(features, feat)
return nil return nil
}) })
@ -751,10 +770,12 @@ func parsePath(path string) ([]*feature, error) {
} }
func parseFeatures(filter string, paths []string) ([]*feature, error) { func parseFeatures(filter string, paths []string) ([]*feature, error) {
byPath := make(map[string]*feature)
var order int var order int
uniqueFeatureURI := make(map[string]*feature)
for _, path := range paths { for _, path := range paths {
feats, err := parsePath(path) feats, err := parsePath(path)
switch { switch {
case os.IsNotExist(err): case os.IsNotExist(err):
return nil, fmt.Errorf(`feature path "%s" is not available`, path) return nil, fmt.Errorf(`feature path "%s" is not available`, path)
@ -765,50 +786,49 @@ func parseFeatures(filter string, paths []string) ([]*feature, error) {
} }
for _, ft := range feats { for _, ft := range feats {
if _, duplicate := byPath[ft.path]; duplicate { if _, duplicate := uniqueFeatureURI[ft.Uri]; duplicate {
continue continue
} }
ft.order = order ft.order = order
order++ order++
byPath[ft.path] = ft uniqueFeatureURI[ft.Uri] = ft
} }
} }
return filterFeatures(filter, byPath), nil return filterFeatures(filter, uniqueFeatureURI), nil
} }
type sortByOrderGiven []*feature type sortFeaturesByOrder []*feature
func (s sortByOrderGiven) Len() int { return len(s) } func (s sortFeaturesByOrder) Len() int { return len(s) }
func (s sortByOrderGiven) Less(i, j int) bool { return s[i].order < s[j].order } func (s sortFeaturesByOrder) Less(i, j int) bool { return s[i].order < s[j].order }
func (s sortByOrderGiven) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s sortFeaturesByOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func filterFeatures(tags string, collected map[string]*feature) (features []*feature) { func filterFeatures(tags string, collected map[string]*feature) (features []*feature) {
for _, ft := range collected { for _, ft := range collected {
applyTagFilter(tags, ft) ft.pickles = applyTagFilter(tags, ft.pickles)
if ft.Feature != nil { if ft.Feature != nil {
features = append(features, ft) features = append(features, ft)
} }
} }
sort.Sort(sortByOrderGiven(features)) sort.Sort(sortFeaturesByOrder(features))
return features return features
} }
func applyTagFilter(tags string, ft *feature) { func applyTagFilter(tags string, pickles []*messages.Pickle) (result []*messages.Pickle) {
if len(tags) == 0 { if len(tags) == 0 {
return return pickles
} }
var pickles []*messages.Pickle for _, pickle := range pickles {
for _, pickle := range ft.pickles {
if matchesTags(tags, pickle.Tags) { if matchesTags(tags, pickle.Tags) {
pickles = append(pickles, pickle) result = append(result, pickle)
} }
} }
ft.pickles = pickles return
} }

Просмотреть файл

@ -57,7 +57,6 @@ func SuiteContext(s *Suite, additionalContextInitializers ...func(suite *Suite))
s.Step(`^(?:a )?failing step`, c.aFailingStep) s.Step(`^(?:a )?failing step`, c.aFailingStep)
s.Step(`^this step should fail`, c.aFailingStep) s.Step(`^this step should fail`, c.aFailingStep)
s.Step(`^the following steps? should be (passed|failed|skipped|undefined|pending):`, c.followingStepsShouldHave) s.Step(`^the following steps? should be (passed|failed|skipped|undefined|pending):`, c.followingStepsShouldHave)
s.Step(`^all steps should (?:be|have|have been) (passed|failed|skipped|undefined|pending)$`, c.allStepsShouldHave)
s.Step(`^the undefined step snippets should be:$`, c.theUndefinedStepSnippetsShouldBe) s.Step(`^the undefined step snippets should be:$`, c.theUndefinedStepSnippetsShouldBe)
// event stream // event stream
@ -180,18 +179,18 @@ func (s *suiteContext) iRunFeatureSuiteWithTags(tags string) error {
} }
for _, feat := range s.testedSuite.features { for _, feat := range s.testedSuite.features {
applyTagFilter(tags, feat) feat.pickles = applyTagFilter(tags, feat.pickles)
} }
st := newStorage() s.testedSuite.storage = newStorage()
for _, feat := range s.testedSuite.features { for _, feat := range s.testedSuite.features {
for _, pickle := range feat.pickles { for _, pickle := range feat.pickles {
st.mustInsertPickle(pickle) s.testedSuite.storage.mustInsertPickle(pickle)
} }
} }
fmt := newBaseFmt("godog", &s.out) fmt := newBaseFmt("godog", &s.out)
fmt.setStorage(st) fmt.setStorage(s.testedSuite.storage)
s.testedSuite.fmt = fmt s.testedSuite.fmt = fmt
s.testedSuite.fmt.TestRunStarted() s.testedSuite.fmt.TestRunStarted()
@ -211,16 +210,16 @@ func (s *suiteContext) iRunFeatureSuiteWithFormatter(name string) error {
return fmt.Errorf(`formatter "%s" is not available`, name) return fmt.Errorf(`formatter "%s" is not available`, name)
} }
st := newStorage() s.testedSuite.storage = newStorage()
for _, feat := range s.testedSuite.features { for _, feat := range s.testedSuite.features {
for _, pickle := range feat.pickles { for _, pickle := range feat.pickles {
st.mustInsertPickle(pickle) s.testedSuite.storage.mustInsertPickle(pickle)
} }
} }
s.testedSuite.fmt = f("godog", colors.Uncolored(&s.out)) s.testedSuite.fmt = f("godog", colors.Uncolored(&s.out))
if fmt, ok := s.testedSuite.fmt.(storageFormatter); ok { if fmt, ok := s.testedSuite.fmt.(storageFormatter); ok {
fmt.setStorage(st) fmt.setStorage(s.testedSuite.storage)
} }
s.testedSuite.fmt.TestRunStarted() s.testedSuite.fmt.TestRunStarted()
@ -294,28 +293,28 @@ func (s *suiteContext) followingStepsShouldHave(status string, steps *DocString)
switch status { switch status {
case "passed": case "passed":
for _, st := range f.findStepResults(passed) { for _, st := range f.storage.mustGetPickleStepResultsByStatus(passed) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID) pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text) actual = append(actual, pickleStep.Text)
} }
case "failed": case "failed":
for _, st := range f.findStepResults(failed) { for _, st := range f.storage.mustGetPickleStepResultsByStatus(failed) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID) pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text) actual = append(actual, pickleStep.Text)
} }
case "skipped": case "skipped":
for _, st := range f.findStepResults(skipped) { for _, st := range f.storage.mustGetPickleStepResultsByStatus(skipped) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID) pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text) actual = append(actual, pickleStep.Text)
} }
case "undefined": case "undefined":
for _, st := range f.findStepResults(undefined) { for _, st := range f.storage.mustGetPickleStepResultsByStatus(undefined) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID) pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text) actual = append(actual, pickleStep.Text)
} }
case "pending": case "pending":
for _, st := range f.findStepResults(pending) { for _, st := range f.storage.mustGetPickleStepResultsByStatus(pending) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID) pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text) actual = append(actual, pickleStep.Text)
} }
default: default:
@ -356,42 +355,6 @@ func (s *suiteContext) followingStepsShouldHave(status string, steps *DocString)
return fmt.Errorf("the steps: %s - are not %s", strings.Join(unmatched, ", "), status) return fmt.Errorf("the steps: %s - are not %s", strings.Join(unmatched, ", "), status)
} }
func (s *suiteContext) allStepsShouldHave(status string) error {
f, ok := s.testedSuite.fmt.(*basefmt)
if !ok {
return fmt.Errorf("this step requires *basefmt, but there is: %T", s.testedSuite.fmt)
}
total := len(f.findStepResults(passed)) +
len(f.findStepResults(failed)) +
len(f.findStepResults(skipped)) +
len(f.findStepResults(undefined)) +
len(f.findStepResults(pending))
var actual int
switch status {
case "passed":
actual = len(f.findStepResults(passed))
case "failed":
actual = len(f.findStepResults(failed))
case "skipped":
actual = len(f.findStepResults(skipped))
case "undefined":
actual = len(f.findStepResults(undefined))
case "pending":
actual = len(f.findStepResults(pending))
default:
return fmt.Errorf("unexpected step status wanted: %s", status)
}
if total > actual {
return fmt.Errorf("number of expected %s steps: %d is less than actual %s steps: %d", status, total, status, actual)
}
return nil
}
func (s *suiteContext) iAmListeningToSuiteEvents() error { func (s *suiteContext) iAmListeningToSuiteEvents() error {
s.testedSuite.BeforeSuite(func() { s.testedSuite.BeforeSuite(func() {
s.events = append(s.events, &firedEvent{"BeforeSuite", []interface{}{}}) s.events = append(s.events, &firedEvent{"BeforeSuite", []interface{}{}})
@ -435,8 +398,10 @@ func (s *suiteContext) aFailingStep() error {
// parse a given feature file body as a feature // parse a given feature file body as a feature
func (s *suiteContext) aFeatureFile(path string, body *DocString) error { func (s *suiteContext) aFeatureFile(path string, body *DocString) error {
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(body.Content), (&messages.Incrementing{}).NewId) gd, err := gherkin.ParseGherkinDocument(strings.NewReader(body.Content), (&messages.Incrementing{}).NewId)
gd.Uri = path
pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
s.testedSuite.features = append(s.testedSuite.features, &feature{GherkinDocument: gd, pickles: pickles, path: path}) s.testedSuite.features = append(s.testedSuite.features, &feature{GherkinDocument: gd, pickles: pickles})
return err return err
} }
@ -479,7 +444,7 @@ func (s *suiteContext) iShouldHaveNumFeatureFiles(num int, files *DocString) err
var actual []string var actual []string
for _, ft := range s.testedSuite.features { for _, ft := range s.testedSuite.features {
actual = append(actual, ft.path) actual = append(actual, ft.Uri)
} }
if len(expected) != len(actual) { if len(expected) != len(actual) {

Просмотреть файл

@ -41,7 +41,6 @@ func InitializeScenario(ctx *ScenarioContext) {
ctx.Step(`^(?:a )?failing step`, tc.aFailingStep) ctx.Step(`^(?:a )?failing step`, tc.aFailingStep)
ctx.Step(`^this step should fail`, tc.aFailingStep) ctx.Step(`^this step should fail`, tc.aFailingStep)
ctx.Step(`^the following steps? should be (passed|failed|skipped|undefined|pending):`, tc.followingStepsShouldHave) ctx.Step(`^the following steps? should be (passed|failed|skipped|undefined|pending):`, tc.followingStepsShouldHave)
ctx.Step(`^all steps should (?:be|have|have been) (passed|failed|skipped|undefined|pending)$`, tc.allStepsShouldHave)
ctx.Step(`^the undefined step snippets should be:$`, tc.theUndefinedStepSnippetsShouldBe) ctx.Step(`^the undefined step snippets should be:$`, tc.theUndefinedStepSnippetsShouldBe)
// event stream // event stream
@ -142,18 +141,18 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTags(tags string) error {
} }
for _, feat := range tc.testedSuite.features { for _, feat := range tc.testedSuite.features {
applyTagFilter(tags, feat) feat.pickles = applyTagFilter(tags, feat.pickles)
} }
st := newStorage() tc.testedSuite.storage = newStorage()
for _, feat := range tc.testedSuite.features { for _, feat := range tc.testedSuite.features {
for _, pickle := range feat.pickles { for _, pickle := range feat.pickles {
st.mustInsertPickle(pickle) tc.testedSuite.storage.mustInsertPickle(pickle)
} }
} }
fmt := newBaseFmt("godog", &tc.out) fmt := newBaseFmt("godog", &tc.out)
fmt.setStorage(st) fmt.setStorage(tc.testedSuite.storage)
tc.testedSuite.fmt = fmt tc.testedSuite.fmt = fmt
tc.testedSuite.fmt.TestRunStarted() tc.testedSuite.fmt.TestRunStarted()
@ -173,16 +172,16 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithFormatter(name string) erro
return fmt.Errorf(`formatter "%s" is not available`, name) return fmt.Errorf(`formatter "%s" is not available`, name)
} }
st := newStorage() tc.testedSuite.storage = newStorage()
for _, feat := range tc.testedSuite.features { for _, feat := range tc.testedSuite.features {
for _, pickle := range feat.pickles { for _, pickle := range feat.pickles {
st.mustInsertPickle(pickle) tc.testedSuite.storage.mustInsertPickle(pickle)
} }
} }
tc.testedSuite.fmt = f("godog", colors.Uncolored(&tc.out)) tc.testedSuite.fmt = f("godog", colors.Uncolored(&tc.out))
if fmt, ok := tc.testedSuite.fmt.(storageFormatter); ok { if fmt, ok := tc.testedSuite.fmt.(storageFormatter); ok {
fmt.setStorage(st) fmt.setStorage(tc.testedSuite.storage)
} }
tc.testedSuite.fmt.TestRunStarted() tc.testedSuite.fmt.TestRunStarted()
@ -256,28 +255,28 @@ func (tc *godogFeaturesScenario) followingStepsShouldHave(status string, steps *
switch status { switch status {
case "passed": case "passed":
for _, st := range f.findStepResults(passed) { for _, st := range f.storage.mustGetPickleStepResultsByStatus(passed) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID) pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text) actual = append(actual, pickleStep.Text)
} }
case "failed": case "failed":
for _, st := range f.findStepResults(failed) { for _, st := range f.storage.mustGetPickleStepResultsByStatus(failed) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID) pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text) actual = append(actual, pickleStep.Text)
} }
case "skipped": case "skipped":
for _, st := range f.findStepResults(skipped) { for _, st := range f.storage.mustGetPickleStepResultsByStatus(skipped) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID) pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text) actual = append(actual, pickleStep.Text)
} }
case "undefined": case "undefined":
for _, st := range f.findStepResults(undefined) { for _, st := range f.storage.mustGetPickleStepResultsByStatus(undefined) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID) pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text) actual = append(actual, pickleStep.Text)
} }
case "pending": case "pending":
for _, st := range f.findStepResults(pending) { for _, st := range f.storage.mustGetPickleStepResultsByStatus(pending) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID) pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text) actual = append(actual, pickleStep.Text)
} }
default: default:
@ -318,42 +317,6 @@ func (tc *godogFeaturesScenario) followingStepsShouldHave(status string, steps *
return fmt.Errorf("the steps: %s - are not %s", strings.Join(unmatched, ", "), status) return fmt.Errorf("the steps: %s - are not %s", strings.Join(unmatched, ", "), status)
} }
func (tc *godogFeaturesScenario) allStepsShouldHave(status string) error {
f, ok := tc.testedSuite.fmt.(*basefmt)
if !ok {
return fmt.Errorf("this step requires *basefmt, but there is: %T", tc.testedSuite.fmt)
}
total := len(f.findStepResults(passed)) +
len(f.findStepResults(failed)) +
len(f.findStepResults(skipped)) +
len(f.findStepResults(undefined)) +
len(f.findStepResults(pending))
var actual int
switch status {
case "passed":
actual = len(f.findStepResults(passed))
case "failed":
actual = len(f.findStepResults(failed))
case "skipped":
actual = len(f.findStepResults(skipped))
case "undefined":
actual = len(f.findStepResults(undefined))
case "pending":
actual = len(f.findStepResults(pending))
default:
return fmt.Errorf("unexpected step status wanted: %s", status)
}
if total > actual {
return fmt.Errorf("number of expected %s steps: %d is less than actual %s steps: %d", status, total, status, actual)
}
return nil
}
func (tc *godogFeaturesScenario) iAmListeningToSuiteEvents() error { func (tc *godogFeaturesScenario) iAmListeningToSuiteEvents() error {
tc.testedSuite.BeforeSuite(func() { tc.testedSuite.BeforeSuite(func() {
tc.events = append(tc.events, &firedEvent{"BeforeSuite", []interface{}{}}) tc.events = append(tc.events, &firedEvent{"BeforeSuite", []interface{}{}})
@ -397,8 +360,10 @@ func (tc *godogFeaturesScenario) aFailingStep() error {
// parse a given feature file body as a feature // parse a given feature file body as a feature
func (tc *godogFeaturesScenario) aFeatureFile(path string, body *DocString) error { func (tc *godogFeaturesScenario) aFeatureFile(path string, body *DocString) error {
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(body.Content), (&messages.Incrementing{}).NewId) gd, err := gherkin.ParseGherkinDocument(strings.NewReader(body.Content), (&messages.Incrementing{}).NewId)
gd.Uri = path
pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
tc.testedSuite.features = append(tc.testedSuite.features, &feature{GherkinDocument: gd, pickles: pickles, path: path}) tc.testedSuite.features = append(tc.testedSuite.features, &feature{GherkinDocument: gd, pickles: pickles})
return err return err
} }
@ -441,7 +406,7 @@ func (tc *godogFeaturesScenario) iShouldHaveNumFeatureFiles(num int, files *DocS
var actual []string var actual []string
for _, ft := range tc.testedSuite.features { for _, ft := range tc.testedSuite.features {
actual = append(actual, ft.path) actual = append(actual, ft.Uri)
} }
if len(expected) != len(actual) { if len(expected) != len(actual) {

Просмотреть файл

@ -33,6 +33,11 @@ func Test_TestContext(t *testing.T) {
}, },
} }
r.storage = newStorage()
for _, pickle := range pickles {
r.storage.mustInsertPickle(pickle)
}
failed := r.concurrent(1, func() Formatter { return progressFunc("progress", w) }) failed := r.concurrent(1, func() Formatter { return progressFunc("progress", w) })
require.False(t, failed) require.False(t, failed)