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 {
status stepResultStatus
time time.Time
err error
type pickleStepResult struct {
Status stepResultStatus
finishedAt time.Time
err error
pickleID string
pickleStepID string
PickleID string
PickleStepID string
def *StepDefinition
}
func newStepResult(pickleID, pickleStepID string, match *StepDefinition) *stepResult {
return &stepResult{time: timeNowFunc(), pickleID: pickleID, pickleStepID: pickleStepID, def: match}
func newStepResult(pickleID, pickleStepID string, match *StepDefinition) pickleStepResult {
return pickleStepResult{finishedAt: timeNowFunc(), PickleID: pickleID, PickleStepID: pickleStepID, def: match}
}
func newBaseFmt(suite string, out io.Writer) *basefmt {
return &basefmt{
suiteName: suite,
started: timeNowFunc(),
startedAt: timeNowFunc(),
indent: 2,
out: out,
lock: new(sync.Mutex),
@ -173,8 +173,8 @@ type basefmt struct {
storage *storage
started time.Time
features []*feature
startedAt time.Time
features []*feature
firstFeature *bool
lock *sync.Mutex
@ -188,10 +188,6 @@ func (f *basefmt) lastFeature() *feature {
return f.features[len(f.features)-1]
}
func (f *basefmt) lastStepResult() *stepResult {
return f.lastFeature().lastStepResult()
}
func (f *basefmt) findFeature(scenarioAstID string) *feature {
for _, ft := range f.features {
if sc := ft.findScenario(scenarioAstID); sc != nil {
@ -250,15 +246,7 @@ func (f *basefmt) TestRunStarted() {
f.firstFeature = &firstFeature
}
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) Pickle(p *messages.Pickle) {}
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.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) {
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) {
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) {
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) {
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) {
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() {
var totalSc, passedSc, undefinedSc int
var totalSt, passedSt, failedSt, skippedSt, pendingSt, undefinedSt int
for _, feat := range f.features {
for _, pr := range feat.pickleResults {
var prStatus stepResultStatus
totalSc++
pickleResults := f.storage.mustGetPickleResults()
for _, pr := range pickleResults {
var prStatus stepResultStatus
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
undefinedSt++
case pending:
prStatus = pending
pendingSt++
}
}
for _, sr := range pr.stepResults {
totalSt++
switch sr.status {
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++
}
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, parts...)
elapsed := timeNowFunc().Sub(f.started)
elapsed := timeNowFunc().Sub(f.startedAt)
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 {
undefinedStepResults := f.findStepResults(undefined)
undefinedStepResults := f.storage.mustGetPickleStepResultsByStatus(undefined)
if len(undefinedStepResults) == 0 {
return ""
}
@ -461,7 +401,7 @@ func (f *basefmt) snippets() string {
var snips []undefinedSnippet
// build snippets
for _, u := range undefinedStepResults {
pickleStep := f.storage.mustGetPickleStep(u.pickleStepID)
pickleStep := f.storage.mustGetPickleStep(u.PickleStepID)
steps := []string{pickleStep.Text}
arg := pickleStep.Argument

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

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

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

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

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

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

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

@ -4,6 +4,7 @@ import (
"fmt"
"io"
"regexp"
"sort"
"strings"
"unicode/utf8"
@ -61,7 +62,7 @@ func (f *pretty) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.lock.Lock()
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) {
@ -70,7 +71,7 @@ func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock()
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) {
@ -79,7 +80,7 @@ func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.lock.Lock()
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) {
@ -88,7 +89,7 @@ func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.lock.Lock()
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) {
@ -97,7 +98,7 @@ func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock()
defer f.lock.Unlock()
f.printStep(pickle, f.lastStepResult())
f.printStep(pickle, step)
}
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) {
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)
}
@ -192,12 +193,15 @@ func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
// Summary sumarize the feature formatter output
func (f *pretty) Summary() {
failedStepResults := f.findStepResults(failed)
failedStepResults := f.storage.mustGetPickleStepResultsByStatus(failed)
if len(failedStepResults) > 0 {
fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n")
sort.Sort(sortPickleStepResultsByPickleStepID(failedStepResults))
for _, fail := range failedStepResults {
pickle := f.storage.mustGetPickle(fail.pickleID)
pickleStep := f.storage.mustGetPickleStep(fail.pickleStepID)
pickle := f.storage.mustGetPickle(fail.PickleID)
pickleStep := f.storage.mustGetPickleStep(fail.PickleStepID)
feature := f.findFeature(pickle.AstNodeIds[0])
@ -207,8 +211,8 @@ func (f *pretty) Summary() {
astStep := f.findStep(pickleStep.AstNodeIds[0])
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*2)+red(stepDesc)+f.line(feature.path, astStep.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.Uri, astStep.Location))
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
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 {
f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength)
}
@ -237,32 +243,31 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
return
}
lastStep := len(f.lastFeature().lastPickleResult().stepResults) == len(pickle.Steps)
lastStep := len(pickleStepResults) == len(pickle.Steps)
if !lastStep {
// do not print examples unless all steps has finished
return
}
lastPickleResult := f.lastFeature().lastPickleResult()
for _, result := range lastPickleResult.stepResults {
for _, result := range pickleStepResults {
// determine example row status
switch {
case result.status == failed:
case result.Status == failed:
errorMsg = result.err.Error()
clr = result.status.clr()
case result.status == undefined || result.status == pending:
clr = result.status.clr()
case result.status == skipped && clr == nil:
clr = result.Status.clr()
case result.Status == undefined || result.Status == pending:
clr = result.Status.clr()
case result.Status == skipped && clr == nil:
clr = cyan
}
if firstExamplesTable && printExampleHeader {
// 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])
var text = ""
if result.def != nil {
if m := outlinePlaceholderRegexp.FindAllStringIndex(astStep.Text, -1); len(m) > 0 {
var pos int
@ -277,16 +282,13 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
text = cyan(astStep.Text)
}
pickle := f.storage.mustGetPickle(lastPickleResult.pickleID)
_, maxLength := f.scenarioLengths(pickle.AstNodeIds[0])
stepLength := f.lengthPickleStep(astStep.Keyword, astStep.Text)
text += s(maxLength - stepLength)
text += " " + blackb("# "+result.def.definitionID())
} else {
text = cyan(astStep.Text)
}
// print the step outline
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)
}
func (f *pretty) printStep(pickle *messages.Pickle, result *stepResult) {
pickleStep := f.storage.mustGetPickleStep(result.pickleStepID)
func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep) {
astBackground := f.findBackground(pickle.AstNodeIds[0])
astScenario := f.findScenario(pickle.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)
}
astBackgroundStep := backgroundSteps > 0 && backgroundSteps >= len(f.lastFeature().lastPickleResult().stepResults)
pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id)
astBackgroundStep := backgroundSteps > 0 && backgroundSteps >= len(pickleStepResults)
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
}
firstExecutedBackgroundStep := astBackground != nil && len(f.lastFeature().lastPickleResult().stepResults) == 1
firstExecutedBackgroundStep := astBackground != nil && len(pickleStepResults) == 1
if firstExecutedBackgroundStep {
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])
stepLength := f.lengthPickleStep(astStep.Keyword, pickleStep.Text)
firstExecutedScenarioStep := len(f.lastFeature().lastPickleResult().stepResults) == backgroundSteps+1
firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1
if !astBackgroundStep && firstExecutedScenarioStep {
f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength)
}
text := s(f.indent*2) + result.status.clr()(strings.TrimSpace(astStep.Keyword)) + " " + result.status.clr()(pickleStep.Text)
if result.def != nil {
pickleStepResult := f.storage.mustGetPickleStepResult(pickleStep.Id)
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 += blackb("# " + result.def.definitionID())
text += blackb("# " + pickleStepResult.def.definitionID())
}
fmt.Fprintln(f.out, text)
@ -386,11 +398,11 @@ func (f *pretty) printStep(pickle *messages.Pickle, result *stepResult) {
f.printDocString(docString)
}
if result.err != nil {
fmt.Fprintln(f.out, s(f.indent*2)+redb(fmt.Sprintf("%+v", result.err)))
if pickleStepResult.err != nil {
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"))
}
}

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

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

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

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

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

@ -1,6 +1,8 @@
package godog
import (
"fmt"
"github.com/cucumber/messages-go/v10"
"github.com/hashicorp/go-memdb"
)
@ -9,11 +11,20 @@ const (
writeMode bool = true
readMode bool = false
tablePickle string = "pickle"
tablePickleIndexID string = "id"
tablePickle string = "pickle"
tablePickleIndexID string = "id"
tablePickleIndexURI string = "uri"
tablePickleStep string = "pickle_step"
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 {
@ -32,6 +43,11 @@ func newStorage() *storage {
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "Id"},
},
tablePickleIndexURI: {
Name: tablePickleIndexURI,
Unique: false,
Indexer: &memdb.StringFieldIndex{Field: "Uri"},
},
},
},
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}
}
func (s *storage) mustInsertPickle(p *messages.Pickle) (err error) {
func (s *storage) mustInsertPickle(p *messages.Pickle) {
txn := s.db.Txn(writeMode)
if err = txn.Insert(tablePickle, p); err != nil {
if err := txn.Insert(tablePickle, p); err != nil {
panic(err)
}
for _, step := range p.Steps {
if err = txn.Insert(tablePickleStep, step); err != nil {
if err := txn.Insert(tablePickleStep, step); err != nil {
panic(err)
}
}
txn.Commit()
return
}
func (s *storage) mustGetPickle(id string) *messages.Pickle {
@ -88,6 +133,22 @@ func (s *storage) mustGetPickle(id string) *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 {
txn := s.db.Txn(readMode)
defer txn.Abort()
@ -102,3 +163,109 @@ func (s *storage) mustGetPickleStep(id string) *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 {
*messages.GherkinDocument
pickles []*messages.Pickle
pickleResults []*pickleResult
pickles []*messages.Pickle
time time.Time
content []byte
path string
order int
}
@ -100,51 +98,44 @@ func (f feature) startedAt() time.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
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) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type pickleResult struct {
pickleID string
type sortPicklesByID []*messages.Pickle
time time.Time
stepResults []*stepResult
func (s sortPicklesByID) Len() int { return len(s) }
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 {
return s.time
type sortPickleStepResultsByPickleStepID []pickleStepResult
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 {
if len(s.stepResults) == 0 {
return s.startedAt()
func mustConvertStringToInt(s string) int {
i, err := strconv.Atoi(s)
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
@ -172,7 +163,9 @@ var ErrPending = fmt.Errorf("step implementation is pending")
type Suite struct {
steps []*StepDefinition
features []*feature
fmt Formatter
fmt Formatter
storage *storage
failed bool
randomSeed int64
@ -417,12 +410,24 @@ func (s *Suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleSte
return
}
sr := newStepResult(pickle.Id, step.Id, match)
switch err {
case nil:
sr.Status = passed
s.storage.mustInsertPickleStepResult(sr)
s.fmt.Passed(pickle, step, match)
case ErrPending:
sr.Status = pending
s.storage.mustInsertPickleStepResult(sr)
s.fmt.Pending(pickle, step, match)
default:
sr.Status = failed
sr.err = err
s.storage.mustInsertPickleStepResult(sr)
s.fmt.Failed(pickle, step, match, err)
}
@ -445,11 +450,20 @@ func (s *Suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleSte
undefined: undef,
}
}
sr := newStepResult(pickle.Id, step.Id, match)
sr.Status = undefined
s.storage.mustInsertPickleStepResult(sr)
s.fmt.Undefined(pickle, step, match)
return ErrUndefined
}
if prevStepErr != nil {
sr := newStepResult(pickle.Id, step.Id, match)
sr.Status = skipped
s.storage.mustInsertPickleStepResult(sr)
s.fmt.Skipped(pickle, step, match)
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() {
if !isEmptyFeature(f.pickles) {
@ -613,6 +627,9 @@ func isEmptyFeature(pickles []*messages.Pickle) bool {
func (s *Suite) runPickle(pickle *messages.Pickle) (err error) {
if len(pickle.Steps) == 0 {
pr := pickleResult{PickleID: pickle.Id, StartedAt: timeNowFunc()}
s.storage.mustInsertPickleResult(pr)
s.fmt.Pickle(pickle)
return ErrUndefined
}
@ -622,6 +639,9 @@ func (s *Suite) runPickle(pickle *messages.Pickle) (err error) {
f(pickle)
}
pr := pickleResult{PickleID: pickle.Id, StartedAt: timeNowFunc()}
s.storage.mustInsertPickleResult(pr)
s.fmt.Pickle(pickle)
// scenario
@ -673,6 +693,7 @@ func parseFeatureFile(path string, newIDFunc func() string) (*feature, error) {
if err != nil {
return nil, err
}
defer reader.Close()
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)
}
gherkinDocument.Uri = path
pickles := gherkin.Pickles(*gherkinDocument, path, newIDFunc)
return &feature{
GherkinDocument: gherkinDocument,
pickles: pickles,
content: buf.Bytes(),
path: path,
}, nil
f := feature{GherkinDocument: gherkinDocument, pickles: pickles, content: buf.Bytes()}
return &f, nil
}
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 {
return err
}
features = append(features, feat)
return nil
})
@ -751,10 +770,12 @@ func parsePath(path string) ([]*feature, error) {
}
func parseFeatures(filter string, paths []string) ([]*feature, error) {
byPath := make(map[string]*feature)
var order int
uniqueFeatureURI := make(map[string]*feature)
for _, path := range paths {
feats, err := parsePath(path)
switch {
case os.IsNotExist(err):
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 {
if _, duplicate := byPath[ft.path]; duplicate {
if _, duplicate := uniqueFeatureURI[ft.Uri]; duplicate {
continue
}
ft.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 sortByOrderGiven) 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) Len() int { return len(s) }
func (s sortFeaturesByOrder) Less(i, j int) bool { return s[i].order < s[j].order }
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) {
for _, ft := range collected {
applyTagFilter(tags, ft)
ft.pickles = applyTagFilter(tags, ft.pickles)
if ft.Feature != nil {
features = append(features, ft)
}
}
sort.Sort(sortByOrderGiven(features))
sort.Sort(sortFeaturesByOrder(features))
return features
}
func applyTagFilter(tags string, ft *feature) {
func applyTagFilter(tags string, pickles []*messages.Pickle) (result []*messages.Pickle) {
if len(tags) == 0 {
return
return pickles
}
var pickles []*messages.Pickle
for _, pickle := range ft.pickles {
for _, pickle := range pickles {
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(`^this step should fail`, c.aFailingStep)
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)
// event stream
@ -180,18 +179,18 @@ func (s *suiteContext) iRunFeatureSuiteWithTags(tags string) error {
}
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 _, pickle := range feat.pickles {
st.mustInsertPickle(pickle)
s.testedSuite.storage.mustInsertPickle(pickle)
}
}
fmt := newBaseFmt("godog", &s.out)
fmt.setStorage(st)
fmt.setStorage(s.testedSuite.storage)
s.testedSuite.fmt = fmt
s.testedSuite.fmt.TestRunStarted()
@ -211,16 +210,16 @@ func (s *suiteContext) iRunFeatureSuiteWithFormatter(name string) error {
return fmt.Errorf(`formatter "%s" is not available`, name)
}
st := newStorage()
s.testedSuite.storage = newStorage()
for _, feat := range s.testedSuite.features {
for _, pickle := range feat.pickles {
st.mustInsertPickle(pickle)
s.testedSuite.storage.mustInsertPickle(pickle)
}
}
s.testedSuite.fmt = f("godog", colors.Uncolored(&s.out))
if fmt, ok := s.testedSuite.fmt.(storageFormatter); ok {
fmt.setStorage(st)
fmt.setStorage(s.testedSuite.storage)
}
s.testedSuite.fmt.TestRunStarted()
@ -294,28 +293,28 @@ func (s *suiteContext) followingStepsShouldHave(status string, steps *DocString)
switch status {
case "passed":
for _, st := range f.findStepResults(passed) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
for _, st := range f.storage.mustGetPickleStepResultsByStatus(passed) {
pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "failed":
for _, st := range f.findStepResults(failed) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
for _, st := range f.storage.mustGetPickleStepResultsByStatus(failed) {
pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "skipped":
for _, st := range f.findStepResults(skipped) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
for _, st := range f.storage.mustGetPickleStepResultsByStatus(skipped) {
pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "undefined":
for _, st := range f.findStepResults(undefined) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
for _, st := range f.storage.mustGetPickleStepResultsByStatus(undefined) {
pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "pending":
for _, st := range f.findStepResults(pending) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
for _, st := range f.storage.mustGetPickleStepResultsByStatus(pending) {
pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
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)
}
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 {
s.testedSuite.BeforeSuite(func() {
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
func (s *suiteContext) aFeatureFile(path string, body *DocString) error {
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(body.Content), (&messages.Incrementing{}).NewId)
gd.Uri = path
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
}
@ -479,7 +444,7 @@ func (s *suiteContext) iShouldHaveNumFeatureFiles(num int, files *DocString) err
var actual []string
for _, ft := range s.testedSuite.features {
actual = append(actual, ft.path)
actual = append(actual, ft.Uri)
}
if len(expected) != len(actual) {

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

@ -41,7 +41,6 @@ func InitializeScenario(ctx *ScenarioContext) {
ctx.Step(`^(?:a )?failing step`, 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(`^all steps should (?:be|have|have been) (passed|failed|skipped|undefined|pending)$`, tc.allStepsShouldHave)
ctx.Step(`^the undefined step snippets should be:$`, tc.theUndefinedStepSnippetsShouldBe)
// event stream
@ -142,18 +141,18 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTags(tags string) error {
}
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 _, pickle := range feat.pickles {
st.mustInsertPickle(pickle)
tc.testedSuite.storage.mustInsertPickle(pickle)
}
}
fmt := newBaseFmt("godog", &tc.out)
fmt.setStorage(st)
fmt.setStorage(tc.testedSuite.storage)
tc.testedSuite.fmt = fmt
tc.testedSuite.fmt.TestRunStarted()
@ -173,16 +172,16 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithFormatter(name string) erro
return fmt.Errorf(`formatter "%s" is not available`, name)
}
st := newStorage()
tc.testedSuite.storage = newStorage()
for _, feat := range tc.testedSuite.features {
for _, pickle := range feat.pickles {
st.mustInsertPickle(pickle)
tc.testedSuite.storage.mustInsertPickle(pickle)
}
}
tc.testedSuite.fmt = f("godog", colors.Uncolored(&tc.out))
if fmt, ok := tc.testedSuite.fmt.(storageFormatter); ok {
fmt.setStorage(st)
fmt.setStorage(tc.testedSuite.storage)
}
tc.testedSuite.fmt.TestRunStarted()
@ -256,28 +255,28 @@ func (tc *godogFeaturesScenario) followingStepsShouldHave(status string, steps *
switch status {
case "passed":
for _, st := range f.findStepResults(passed) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
for _, st := range f.storage.mustGetPickleStepResultsByStatus(passed) {
pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "failed":
for _, st := range f.findStepResults(failed) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
for _, st := range f.storage.mustGetPickleStepResultsByStatus(failed) {
pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "skipped":
for _, st := range f.findStepResults(skipped) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
for _, st := range f.storage.mustGetPickleStepResultsByStatus(skipped) {
pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "undefined":
for _, st := range f.findStepResults(undefined) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
for _, st := range f.storage.mustGetPickleStepResultsByStatus(undefined) {
pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
case "pending":
for _, st := range f.findStepResults(pending) {
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
for _, st := range f.storage.mustGetPickleStepResultsByStatus(pending) {
pickleStep := f.storage.mustGetPickleStep(st.PickleStepID)
actual = append(actual, pickleStep.Text)
}
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)
}
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 {
tc.testedSuite.BeforeSuite(func() {
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
func (tc *godogFeaturesScenario) aFeatureFile(path string, body *DocString) error {
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(body.Content), (&messages.Incrementing{}).NewId)
gd.Uri = path
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
}
@ -441,7 +406,7 @@ func (tc *godogFeaturesScenario) iShouldHaveNumFeatureFiles(num int, files *DocS
var actual []string
for _, ft := range tc.testedSuite.features {
actual = append(actual, ft.path)
actual = append(actual, ft.Uri)
}
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) })
require.False(t, failed)