godog/internal/parser/parser.go
2020-07-05 18:40:56 +02:00

167 строки
3,5 КиБ
Go

package parser
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/cucumber/gherkin-go/v11"
"github.com/cucumber/messages-go/v10"
"github.com/cucumber/godog/internal/models"
"github.com/cucumber/godog/internal/tags"
)
var pathLineRe = regexp.MustCompile(`:([\d]+)$`)
// ExtractFeaturePathLine ...
func ExtractFeaturePathLine(p string) (string, int) {
line := -1
retPath := p
if m := pathLineRe.FindStringSubmatch(p); len(m) > 0 {
if i, err := strconv.Atoi(m[1]); err == nil {
line = i
retPath = p[:strings.LastIndexByte(p, ':')]
}
}
return retPath, line
}
func parseFeatureFile(path string, newIDFunc func() string) (*models.Feature, error) {
reader, err := os.Open(path)
if err != nil {
return nil, err
}
defer reader.Close()
var buf bytes.Buffer
gherkinDocument, err := gherkin.ParseGherkinDocument(io.TeeReader(reader, &buf), newIDFunc)
if err != nil {
return nil, fmt.Errorf("%s - %v", path, err)
}
gherkinDocument.Uri = path
pickles := gherkin.Pickles(*gherkinDocument, path, newIDFunc)
f := models.Feature{GherkinDocument: gherkinDocument, Pickles: pickles, Content: buf.Bytes()}
return &f, nil
}
func parseFeatureDir(dir string, newIDFunc func() string) ([]*models.Feature, error) {
var features []*models.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, newIDFunc)
if err != nil {
return err
}
features = append(features, feat)
return nil
})
}
func parsePath(path string) ([]*models.Feature, error) {
var features []*models.Feature
path, line := ExtractFeaturePathLine(path)
fi, err := os.Stat(path)
if err != nil {
return features, err
}
newIDFunc := (&messages.Incrementing{}).NewId
if fi.IsDir() {
return parseFeatureDir(path, newIDFunc)
}
ft, err := parseFeatureFile(path, newIDFunc)
if err != nil {
return features, err
}
// filter scenario by line number
var pickles []*messages.Pickle
for _, pickle := range ft.Pickles {
sc := ft.FindScenario(pickle.AstNodeIds[0])
if line == -1 || uint32(line) == sc.Location.Line {
pickles = append(pickles, pickle)
}
}
ft.Pickles = pickles
return append(features, ft), nil
}
// ParseFeatures ...
func ParseFeatures(filter string, paths []string) ([]*models.Feature, error) {
var order int
featureIdxs := make(map[string]int)
uniqueFeatureURI := make(map[string]*models.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)
case os.IsPermission(err):
return nil, fmt.Errorf(`feature path "%s" is not accessible`, path)
case err != nil:
return nil, err
}
for _, ft := range feats {
if _, duplicate := uniqueFeatureURI[ft.Uri]; duplicate {
continue
}
uniqueFeatureURI[ft.Uri] = ft
featureIdxs[ft.Uri] = order
order++
}
}
var features = make([]*models.Feature, len(uniqueFeatureURI))
for uri, feature := range uniqueFeatureURI {
idx := featureIdxs[uri]
features[idx] = feature
}
features = filterFeatures(filter, features)
return features, nil
}
func filterFeatures(filter string, features []*models.Feature) (result []*models.Feature) {
for _, ft := range features {
ft.Pickles = tags.ApplyTagFilter(filter, ft.Pickles)
if ft.Feature != nil && len(ft.Pickles) > 0 {
result = append(result, ft)
}
}
return
}