Added an in-mem storage for pickles

Этот коммит содержится в:
Fredrik Lönnblad 2020-06-09 13:26:01 +02:00
родитель 055f87f731
коммит 4356addf9f
15 изменённых файлов: 350 добавлений и 94 удалений

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

@ -91,6 +91,10 @@ type ConcurrentFormatter interface {
Sync(ConcurrentFormatter)
}
type storageFormatter interface {
setStorage(*storage)
}
// FormatterFunc builds a formatter with given
// suite name and io.Writer to record output
type FormatterFunc func(string, io.Writer) Formatter
@ -140,13 +144,14 @@ type stepResult struct {
time time.Time
err error
owner *messages.Pickle
step *messages.Pickle_PickleStep
pickleID string
pickleStepID string
def *StepDefinition
}
func newStepResult(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) *stepResult {
return &stepResult{time: timeNowFunc(), owner: pickle, step: step, def: match}
func newStepResult(pickleID, pickleStepID string, match *StepDefinition) *stepResult {
return &stepResult{time: timeNowFunc(), pickleID: pickleID, pickleStepID: pickleStepID, def: match}
}
func newBaseFmt(suite string, out io.Writer) *basefmt {
@ -166,6 +171,8 @@ type basefmt struct {
owner interface{}
indent int
storage *storage
started time.Time
features []*feature
@ -173,6 +180,10 @@ type basefmt struct {
lock *sync.Mutex
}
func (f *basefmt) setStorage(st *storage) {
f.storage = st
}
func (f *basefmt) lastFeature() *feature {
return f.features[len(f.features)-1]
}
@ -245,7 +256,7 @@ func (f *basefmt) Pickle(p *messages.Pickle) {
feature := f.features[len(f.features)-1]
pr := pickleResult{name: p.Name, astNodeIDs: p.AstNodeIds, time: timeNowFunc()}
pr := pickleResult{pickleID: p.Id, time: timeNowFunc()}
feature.pickleResults = append(feature.pickleResults, &pr)
}
@ -264,7 +275,7 @@ func (f *basefmt) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock()
defer f.lock.Unlock()
s := newStepResult(pickle, step, match)
s := newStepResult(pickle.Id, step.Id, match)
s.status = passed
f.lastFeature().appendStepResult(s)
}
@ -273,7 +284,7 @@ func (f *basefmt) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleS
f.lock.Lock()
defer f.lock.Unlock()
s := newStepResult(pickle, step, match)
s := newStepResult(pickle.Id, step.Id, match)
s.status = skipped
f.lastFeature().appendStepResult(s)
}
@ -282,7 +293,7 @@ func (f *basefmt) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pickl
f.lock.Lock()
defer f.lock.Unlock()
s := newStepResult(pickle, step, match)
s := newStepResult(pickle.Id, step.Id, match)
s.status = undefined
f.lastFeature().appendStepResult(s)
}
@ -291,7 +302,7 @@ func (f *basefmt) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock()
defer f.lock.Unlock()
s := newStepResult(pickle, step, match)
s := newStepResult(pickle.Id, step.Id, match)
s.status = failed
s.err = err
f.lastFeature().appendStepResult(s)
@ -301,7 +312,7 @@ func (f *basefmt) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleS
f.lock.Lock()
defer f.lock.Unlock()
s := newStepResult(pickle, step, match)
s := newStepResult(pickle.Id, step.Id, match)
s.status = pending
f.lastFeature().appendStepResult(s)
}
@ -450,8 +461,10 @@ func (f *basefmt) snippets() string {
var snips []undefinedSnippet
// build snippets
for _, u := range undefinedStepResults {
steps := []string{u.step.Text}
arg := u.step.Argument
pickleStep := f.storage.mustGetPickleStep(u.pickleStepID)
steps := []string{pickleStep.Text}
arg := pickleStep.Argument
if u.def != nil {
steps = u.def.undefined
arg = nil

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

@ -79,7 +79,9 @@ func (f *cukefmt) buildCukeElements(pickleResults []*pickleResult) (res []cukeEl
res = make([]cukeElement, len(pickleResults))
for idx, pickleResult := range pickleResults {
cukeElement := f.buildCukeElement(pickleResult.name, pickleResult.astNodeIDs)
pickle := f.storage.mustGetPickle(pickleResult.pickleID)
cukeElement := f.buildCukeElement(pickle.Name, pickle.AstNodeIds)
stepStartedAt := pickleResult.startedAt()
@ -237,19 +239,21 @@ func (f *cukefmt) buildCukeElement(pickleName string, pickleAstNodeIDs []string)
}
func (f *cukefmt) buildCukeStep(stepResult *stepResult) (cukeStep cukeStep) {
step := f.findStep(stepResult.step.AstNodeIds[0])
pickle := f.storage.mustGetPickle(stepResult.pickleID)
pickleStep := f.storage.mustGetPickleStep(stepResult.pickleStepID)
step := f.findStep(pickleStep.AstNodeIds[0])
line := step.Location.Line
if len(stepResult.owner.AstNodeIds) == 2 {
_, row := f.findExample(stepResult.owner.AstNodeIds[1])
if len(pickle.AstNodeIds) == 2 {
_, row := f.findExample(pickle.AstNodeIds[1])
line = row.Location.Line
}
cukeStep.Name = stepResult.step.Text
cukeStep.Name = pickleStep.Text
cukeStep.Line = int(line)
cukeStep.Keyword = step.Keyword
arg := stepResult.step.Argument
arg := pickleStep.Argument
if arg.GetDocString() != nil && step.GetDocString() != nil {
cukeStep.Docstring = &cukeDocstring{}
@ -279,7 +283,7 @@ func (f *cukefmt) buildCukeStep(stepResult *stepResult) (cukeStep cukeStep) {
}
if stepResult.status == undefined || stepResult.status == pending {
cukeStep.Match.Location = fmt.Sprintf("%s:%d", stepResult.owner.Uri, step.Location.Line)
cukeStep.Match.Location = fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line)
}
return cukeStep

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

@ -145,8 +145,9 @@ func (f *events) Copy(cf ConcurrentFormatter) {
}
}
func (f *events) step(res *stepResult) {
step := f.findStep(res.step.AstNodeIds[0])
func (f *events) step(pickle *messages.Pickle, res *stepResult) {
pickleStep := f.storage.mustGetPickleStep(res.pickleStepID)
step := f.findStep(pickleStep.AstNodeIds[0])
var errMsg string
if res.err != nil {
@ -160,13 +161,13 @@ func (f *events) step(res *stepResult) {
Summary string `json:"summary,omitempty"`
}{
"TestStepFinished",
fmt.Sprintf("%s:%d", res.owner.Uri, step.Location.Line),
fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line),
timeNowFunc().UnixNano() / nanoSec,
res.status.String(),
errMsg,
})
if isLastStep(res.owner, res.step) {
if isLastStep(pickle, pickleStep) {
var status string
for _, stepResult := range f.lastFeature().lastPickleResult().stepResults {
@ -189,7 +190,7 @@ func (f *events) step(res *stepResult) {
Status string `json:"status"`
}{
"TestCaseFinished",
f.scenarioLocation(res.owner),
f.scenarioLocation(pickle),
timeNowFunc().UnixNano() / nanoSec,
status,
})
@ -249,7 +250,7 @@ func (f *events) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.lock.Lock()
defer f.lock.Unlock()
f.step(f.lastStepResult())
f.step(pickle, f.lastStepResult())
}
func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -258,7 +259,7 @@ func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock()
defer f.lock.Unlock()
f.step(f.lastStepResult())
f.step(pickle, f.lastStepResult())
}
func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -267,7 +268,7 @@ func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.lock.Lock()
defer f.lock.Unlock()
f.step(f.lastStepResult())
f.step(pickle, f.lastStepResult())
}
func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
@ -276,7 +277,7 @@ func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.lock.Lock()
defer f.lock.Unlock()
f.step(f.lastStepResult())
f.step(pickle, f.lastStepResult())
}
func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -285,7 +286,7 @@ func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock()
defer f.lock.Unlock()
f.step(f.lastStepResult())
f.step(pickle, f.lastStepResult())
}
func (f *events) scenarioLocation(pickle *messages.Pickle) string {

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

@ -23,7 +23,7 @@ type junitFormatter struct {
}
func (f *junitFormatter) Summary() {
suite := buildJUNITPackageSuite(f.suiteName, f.started, f.features)
suite := f.buildJUNITPackageSuite()
_, err := io.WriteString(f.out, xml.Header)
if err != nil {
@ -53,16 +53,16 @@ func junitTimeDuration(from, to time.Time) string {
return strconv.FormatFloat(to.Sub(from).Seconds(), 'f', -1, 64)
}
func buildJUNITPackageSuite(suiteName string, startedAt time.Time, features []*feature) junitPackageSuite {
func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite {
suite := junitPackageSuite{
Name: suiteName,
TestSuites: make([]*junitTestSuite, len(features)),
Time: junitTimeDuration(startedAt, timeNowFunc()),
Name: f.suiteName,
TestSuites: make([]*junitTestSuite, len(f.features)),
Time: junitTimeDuration(f.started, timeNowFunc()),
}
sort.Sort(sortByName(features))
sort.Sort(sortByName(f.features))
for idx, feat := range features {
for idx, feat := range f.features {
ts := junitTestSuite{
Name: feat.GherkinDocument.Feature.Name,
Time: junitTimeDuration(feat.startedAt(), feat.finishedAt()),
@ -71,7 +71,8 @@ func buildJUNITPackageSuite(suiteName string, startedAt time.Time, features []*f
var testcaseNames = make(map[string]int)
for _, pickleResult := range feat.pickleResults {
testcaseNames[pickleResult.name] = testcaseNames[pickleResult.name] + 1
pickle := f.storage.mustGetPickle(pickleResult.pickleID)
testcaseNames[pickle.Name] = testcaseNames[pickle.Name] + 1
}
var outlineNo = make(map[string]int)
@ -79,7 +80,9 @@ func buildJUNITPackageSuite(suiteName string, startedAt time.Time, features []*f
tc := junitTestCase{}
tc.Time = junitTimeDuration(pickleResult.startedAt(), pickleResult.finishedAt())
tc.Name = pickleResult.name
pickle := f.storage.mustGetPickle(pickleResult.pickleID)
tc.Name = pickle.Name
if testcaseNames[tc.Name] > 1 {
outlineNo[tc.Name] = outlineNo[tc.Name] + 1
tc.Name += fmt.Sprintf(" #%d", outlineNo[tc.Name])
@ -89,30 +92,32 @@ func buildJUNITPackageSuite(suiteName string, startedAt time.Time, features []*f
suite.Tests++
for _, stepResult := range pickleResult.stepResults {
pickleStep := f.storage.mustGetPickleStep(stepResult.pickleStepID)
switch stepResult.status {
case passed:
tc.Status = passed.String()
case failed:
tc.Status = failed.String()
tc.Failure = &junitFailure{
Message: fmt.Sprintf("Step %s: %s", stepResult.step.Text, stepResult.err),
Message: fmt.Sprintf("Step %s: %s", pickleStep.Text, stepResult.err),
}
case skipped:
tc.Error = append(tc.Error, &junitError{
Type: "skipped",
Message: fmt.Sprintf("Step %s", stepResult.step.Text),
Message: fmt.Sprintf("Step %s", pickleStep.Text),
})
case undefined:
tc.Status = undefined.String()
tc.Error = append(tc.Error, &junitError{
Type: "undefined",
Message: fmt.Sprintf("Step %s", stepResult.step.Text),
Message: fmt.Sprintf("Step %s", pickleStep.Text),
})
case pending:
tc.Status = pending.String()
tc.Error = append(tc.Error, &junitError{
Type: "pending",
Message: fmt.Sprintf("Step %s: TODO: write pending definition", stepResult.step.Text),
Message: fmt.Sprintf("Step %s: TODO: write pending definition", pickleStep.Text),
})
}
}

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

@ -61,7 +61,7 @@ func (f *pretty) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.lock.Lock()
defer f.lock.Unlock()
f.printStep(f.lastStepResult())
f.printStep(pickle, f.lastStepResult())
}
func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -70,7 +70,7 @@ func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock()
defer f.lock.Unlock()
f.printStep(f.lastStepResult())
f.printStep(pickle, f.lastStepResult())
}
func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -79,7 +79,7 @@ func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_Pickle
f.lock.Lock()
defer f.lock.Unlock()
f.printStep(f.lastStepResult())
f.printStep(pickle, f.lastStepResult())
}
func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
@ -88,7 +88,7 @@ func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleSte
f.lock.Lock()
defer f.lock.Unlock()
f.printStep(f.lastStepResult())
f.printStep(pickle, f.lastStepResult())
}
func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
@ -97,7 +97,7 @@ func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleSt
f.lock.Lock()
defer f.lock.Unlock()
f.printStep(f.lastStepResult())
f.printStep(pickle, f.lastStepResult())
}
func (f *pretty) Sync(cf ConcurrentFormatter) {
@ -196,13 +196,16 @@ func (f *pretty) Summary() {
if len(failedStepResults) > 0 {
fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n")
for _, fail := range failedStepResults {
feature := f.findFeature(fail.owner.AstNodeIds[0])
pickle := f.storage.mustGetPickle(fail.pickleID)
pickleStep := f.storage.mustGetPickleStep(fail.pickleStepID)
astScenario := f.findScenario(fail.owner.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", astScenario.Keyword, fail.owner.Name)
feature := f.findFeature(pickle.AstNodeIds[0])
astStep := f.findStep(fail.step.AstNodeIds[0])
stepDesc := strings.TrimSpace(astStep.Keyword) + " " + fail.step.Text
astScenario := f.findScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", astScenario.Keyword, pickle.Name)
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))
@ -240,7 +243,8 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
return
}
for _, result := range f.lastFeature().lastPickleResult().stepResults {
lastPickleResult := f.lastFeature().lastPickleResult()
for _, result := range lastPickleResult.stepResults {
// determine example row status
switch {
case result.status == failed:
@ -256,7 +260,8 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
// in first example, we need to print steps
var text string
astStep := f.findStep(result.step.AstNodeIds[0])
pickleStep := f.storage.mustGetPickleStep(result.pickleStepID)
astStep := f.findStep(pickleStep.AstNodeIds[0])
if result.def != nil {
if m := outlinePlaceholderRegexp.FindAllStringIndex(astStep.Text, -1); len(m) > 0 {
@ -272,7 +277,9 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
text = cyan(astStep.Text)
}
_, maxLength := f.scenarioLengths(result.owner.AstNodeIds[0])
pickle := f.storage.mustGetPickle(lastPickleResult.pickleID)
_, maxLength := f.scenarioLengths(pickle.AstNodeIds[0])
stepLength := f.lengthPickleStep(astStep.Keyword, astStep.Text)
text += s(maxLength - stepLength)
@ -283,7 +290,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
// print the step outline
fmt.Fprintln(f.out, s(f.indent*2)+cyan(strings.TrimSpace(astStep.Keyword))+" "+text)
if table := result.step.Argument.GetDataTable(); table != nil {
if table := pickleStep.Argument.GetDataTable(); table != nil {
f.printTable(table, cyan)
}
@ -326,10 +333,12 @@ func (f *pretty) printTableHeader(row *messages.GherkinDocument_Feature_TableRow
f.printTableRow(row, max, cyan)
}
func (f *pretty) printStep(result *stepResult) {
astBackground := f.findBackground(result.owner.AstNodeIds[0])
astScenario := f.findScenario(result.owner.AstNodeIds[0])
astStep := f.findStep(result.step.AstNodeIds[0])
func (f *pretty) printStep(pickle *messages.Pickle, result *stepResult) {
pickleStep := f.storage.mustGetPickleStep(result.pickleStepID)
astBackground := f.findBackground(pickle.AstNodeIds[0])
astScenario := f.findScenario(pickle.AstNodeIds[0])
astStep := f.findStep(pickleStep.AstNodeIds[0])
var backgroundSteps int
if astBackground != nil {
@ -350,26 +359,26 @@ func (f *pretty) printStep(result *stepResult) {
}
if !astBackgroundStep && len(astScenario.Examples) > 0 {
f.printOutlineExample(result.owner, backgroundSteps)
f.printOutlineExample(pickle, backgroundSteps)
return
}
scenarioHeaderLength, maxLength := f.scenarioLengths(result.owner.AstNodeIds[0])
stepLength := f.lengthPickleStep(astStep.Keyword, result.step.Text)
scenarioHeaderLength, maxLength := f.scenarioLengths(pickle.AstNodeIds[0])
stepLength := f.lengthPickleStep(astStep.Keyword, pickleStep.Text)
firstExecutedScenarioStep := len(f.lastFeature().lastPickleResult().stepResults) == 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()(result.step.Text)
text := s(f.indent*2) + result.status.clr()(strings.TrimSpace(astStep.Keyword)) + " " + result.status.clr()(pickleStep.Text)
if result.def != nil {
text += s(maxLength - stepLength + 1)
text += blackb("# " + result.def.definitionID())
}
fmt.Fprintln(f.out, text)
if table := result.step.Argument.GetDataTable(); table != nil {
if table := pickleStep.Argument.GetDataTable(); table != nil {
f.printTable(table, cyan)
}

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

@ -41,13 +41,16 @@ func (f *progress) Summary() {
var failedStepsOutput []string
for _, sr := range f.findStepResults(failed) {
if sr.status == failed {
sc := f.findScenario(sr.owner.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, sr.owner.Name)
scenarioLine := fmt.Sprintf("%s:%d", sr.owner.Uri, sc.Location.Line)
pickle := f.storage.mustGetPickle(sr.pickleID)
pickleStep := f.storage.mustGetPickleStep(sr.pickleStepID)
step := f.findStep(sr.step.AstNodeIds[0])
stepDesc := strings.TrimSpace(step.Keyword) + " " + sr.step.Text
stepLine := fmt.Sprintf("%s:%d", sr.owner.Uri, step.Location.Line)
sc := f.findScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, pickle.Name)
scenarioLine := fmt.Sprintf("%s:%d", pickle.Uri, sc.Location.Line)
step := f.findStep(pickleStep.AstNodeIds[0])
stepDesc := strings.TrimSpace(step.Keyword) + " " + pickleStep.Text
stepLine := fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line)
failedStepsOutput = append(
failedStepsOutput,

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

@ -40,6 +40,11 @@ func TestProgressFormatterWhenStepPanics(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.True(t, failed)
@ -54,6 +59,7 @@ func TestProgressFormatterWithPanicInMultistep(t *testing.T) {
require.NoError(t, err)
pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
var buf bytes.Buffer
w := colors.Uncolored(&buf)
r := runner{
@ -68,6 +74,11 @@ func TestProgressFormatterWithPanicInMultistep(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.True(t, failed)
}
@ -93,6 +104,11 @@ func TestProgressFormatterMultistepTemplates(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)
@ -159,6 +175,11 @@ Feature: basic
},
}
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)
}
@ -195,6 +216,11 @@ Feature: basic
},
}
r.storage = newStorage()
for _, pickle := range pickles {
r.storage.mustInsertPickle(pickle)
}
failed := r.concurrent(1, func() Formatter { return progressFunc("progress", w) })
require.True(t, failed)

1
go.mod
Просмотреть файл

@ -5,5 +5,6 @@ go 1.13
require (
github.com/cucumber/gherkin-go/v11 v11.0.0
github.com/cucumber/messages-go/v10 v10.0.3
github.com/hashicorp/go-memdb v1.2.1
github.com/stretchr/testify v1.6.1
)

9
go.sum
Просмотреть файл

@ -12,7 +12,16 @@ github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/hashicorp/go-immutable-radix v1.2.0 h1:l6UW37iCXwZkZoAbEYnptSHVE/cQ5bOTPYG5W3vf9+8=
github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-memdb v1.2.1 h1:wI9btDjYUOJJHTCnRlAG/TkRyD/ij7meJMrLK9X31Cc=
github.com/hashicorp/go-memdb v1.2.1/go.mod h1:OSvLJ662Jim8hMM+gWGyhktyWk2xPCnWMc7DWIqtkGA=
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=

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

@ -31,6 +31,8 @@ type runner struct {
initializer initializer
testSuiteInitializer testSuiteInitializer
scenarioInitializer scenarioInitializer
storage *storage
}
func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool) {
@ -42,10 +44,15 @@ func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool
useFmtCopy = true
}
if fmt, ok := r.fmt.(storageFormatter); ok {
fmt.setStorage(r.storage)
}
testSuiteContext := TestSuiteContext{}
if r.testSuiteInitializer != nil {
r.testSuiteInitializer(&testSuiteContext)
}
r.fmt.TestRunStarted()
// run before suite handlers
@ -73,6 +80,8 @@ func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool
strict: r.strict,
features: []*feature{feat},
}
suite.fmt = r.fmt
if useFmtCopy {
fmtCopy = formatterFn()
suite.fmt = fmtCopy
@ -83,8 +92,10 @@ func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool
if dOk && sOk {
concurrentDestFmt.Sync(concurrentSourceFmt)
}
} else {
suite.fmt = r.fmt
}
if fmt, ok := suite.fmt.(storageFormatter); ok {
fmt.setStorage(r.storage)
}
if r.initializer != nil {
@ -217,6 +228,13 @@ func runWithOptions(suite string, runner runner, opt Options) int {
return exitOptionError
}
runner.storage = newStorage()
for _, feat := range runner.features {
for _, pickle := range feat.pickles {
runner.storage.mustInsertPickle(pickle)
}
}
// user may have specified -1 option to create random seed
runner.randomSeed = opt.Randomize
if runner.randomSeed == -1 {

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

@ -75,6 +75,11 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) {
},
}
r.storage = newStorage()
for _, pickle := range pickles {
r.storage.mustInsertPickle(pickle)
}
failed := r.concurrent(1, func() Formatter { return progressFunc("progress", ioutil.Discard) })
require.False(t, failed)
@ -100,6 +105,11 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) {
},
}
r.storage = newStorage()
for _, pickle := range pickles {
r.storage.mustInsertPickle(pickle)
}
failed := r.concurrent(1, func() Formatter { return progressFunc("progress", ioutil.Discard) })
require.False(t, failed)
@ -125,6 +135,11 @@ func TestShouldFailOnError(t *testing.T) {
},
}
r.storage = newStorage()
for _, pickle := range pickles {
r.storage.mustInsertPickle(pickle)
}
failed := r.concurrent(1, func() Formatter { return progressFunc("progress", ioutil.Discard) })
require.True(t, failed)
}

104
storage.go Обычный файл
Просмотреть файл

@ -0,0 +1,104 @@
package godog
import (
"github.com/cucumber/messages-go/v10"
"github.com/hashicorp/go-memdb"
)
const (
writeMode bool = true
readMode bool = false
tablePickle string = "pickle"
tablePickleIndexID string = "id"
tablePickleStep string = "pickle_step"
tablePickleStepIndexID string = "id"
)
type storage struct {
db *memdb.MemDB
}
func newStorage() *storage {
// Create the DB schema
schema := memdb.DBSchema{
Tables: map[string]*memdb.TableSchema{
tablePickle: {
Name: tablePickle,
Indexes: map[string]*memdb.IndexSchema{
tablePickleIndexID: {
Name: tablePickleIndexID,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "Id"},
},
},
},
tablePickleStep: {
Name: tablePickleStep,
Indexes: map[string]*memdb.IndexSchema{
tablePickleStepIndexID: {
Name: tablePickleStepIndexID,
Unique: true,
Indexer: &memdb.StringFieldIndex{Field: "Id"},
},
},
},
},
}
// Create a new data base
db, err := memdb.NewMemDB(&schema)
if err != nil {
panic(err)
}
return &storage{db}
}
func (s *storage) mustInsertPickle(p *messages.Pickle) (err error) {
txn := s.db.Txn(writeMode)
if err = txn.Insert(tablePickle, p); err != nil {
panic(err)
}
for _, step := range p.Steps {
if err = txn.Insert(tablePickleStep, step); err != nil {
panic(err)
}
}
txn.Commit()
return
}
func (s *storage) mustGetPickle(id string) *messages.Pickle {
txn := s.db.Txn(readMode)
defer txn.Abort()
var v interface{}
v, err := txn.First(tablePickle, tablePickleIndexID, id)
if err != nil {
panic(err)
} else if v == nil {
panic("Couldn't find pickle with ID: " + id)
}
return v.(*messages.Pickle)
}
func (s *storage) mustGetPickleStep(id string) *messages.Pickle_PickleStep {
txn := s.db.Txn(readMode)
defer txn.Abort()
var v interface{}
v, err := txn.First(tablePickleStep, tablePickleStepIndexID, id)
if err != nil {
panic(err)
} else if v == nil {
panic("Couldn't find pickle step with ID: " + id)
}
return v.(*messages.Pickle_PickleStep)
}

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

@ -129,8 +129,8 @@ func (s sortByName) Less(i, j int) bool { return s[i].Feature.Name < s[j].Featur
func (s sortByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type pickleResult struct {
name string
astNodeIDs []string
pickleID string
time time.Time
stepResults []*stepResult
}

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

@ -183,7 +183,16 @@ func (s *suiteContext) iRunFeatureSuiteWithTags(tags string) error {
applyTagFilter(tags, feat)
}
s.testedSuite.fmt = newBaseFmt("godog", &s.out)
st := newStorage()
for _, feat := range s.testedSuite.features {
for _, pickle := range feat.pickles {
st.mustInsertPickle(pickle)
}
}
fmt := newBaseFmt("godog", &s.out)
fmt.setStorage(st)
s.testedSuite.fmt = fmt
s.testedSuite.fmt.TestRunStarted()
s.testedSuite.run()
@ -193,15 +202,25 @@ func (s *suiteContext) iRunFeatureSuiteWithTags(tags string) error {
}
func (s *suiteContext) iRunFeatureSuiteWithFormatter(name string) error {
if err := s.parseFeatures(); err != nil {
return err
}
f := FindFmt(name)
if f == nil {
return fmt.Errorf(`formatter "%s" is not available`, name)
}
s.testedSuite.fmt = f("godog", colors.Uncolored(&s.out))
st := newStorage()
for _, feat := range s.testedSuite.features {
for _, pickle := range feat.pickles {
st.mustInsertPickle(pickle)
}
}
if err := s.parseFeatures(); err != nil {
return err
s.testedSuite.fmt = f("godog", colors.Uncolored(&s.out))
if fmt, ok := s.testedSuite.fmt.(storageFormatter); ok {
fmt.setStorage(st)
}
s.testedSuite.fmt.TestRunStarted()
@ -276,23 +295,28 @@ func (s *suiteContext) followingStepsShouldHave(status string, steps *DocString)
switch status {
case "passed":
for _, st := range f.findStepResults(passed) {
actual = append(actual, st.step.Text)
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
actual = append(actual, pickleStep.Text)
}
case "failed":
for _, st := range f.findStepResults(failed) {
actual = append(actual, st.step.Text)
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
actual = append(actual, pickleStep.Text)
}
case "skipped":
for _, st := range f.findStepResults(skipped) {
actual = append(actual, st.step.Text)
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
actual = append(actual, pickleStep.Text)
}
case "undefined":
for _, st := range f.findStepResults(undefined) {
actual = append(actual, st.step.Text)
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
actual = append(actual, pickleStep.Text)
}
case "pending":
for _, st := range f.findStepResults(pending) {
actual = append(actual, st.step.Text)
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
actual = append(actual, pickleStep.Text)
}
default:
return fmt.Errorf("unexpected step status wanted: %s", status)

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

@ -145,7 +145,16 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTags(tags string) error {
applyTagFilter(tags, feat)
}
tc.testedSuite.fmt = newBaseFmt("godog", &tc.out)
st := newStorage()
for _, feat := range tc.testedSuite.features {
for _, pickle := range feat.pickles {
st.mustInsertPickle(pickle)
}
}
fmt := newBaseFmt("godog", &tc.out)
fmt.setStorage(st)
tc.testedSuite.fmt = fmt
tc.testedSuite.fmt.TestRunStarted()
tc.testedSuite.run()
@ -155,15 +164,25 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTags(tags string) error {
}
func (tc *godogFeaturesScenario) iRunFeatureSuiteWithFormatter(name string) error {
if err := tc.parseFeatures(); err != nil {
return err
}
f := FindFmt(name)
if f == nil {
return fmt.Errorf(`formatter "%s" is not available`, name)
}
tc.testedSuite.fmt = f("godog", colors.Uncolored(&tc.out))
st := newStorage()
for _, feat := range tc.testedSuite.features {
for _, pickle := range feat.pickles {
st.mustInsertPickle(pickle)
}
}
if err := tc.parseFeatures(); err != nil {
return err
tc.testedSuite.fmt = f("godog", colors.Uncolored(&tc.out))
if fmt, ok := tc.testedSuite.fmt.(storageFormatter); ok {
fmt.setStorage(st)
}
tc.testedSuite.fmt.TestRunStarted()
@ -238,23 +257,28 @@ func (tc *godogFeaturesScenario) followingStepsShouldHave(status string, steps *
switch status {
case "passed":
for _, st := range f.findStepResults(passed) {
actual = append(actual, st.step.Text)
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
actual = append(actual, pickleStep.Text)
}
case "failed":
for _, st := range f.findStepResults(failed) {
actual = append(actual, st.step.Text)
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
actual = append(actual, pickleStep.Text)
}
case "skipped":
for _, st := range f.findStepResults(skipped) {
actual = append(actual, st.step.Text)
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
actual = append(actual, pickleStep.Text)
}
case "undefined":
for _, st := range f.findStepResults(undefined) {
actual = append(actual, st.step.Text)
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
actual = append(actual, pickleStep.Text)
}
case "pending":
for _, st := range f.findStepResults(pending) {
actual = append(actual, st.step.Text)
pickleStep := f.storage.mustGetPickleStep(st.pickleStepID)
actual = append(actual, pickleStep.Text)
}
default:
return fmt.Errorf("unexpected step status wanted: %s", status)