diff --git a/fmt.go b/fmt.go index 1e28aa6..7a14906 100644 --- a/fmt.go +++ b/fmt.go @@ -174,7 +174,6 @@ type basefmt struct { storage *storage startedAt time.Time - features []*feature firstFeature *bool lock *sync.Mutex @@ -184,60 +183,6 @@ func (f *basefmt) setStorage(st *storage) { f.storage = st } -func (f *basefmt) lastFeature() *feature { - return f.features[len(f.features)-1] -} - -func (f *basefmt) findFeature(scenarioAstID string) *feature { - for _, ft := range f.features { - if sc := ft.findScenario(scenarioAstID); sc != nil { - return ft - } - } - - panic("Couldn't find scenario for AST ID: " + scenarioAstID) -} - -func (f *basefmt) findScenario(scenarioAstID string) *messages.GherkinDocument_Feature_Scenario { - for _, ft := range f.features { - if sc := ft.findScenario(scenarioAstID); sc != nil { - return sc - } - } - - panic("Couldn't find scenario for AST ID: " + scenarioAstID) -} - -func (f *basefmt) findBackground(scenarioAstID string) *messages.GherkinDocument_Feature_Background { - for _, ft := range f.features { - if bg := ft.findBackground(scenarioAstID); bg != nil { - return bg - } - } - - return nil -} - -func (f *basefmt) findExample(exampleAstID string) (*messages.GherkinDocument_Feature_Scenario_Examples, *messages.GherkinDocument_Feature_TableRow) { - for _, ft := range f.features { - if es, rs := ft.findExample(exampleAstID); es != nil && rs != nil { - return es, rs - } - } - - return nil, nil -} - -func (f *basefmt) findStep(stepAstID string) *messages.GherkinDocument_Feature_Step { - for _, ft := range f.features { - if st := ft.findStep(stepAstID); st != nil { - return st - } - } - - panic("Couldn't find step for AST ID: " + stepAstID) -} - func (f *basefmt) TestRunStarted() { f.lock.Lock() defer f.lock.Unlock() @@ -255,8 +200,6 @@ func (f *basefmt) Feature(ft *messages.GherkinDocument, p string, c []byte) { defer f.lock.Unlock() *f.firstFeature = false - - f.features = append(f.features, &feature{GherkinDocument: ft, time: timeNowFunc()}) } func (f *basefmt) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { @@ -383,13 +326,7 @@ func (f *basefmt) Sync(cf ConcurrentFormatter) { } } -func (f *basefmt) Copy(cf ConcurrentFormatter) { - if source, ok := cf.(*basefmt); ok { - for _, v := range source.features { - f.features = append(f.features, v) - } - } -} +func (f *basefmt) Copy(cf ConcurrentFormatter) {} func (f *basefmt) snippets() string { undefinedStepResults := f.storage.mustGetPickleStepResultsByStatus(undefined) diff --git a/fmt_cucumber.go b/fmt_cucumber.go index 3eb7802..9ab16b0 100644 --- a/fmt_cucumber.go +++ b/fmt_cucumber.go @@ -34,7 +34,9 @@ type cukefmt struct { } func (f *cukefmt) Summary() { - res := f.buildCukeFeatures(f.features) + features := f.storage.mustGetFeatures() + + res := f.buildCukeFeatures(features) dat, err := json.MarshalIndent(res, "", " ") if err != nil { @@ -57,7 +59,7 @@ func (f *cukefmt) Copy(cf ConcurrentFormatter) { } func (f *cukefmt) buildCukeFeatures(features []*feature) (res []cukeFeatureJSON) { - sort.Sort(sortByName(features)) + sort.Sort(sortFeaturesByName(features)) res = make([]cukeFeatureJSON, len(features)) @@ -88,7 +90,7 @@ func (f *cukefmt) buildCukeElements(pickles []*messages.Pickle) (res []cukeEleme pickleResult := f.storage.mustGetPickleResult(pickle.Id) pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id) - cukeElement := f.buildCukeElement(pickle.Name, pickle.AstNodeIds) + cukeElement := f.buildCukeElement(pickle) stepStartedAt := pickleResult.StartedAt @@ -204,10 +206,11 @@ func buildCukeFeature(feat *feature) cukeFeatureJSON { return cukeFeature } -func (f *cukefmt) buildCukeElement(pickleName string, pickleAstNodeIDs []string) (cukeElement cukeElement) { - scenario := f.findScenario(pickleAstNodeIDs[0]) +func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeElement) { + feature := f.storage.mustGetFeature(pickle.Uri) + scenario := feature.findScenario(pickle.AstNodeIds[0]) - cukeElement.Name = pickleName + cukeElement.Name = pickle.Name cukeElement.Line = int(scenario.Location.Line) cukeElement.Description = scenario.Description cukeElement.Keyword = scenario.Keyword @@ -219,11 +222,11 @@ func (f *cukefmt) buildCukeElement(pickleName string, pickleAstNodeIDs []string) cukeElement.Tags[idx].Name = element.Name } - if len(pickleAstNodeIDs) == 1 { + if len(pickle.AstNodeIds) == 1 { return } - example, _ := f.findExample(pickleAstNodeIDs[1]) + example, _ := feature.findExample(pickle.AstNodeIds[1]) for _, tag := range example.Tags { tag := cukeTag{Line: int(tag.Location.Line), Name: tag.Name} @@ -232,7 +235,7 @@ func (f *cukefmt) buildCukeElement(pickleName string, pickleAstNodeIDs []string) examples := scenario.GetExamples() if len(examples) > 0 { - rowID := pickleAstNodeIDs[1] + rowID := pickle.AstNodeIds[1] for _, example := range examples { for idx, row := range example.TableBody { @@ -248,12 +251,13 @@ func (f *cukefmt) buildCukeElement(pickleName string, pickleAstNodeIDs []string) } func (f *cukefmt) buildCukeStep(pickle *messages.Pickle, stepResult pickleStepResult) (cukeStep cukeStep) { + feature := f.storage.mustGetFeature(pickle.Uri) pickleStep := f.storage.mustGetPickleStep(stepResult.PickleStepID) - step := f.findStep(pickleStep.AstNodeIds[0]) + step := feature.findStep(pickleStep.AstNodeIds[0]) line := step.Location.Line if len(pickle.AstNodeIds) == 2 { - _, row := f.findExample(pickle.AstNodeIds[1]) + _, row := feature.findExample(pickle.AstNodeIds[1]) line = row.Location.Line } diff --git a/fmt_events.go b/fmt_events.go index b047175..ff40dc3 100644 --- a/fmt_events.go +++ b/fmt_events.go @@ -149,8 +149,9 @@ func (f *events) Copy(cf ConcurrentFormatter) { } func (f *events) step(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep) { + feature := f.storage.mustGetFeature(pickle.Uri) pickleStepResult := f.storage.mustGetPickleStepResult(pickleStep.Id) - step := f.findStep(pickleStep.AstNodeIds[0]) + step := feature.findStep(pickleStep.AstNodeIds[0]) var errMsg string if pickleStepResult.err != nil { @@ -207,7 +208,8 @@ func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_Pi f.lock.Lock() defer f.lock.Unlock() - step := f.findStep(pickleStep.AstNodeIds[0]) + feature := f.storage.mustGetFeature(pickle.Uri) + step := feature.findStep(pickleStep.AstNodeIds[0]) if def != nil { m := def.Expr.FindStringSubmatchIndex(pickleStep.Text)[2:] @@ -294,10 +296,12 @@ func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleSt } func (f *events) scenarioLocation(pickle *messages.Pickle) string { - scenario := f.findScenario(pickle.AstNodeIds[0]) + feature := f.storage.mustGetFeature(pickle.Uri) + scenario := feature.findScenario(pickle.AstNodeIds[0]) + line := scenario.Location.Line if len(pickle.AstNodeIds) == 2 { - _, row := f.findExample(pickle.AstNodeIds[1]) + _, row := feature.findExample(pickle.AstNodeIds[1]) line = row.Location.Line } diff --git a/fmt_junit.go b/fmt_junit.go index a6890ef..d01f51c 100644 --- a/fmt_junit.go +++ b/fmt_junit.go @@ -54,19 +54,20 @@ func junitTimeDuration(from, to time.Time) string { } func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite { + features := f.storage.mustGetFeatures() + sort.Sort(sortFeaturesByName(features)) + suite := junitPackageSuite{ Name: f.suiteName, - TestSuites: make([]*junitTestSuite, len(f.features)), + TestSuites: make([]*junitTestSuite, len(features)), Time: junitTimeDuration(f.startedAt, timeNowFunc()), } - sort.Sort(sortByName(f.features)) - - for idx, feat := range f.features { - pickles := f.storage.mustGetPickles(feat.Uri) + for idx, feature := range features { + pickles := f.storage.mustGetPickles(feature.Uri) sort.Sort(sortPicklesByID(pickles)) - var finishedAt = feat.startedAt() + var finishedAt = feature.startedAt() if len(pickles) > 0 { lastPickle := pickles[len(pickles)-1] @@ -79,8 +80,8 @@ func (f *junitFormatter) buildJUNITPackageSuite() junitPackageSuite { } ts := junitTestSuite{ - Name: feat.GherkinDocument.Feature.Name, - Time: junitTimeDuration(feat.startedAt(), finishedAt), + Name: feature.Feature.Name, + Time: junitTimeDuration(feature.startedAt(), finishedAt), TestCases: make([]*junitTestCase, len(pickles)), } diff --git a/fmt_pretty.go b/fmt_pretty.go index bc1435d..697a905 100644 --- a/fmt_pretty.go +++ b/fmt_pretty.go @@ -130,9 +130,10 @@ func keywordAndName(keyword, name string) string { return title } -func (f *pretty) scenarioLengths(scenarioAstID string) (scenarioHeaderLength int, maxLength int) { - astScenario := f.findScenario(scenarioAstID) - astBackground := f.findBackground(scenarioAstID) +func (f *pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength int, maxLength int) { + feature := f.storage.mustGetFeature(pickle.Uri) + astScenario := feature.findScenario(pickle.AstNodeIds[0]) + astBackground := feature.findBackground(pickle.AstNodeIds[0]) scenarioHeaderLength = f.lengthPickle(astScenario.Keyword, astScenario.Name) maxLength = f.longestStep(astScenario.Steps, scenarioHeaderLength) @@ -144,17 +145,19 @@ func (f *pretty) scenarioLengths(scenarioAstID string) (scenarioHeaderLength int return scenarioHeaderLength, maxLength } -func (f *pretty) printScenarioHeader(astScenario *messages.GherkinDocument_Feature_Scenario, spaceFilling int) { +func (f *pretty) printScenarioHeader(pickle *messages.Pickle, astScenario *messages.GherkinDocument_Feature_Scenario, spaceFilling int) { + feature := f.storage.mustGetFeature(pickle.Uri) text := s(f.indent) + keywordAndName(astScenario.Keyword, astScenario.Name) - text += s(spaceFilling) + f.line(f.lastFeature().Uri, astScenario.Location) + text += s(spaceFilling) + line(feature.Uri, astScenario.Location) fmt.Fprintln(f.out, "\n"+text) } func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) { - astScenario := f.findScenario(pickle.AstNodeIds[0]) - astBackground := f.findBackground(pickle.AstNodeIds[0]) + feature := f.storage.mustGetFeature(pickle.Uri) + astScenario := feature.findScenario(pickle.AstNodeIds[0]) + astBackground := feature.findBackground(pickle.AstNodeIds[0]) - scenarioHeaderLength, maxLength := f.scenarioLengths(pickle.AstNodeIds[0]) + scenarioHeaderLength, maxLength := f.scenarioLengths(pickle) if astBackground != nil { fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astBackground.Keyword, astBackground.Name)) @@ -166,7 +169,7 @@ func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) { // do not print scenario headers and examples multiple times if len(astScenario.Examples) > 0 { - exampleTable, exampleRow := f.findExample(pickle.AstNodeIds[1]) + exampleTable, exampleRow := feature.findExample(pickle.AstNodeIds[1]) firstExampleRow := exampleTable.TableBody[0].Id == exampleRow.Id firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line @@ -175,7 +178,7 @@ func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) { } } - f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength) + f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength) for _, examples := range astScenario.Examples { max := longestExampleRow(examples, cyan, cyan) @@ -202,17 +205,16 @@ func (f *pretty) Summary() { for _, fail := range failedStepResults { pickle := f.storage.mustGetPickle(fail.PickleID) pickleStep := f.storage.mustGetPickleStep(fail.PickleStepID) + feature := f.storage.mustGetFeature(pickle.Uri) - feature := f.findFeature(pickle.AstNodeIds[0]) - - astScenario := f.findScenario(pickle.AstNodeIds[0]) + astScenario := feature.findScenario(pickle.AstNodeIds[0]) scenarioDesc := fmt.Sprintf("%s: %s", astScenario.Keyword, pickle.Name) - astStep := f.findStep(pickleStep.AstNodeIds[0]) + astStep := feature.findStep(pickleStep.AstNodeIds[0]) stepDesc := strings.TrimSpace(astStep.Keyword) + " " + pickleStep.Text - 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)+red(scenarioDesc)+line(feature.Uri, astScenario.Location)) + fmt.Fprintln(f.out, s(f.indent*2)+red(stepDesc)+line(feature.Uri, astStep.Location)) fmt.Fprintln(f.out, s(f.indent*3)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.err))+"\n") } } @@ -224,10 +226,11 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in var errorMsg string var clr = green - astScenario := f.findScenario(pickle.AstNodeIds[0]) - scenarioHeaderLength, maxLength := f.scenarioLengths(pickle.AstNodeIds[0]) + feature := f.storage.mustGetFeature(pickle.Uri) + astScenario := feature.findScenario(pickle.AstNodeIds[0]) + scenarioHeaderLength, maxLength := f.scenarioLengths(pickle) - exampleTable, exampleRow := f.findExample(pickle.AstNodeIds[1]) + exampleTable, exampleRow := feature.findExample(pickle.AstNodeIds[1]) printExampleHeader := exampleTable.TableBody[0].Id == exampleRow.Id firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line @@ -235,7 +238,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1 if firstExamplesTable && printExampleHeader && firstExecutedScenarioStep { - f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength) + f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength) } if len(exampleTable.TableBody) == 0 { @@ -265,7 +268,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in // in first example, we need to print steps pickleStep := f.storage.mustGetPickleStep(result.PickleStepID) - astStep := f.findStep(pickleStep.AstNodeIds[0]) + astStep := feature.findStep(pickleStep.AstNodeIds[0]) var text = "" if result.def != nil { @@ -282,7 +285,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in text = cyan(astStep.Text) } - _, maxLength := f.scenarioLengths(pickle.AstNodeIds[0]) + _, maxLength := f.scenarioLengths(pickle) stepLength := f.lengthPickleStep(astStep.Keyword, astStep.Text) text += s(maxLength - stepLength) @@ -336,9 +339,10 @@ func (f *pretty) printTableHeader(row *messages.GherkinDocument_Feature_TableRow } 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]) + feature := f.storage.mustGetFeature(pickle.Uri) + astBackground := feature.findBackground(pickle.AstNodeIds[0]) + astScenario := feature.findScenario(pickle.AstNodeIds[0]) + astStep := feature.findStep(pickleStep.AstNodeIds[0]) var backgroundSteps int if astBackground != nil { @@ -374,12 +378,12 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_ return } - scenarioHeaderLength, maxLength := f.scenarioLengths(pickle.AstNodeIds[0]) + scenarioHeaderLength, maxLength := f.scenarioLengths(pickle) stepLength := f.lengthPickleStep(astStep.Keyword, pickleStep.Text) firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1 if !astBackgroundStep && firstExecutedScenarioStep { - f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength) + f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength) } pickleStepResult := f.storage.mustGetPickleStepResult(pickleStep.Id) @@ -519,7 +523,7 @@ func (f *pretty) longestStep(steps []*messages.GherkinDocument_Feature_Step, pic } // a line number representation in feature file -func (f *pretty) line(path string, loc *messages.Location) string { +func line(path string, loc *messages.Location) string { return " " + blackb(fmt.Sprintf("# %s:%d", path, loc.Line)) } diff --git a/fmt_progress.go b/fmt_progress.go index 2bfa531..f7f4ede 100644 --- a/fmt_progress.go +++ b/fmt_progress.go @@ -48,12 +48,13 @@ func (f *progress) Summary() { if sr.Status == failed { pickle := f.storage.mustGetPickle(sr.PickleID) pickleStep := f.storage.mustGetPickleStep(sr.PickleStepID) + feature := f.storage.mustGetFeature(pickle.Uri) - sc := f.findScenario(pickle.AstNodeIds[0]) + sc := feature.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]) + step := feature.findStep(pickleStep.AstNodeIds[0]) stepDesc := strings.TrimSpace(step.Keyword) + " " + pickleStep.Text stepLine := fmt.Sprintf("%s:%d", pickle.Uri, step.Location.Line) diff --git a/fmt_progress_test.go b/fmt_progress_test.go index 789c6ea..454d232 100644 --- a/fmt_progress_test.go +++ b/fmt_progress_test.go @@ -27,13 +27,15 @@ func TestProgressFormatterWhenStepPanics(t *testing.T) { gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + gd.Uri = path + ft := feature{GherkinDocument: gd} + ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) var buf bytes.Buffer w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&ft}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) s.Step(`^two$`, func() error { panic("omg") }) @@ -41,7 +43,8 @@ func TestProgressFormatterWhenStepPanics(t *testing.T) { } r.storage = newStorage() - for _, pickle := range pickles { + r.storage.mustInsertFeature(&ft) + for _, pickle := range ft.pickles { r.storage.mustInsertPickle(pickle) } @@ -49,7 +52,7 @@ func TestProgressFormatterWhenStepPanics(t *testing.T) { require.True(t, failed) actual := buf.String() - assert.Contains(t, actual, "godog/fmt_progress_test.go:39") + assert.Contains(t, actual, "godog/fmt_progress_test.go:41") } func TestProgressFormatterWithPanicInMultistep(t *testing.T) { @@ -58,13 +61,15 @@ func TestProgressFormatterWithPanicInMultistep(t *testing.T) { gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + gd.Uri = path + ft := feature{GherkinDocument: gd} + ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) var buf bytes.Buffer w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&ft}, initializer: func(s *Suite) { s.Step(`^sub1$`, func() error { return nil }) s.Step(`^sub-sub$`, func() error { return nil }) @@ -75,7 +80,8 @@ func TestProgressFormatterWithPanicInMultistep(t *testing.T) { } r.storage = newStorage() - for _, pickle := range pickles { + r.storage.mustInsertFeature(&ft) + for _, pickle := range ft.pickles { r.storage.mustInsertPickle(pickle) } @@ -89,13 +95,15 @@ func TestProgressFormatterMultistepTemplates(t *testing.T) { gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + gd.Uri = path + ft := feature{GherkinDocument: gd} + ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) var buf bytes.Buffer w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&ft}, initializer: func(s *Suite) { s.Step(`^sub-sub$`, func() error { return nil }) s.Step(`^substep$`, func() Steps { return Steps{"sub-sub", `unavailable "John" cost 5`, "one", "three"} }) @@ -105,7 +113,8 @@ func TestProgressFormatterMultistepTemplates(t *testing.T) { } r.storage = newStorage() - for _, pickle := range pickles { + r.storage.mustInsertFeature(&ft) + for _, pickle := range ft.pickles { r.storage.mustInsertPickle(pickle) } @@ -162,13 +171,15 @@ Feature: basic gd, err := gherkin.ParseGherkinDocument(strings.NewReader(featureSource), (&messages.Incrementing{}).NewId) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + gd.Uri = path + ft := feature{GherkinDocument: gd} + ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) var buf bytes.Buffer w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&ft}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) s.Step(`^two:$`, func(doc *messages.PickleStepArgument_PickleDocString) Steps { return Steps{"one"} }) @@ -176,7 +187,8 @@ Feature: basic } r.storage = newStorage() - for _, pickle := range pickles { + r.storage.mustInsertFeature(&ft) + for _, pickle := range ft.pickles { r.storage.mustInsertPickle(pickle) } @@ -197,7 +209,9 @@ Feature: basic gd, err := gherkin.ParseGherkinDocument(strings.NewReader(featureSource), (&messages.Incrementing{}).NewId) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + gd.Uri = path + ft := feature{GherkinDocument: gd} + ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) var subStep = `three: """ @@ -208,7 +222,7 @@ Feature: basic w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&ft}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) s.Step(`^two$`, func() Steps { return Steps{subStep} }) @@ -217,7 +231,8 @@ Feature: basic } r.storage = newStorage() - for _, pickle := range pickles { + r.storage.mustInsertFeature(&ft) + for _, pickle := range ft.pickles { r.storage.mustInsertPickle(pickle) } diff --git a/run.go b/run.go index 572c0ae..c058af9 100644 --- a/run.go +++ b/run.go @@ -237,6 +237,8 @@ func runWithOptions(suite string, runner runner, opt Options) int { runner.storage = newStorage() for _, feat := range runner.features { + runner.storage.mustInsertFeature(feat) + for _, pickle := range feat.pickles { runner.storage.mustInsertPickle(pickle) } diff --git a/run_test.go b/run_test.go index 9ea3674..08b13fa 100644 --- a/run_test.go +++ b/run_test.go @@ -64,11 +64,13 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) { gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + gd.Uri = path + ft := feature{GherkinDocument: gd} + ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) r := runner{ fmt: progressFunc("progress", ioutil.Discard), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&ft}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) s.Step(`^two$`, func() error { return ErrPending }) @@ -76,7 +78,8 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) { } r.storage = newStorage() - for _, pickle := range pickles { + r.storage.mustInsertFeature(&ft) + for _, pickle := range ft.pickles { r.storage.mustInsertPickle(pickle) } @@ -94,11 +97,13 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) { gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + gd.Uri = path + ft := feature{GherkinDocument: gd} + ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) r := runner{ fmt: progressFunc("progress", ioutil.Discard), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&ft}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) // two - is undefined @@ -106,7 +111,8 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) { } r.storage = newStorage() - for _, pickle := range pickles { + r.storage.mustInsertFeature(&ft) + for _, pickle := range ft.pickles { r.storage.mustInsertPickle(pickle) } @@ -124,11 +130,13 @@ func TestShouldFailOnError(t *testing.T) { gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + gd.Uri = path + ft := feature{GherkinDocument: gd} + ft.pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) r := runner{ fmt: progressFunc("progress", ioutil.Discard), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&ft}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) s.Step(`^two$`, func() error { return fmt.Errorf("error") }) @@ -136,7 +144,8 @@ func TestShouldFailOnError(t *testing.T) { } r.storage = newStorage() - for _, pickle := range pickles { + r.storage.mustInsertFeature(&ft) + for _, pickle := range ft.pickles { r.storage.mustInsertPickle(pickle) } diff --git a/storage.go b/storage.go index 5e3ecdc..d4d7e8a 100644 --- a/storage.go +++ b/storage.go @@ -11,6 +11,9 @@ const ( writeMode bool = true readMode bool = false + tableFeature string = "feature" + tableFeatureIndexURI string = "id" + tablePickle string = "pickle" tablePickleIndexID string = "id" tablePickleIndexURI string = "uri" @@ -35,6 +38,16 @@ func newStorage() *storage { // Create the DB schema schema := memdb.DBSchema{ Tables: map[string]*memdb.TableSchema{ + tableFeature: { + Name: tableFeature, + Indexes: map[string]*memdb.IndexSchema{ + tableFeatureIndexURI: { + Name: tableFeatureIndexURI, + Unique: true, + Indexer: &memdb.StringFieldIndex{Field: "Uri"}, + }, + }, + }, tablePickle: { Name: tablePickle, Indexes: map[string]*memdb.IndexSchema{ @@ -119,29 +132,12 @@ func (s *storage) mustInsertPickle(p *messages.Pickle) { } 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) - } - + v := s.mustFirst(tablePickle, tablePickleIndexID, id) 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) - } - + it := s.mustGet(tablePickle, tablePickleIndexURI, uri) for v := it.Next(); v != nil; v = it.Next() { ps = append(ps, v.(*messages.Pickle)) } @@ -150,73 +146,34 @@ func (s *storage) mustGetPickles(uri string) (ps []*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) - } - + v := s.mustFirst(tablePickleStep, tablePickleStepIndexID, id) 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() + s.mustInsert(tablePickleResult, pr) } func (s *storage) mustInsertPickleStepResult(psr pickleStepResult) { - txn := s.db.Txn(writeMode) - - if err := txn.Insert(tablePickleStepResult, psr); err != nil { - panic(err) - } - - txn.Commit() + s.mustInsert(tablePickleStepResult, psr) } func (s *storage) mustGetPickleResult(id string) pickleResult { - pr, err := s.getPickleResult(id) - if err != nil { - panic(err) - } - - return pr + v := s.mustFirst(tablePickleResult, tablePickleResultIndexPickleID, id) + return v.(pickleResult) } func (s *storage) getPickleResult(id string) (_ pickleResult, err error) { - txn := s.db.Txn(readMode) - defer txn.Abort() - - v, err := txn.First(tablePickleResult, tablePickleResultIndexPickleID, id) + v, err := s.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) - } - + it := s.mustGet(tablePickleResult, tablePickleResultIndexPickleID) for v := it.Next(); v != nil; v = it.Next() { prs = append(prs, v.(pickleResult)) } @@ -225,28 +182,12 @@ func (s *storage) mustGetPickleResults() (prs []pickleResult) { } 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) - } - + v := s.mustFirst(tablePickleStepResult, tablePickleStepResultIndexPickleStepID, 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) - } - + it := s.mustGet(tablePickleStepResult, tablePickleStepResultIndexPickleID, pickleID) for v := it.Next(); v != nil; v = it.Next() { psrs = append(psrs, v.(pickleStepResult)) } @@ -255,17 +196,74 @@ func (s *storage) mustGetPickleStepResultsByPickleID(pickleID string) (psrs []pi } 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) - } - + it := s.mustGet(tablePickleStepResult, tablePickleStepResultIndexStatus, status) for v := it.Next(); v != nil; v = it.Next() { psrs = append(psrs, v.(pickleStepResult)) } return psrs } + +func (s *storage) mustInsertFeature(f *feature) { + s.mustInsert(tableFeature, f) +} + +func (s *storage) mustGetFeature(uri string) *feature { + v := s.mustFirst(tableFeature, tableFeatureIndexURI, uri) + return v.(*feature) +} + +func (s *storage) mustGetFeatures() (fs []*feature) { + it := s.mustGet(tableFeature, tableFeatureIndexURI) + for v := it.Next(); v != nil; v = it.Next() { + fs = append(fs, v.(*feature)) + } + + return +} + +func (s *storage) mustInsert(table string, obj interface{}) { + txn := s.db.Txn(writeMode) + + if err := txn.Insert(table, obj); err != nil { + panic(err) + } + + txn.Commit() +} + +func (s *storage) first(table, index string, args ...interface{}) (v interface{}, err error) { + txn := s.db.Txn(readMode) + defer txn.Abort() + + v, err = txn.First(table, index, args...) + if err != nil { + return + } else if v == nil { + err = fmt.Errorf("Couldn't find index: %q in table: %q with args: %+v", index, table, args) + return + } + + return +} + +func (s *storage) mustFirst(table, index string, args ...interface{}) interface{} { + v, err := s.first(table, index, args...) + if err != nil { + panic(err) + } + + return v +} + +func (s *storage) mustGet(table, index string, args ...interface{}) memdb.ResultIterator { + txn := s.db.Txn(readMode) + defer txn.Abort() + + it, err := txn.Get(table, index, args...) + if err != nil { + panic(err) + } + + return it +} diff --git a/suite.go b/suite.go index 89e662b..c483bda 100644 --- a/suite.go +++ b/suite.go @@ -98,11 +98,11 @@ func (f feature) startedAt() time.Time { return f.time } -type sortByName []*feature +type sortFeaturesByName []*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] } +func (s sortFeaturesByName) Len() int { return len(s) } +func (s sortFeaturesByName) Less(i, j int) bool { return s[i].Feature.Name < s[j].Feature.Name } +func (s sortFeaturesByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } type sortPicklesByID []*messages.Pickle diff --git a/suite_context.go b/suite_context.go index 2daf8e2..3f15ad3 100644 --- a/suite_context.go +++ b/suite_context.go @@ -184,6 +184,8 @@ func (s *suiteContext) iRunFeatureSuiteWithTags(tags string) error { s.testedSuite.storage = newStorage() for _, feat := range s.testedSuite.features { + s.testedSuite.storage.mustInsertFeature(feat) + for _, pickle := range feat.pickles { s.testedSuite.storage.mustInsertPickle(pickle) } @@ -212,6 +214,8 @@ func (s *suiteContext) iRunFeatureSuiteWithFormatter(name string) error { s.testedSuite.storage = newStorage() for _, feat := range s.testedSuite.features { + s.testedSuite.storage.mustInsertFeature(feat) + for _, pickle := range feat.pickles { s.testedSuite.storage.mustInsertPickle(pickle) } diff --git a/suite_context_test.go b/suite_context_test.go index b4adcb5..9353f53 100644 --- a/suite_context_test.go +++ b/suite_context_test.go @@ -146,6 +146,8 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTags(tags string) error { tc.testedSuite.storage = newStorage() for _, feat := range tc.testedSuite.features { + tc.testedSuite.storage.mustInsertFeature(feat) + for _, pickle := range feat.pickles { tc.testedSuite.storage.mustInsertPickle(pickle) } @@ -174,6 +176,8 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithFormatter(name string) erro tc.testedSuite.storage = newStorage() for _, feat := range tc.testedSuite.features { + tc.testedSuite.storage.mustInsertFeature(feat) + for _, pickle := range feat.pickles { tc.testedSuite.storage.mustInsertPickle(pickle) }