From 338a78050ceeefd6b11018a3c653339a140cfbbd Mon Sep 17 00:00:00 2001 From: gedi Date: Fri, 12 Apr 2019 11:15:19 +0300 Subject: [PATCH] closes #170 --- fmt_progress_test.go | 2 +- go.mod | 2 + suite.go | 162 ++++++++++++++++++++++++++----------------- 3 files changed, 100 insertions(+), 66 deletions(-) diff --git a/fmt_progress_test.go b/fmt_progress_test.go index 2788cd7..3e696a4 100644 --- a/fmt_progress_test.go +++ b/fmt_progress_test.go @@ -114,7 +114,7 @@ func TestProgressFormatterWhenStepPanics(t *testing.T) { } out := buf.String() - if idx := strings.Index(out, "github.com/DATA-DOG/godog/fmt_progress_test.go:108"); idx == -1 { + if idx := strings.Index(out, "godog/fmt_progress_test.go:108"); idx == -1 { t.Fatalf("expected to find panic stacktrace, actual:\n%s", out) } } diff --git a/go.mod b/go.mod index 9d370bb..ed7a335 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,3 @@ module github.com/DATA-DOG/godog + +go 1.12 diff --git a/suite.go b/suite.go index 4230945..54cbe0c 100644 --- a/suite.go +++ b/suite.go @@ -22,10 +22,9 @@ var typeOfBytes = reflect.TypeOf([]byte(nil)) type feature struct { *gherkin.Feature - Content []byte `json:"-"` - Path string `json:"path"` - scenarios map[int]bool - order int + Content []byte `json:"-"` + Path string `json:"path"` + order int } // ErrUndefined is returned in case if step definition was not found @@ -631,56 +630,93 @@ func extractFeaturePathLine(p string) (string, int) { return retPath, line } +func parseFeatureFile(path string) (*feature, error) { + reader, err := os.Open(path) + if err != nil { + return nil, err + } + defer reader.Close() + + var buf bytes.Buffer + ft, err := gherkin.ParseFeature(io.TeeReader(reader, &buf)) + if err != nil { + return nil, fmt.Errorf("%s - %v", path, err) + } + + return &feature{ + Path: path, + Feature: ft, + Content: buf.Bytes(), + }, nil +} + +func parseFeatureDir(dir string) ([]*feature, error) { + var features []*feature + return features, filepath.Walk(dir, func(p string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + if f.IsDir() { + return nil + } + + if !strings.HasSuffix(p, ".feature") { + return nil + } + + feat, err := parseFeatureFile(p) + if err != nil { + return err + } + features = append(features, feat) + return nil + }) +} + +func parsePath(path string) ([]*feature, error) { + var features []*feature + // check if line number is specified + var line int + path, line = extractFeaturePathLine(path) + + fi, err := os.Stat(path) + if err != nil { + return features, err + } + + if fi.IsDir() { + return parseFeatureDir(path) + } + + ft, err := parseFeatureFile(path) + if err != nil { + return features, err + } + + // filter scenario by line number + var scenarios []interface{} + for _, def := range ft.ScenarioDefinitions { + var ln int + switch t := def.(type) { + case *gherkin.Scenario: + ln = t.Location.Line + case *gherkin.ScenarioOutline: + ln = t.Location.Line + } + if line == -1 || ln == line { + scenarios = append(scenarios, def) + } + } + ft.ScenarioDefinitions = scenarios + return append(features, ft), nil +} + func parseFeatures(filter string, paths []string) ([]*feature, error) { byPath := make(map[string]*feature) var order int - for _, pat := range paths { - // check if line number is specified - path, line := extractFeaturePathLine(pat) - var err error - // parse features - err = filepath.Walk(path, func(p string, f os.FileInfo, err error) error { - if err == nil && !f.IsDir() && strings.HasSuffix(p, ".feature") { - reader, err := os.Open(p) - if err != nil { - return err - } - var buf bytes.Buffer - ft, err := gherkin.ParseFeature(io.TeeReader(reader, &buf)) - reader.Close() - if err != nil { - return fmt.Errorf("%s - %v", p, err) - } - - feat := byPath[p] - if feat == nil { - feat = &feature{ - Path: p, - Feature: ft, - Content: buf.Bytes(), - scenarios: make(map[int]bool), - order: order, - } - order++ - byPath[p] = feat - } - // filter scenario by line number - for _, def := range ft.ScenarioDefinitions { - var ln int - switch t := def.(type) { - case *gherkin.Scenario: - ln = t.Location.Line - case *gherkin.ScenarioOutline: - ln = t.Location.Line - } - if line == -1 || ln == line { - feat.scenarios[ln] = true - } - } - } - return err - }) - // check error + for _, path := range paths { + feats, err := parsePath(path) switch { case os.IsNotExist(err): return nil, fmt.Errorf(`feature path "%s" is not available`, path) @@ -689,6 +725,16 @@ func parseFeatures(filter string, paths []string) ([]*feature, error) { case err != nil: return nil, err } + + for _, ft := range feats { + if _, duplicate := byPath[ft.Path]; duplicate { + continue + } + + ft.order = order + order++ + byPath[ft.Path] = ft + } } return filterFeatures(filter, byPath), nil } @@ -701,20 +747,6 @@ func (s sortByOrderGiven) 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 { - var scenarios []interface{} - for _, def := range ft.ScenarioDefinitions { - var ln int - switch t := def.(type) { - case *gherkin.Scenario: - ln = t.Location.Line - case *gherkin.ScenarioOutline: - ln = t.Location.Line - } - if ft.scenarios[ln] { - scenarios = append(scenarios, def) - } - } - ft.ScenarioDefinitions = scenarios applyTagFilter(tags, ft.Feature) features = append(features, ft) }