Merge pull request #138 from rvflash/master
godog command is on error with go modules and sub-packages
Этот коммит содержится в:
коммит
138e107f34
14 изменённых файлов: 103 добавлений и 117 удалений
|
@ -241,6 +241,8 @@ func makeImportValid(r rune) rune {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type void struct{}
|
||||||
|
|
||||||
func uniqStringList(strs []string) (unique []string) {
|
func uniqStringList(strs []string) (unique []string) {
|
||||||
uniq := make(map[string]void, len(strs))
|
uniq := make(map[string]void, len(strs))
|
||||||
for _, s := range strs {
|
for _, s := range strs {
|
||||||
|
|
102
builder_go110.go
102
builder_go110.go
|
@ -4,6 +4,7 @@ package godog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
"go/build"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
|
@ -19,16 +20,15 @@ import (
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
var tooldir = findToolDir()
|
var (
|
||||||
var compiler = filepath.Join(tooldir, "compile")
|
tooldir = findToolDir()
|
||||||
var linker = filepath.Join(tooldir, "link")
|
compiler = filepath.Join(tooldir, "compile")
|
||||||
var gopaths = filepath.SplitList(build.Default.GOPATH)
|
linker = filepath.Join(tooldir, "link")
|
||||||
var goarch = build.Default.GOARCH
|
gopaths = filepath.SplitList(build.Default.GOPATH)
|
||||||
var goroot = build.Default.GOROOT
|
godogImportPath = "github.com/DATA-DOG/godog"
|
||||||
var goos = build.Default.GOOS
|
|
||||||
|
|
||||||
var godogImportPath = "github.com/DATA-DOG/godog"
|
// godep
|
||||||
var runnerTemplate = template.Must(template.New("testmain").Parse(`package main
|
runnerTemplate = template.Must(template.New("testmain").Parse(`package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/DATA-DOG/godog"
|
"github.com/DATA-DOG/godog"
|
||||||
|
@ -45,6 +45,7 @@ func main() {
|
||||||
})
|
})
|
||||||
os.Exit(status)
|
os.Exit(status)
|
||||||
}`))
|
}`))
|
||||||
|
)
|
||||||
|
|
||||||
// Build creates a test package like go test command at given target path.
|
// Build creates a test package like go test command at given target path.
|
||||||
// If there are no go files in tested directory, then
|
// If there are no go files in tested directory, then
|
||||||
|
@ -299,60 +300,78 @@ func makeImportValid(r rune) rune {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func uniqStringList(strs []string) (unique []string) {
|
|
||||||
uniq := make(map[string]void, len(strs))
|
|
||||||
for _, s := range strs {
|
|
||||||
if _, ok := uniq[s]; !ok {
|
|
||||||
uniq[s] = void{}
|
|
||||||
unique = append(unique, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildTestMain if given package is valid
|
// buildTestMain if given package is valid
|
||||||
// it scans test files for contexts
|
// it scans test files for contexts
|
||||||
// and produces a testmain source code.
|
// and produces a testmain source code.
|
||||||
func buildTestMain(pkg *build.Package) ([]byte, bool, error) {
|
func buildTestMain(pkg *build.Package) ([]byte, bool, error) {
|
||||||
var contexts []string
|
var (
|
||||||
var importPath string
|
contexts []string
|
||||||
name := "main"
|
err error
|
||||||
|
name, importPath string
|
||||||
|
)
|
||||||
if nil != pkg {
|
if nil != pkg {
|
||||||
ctxs, err := processPackageTestFiles(
|
contexts, err = processPackageTestFiles(
|
||||||
pkg.TestGoFiles,
|
pkg.TestGoFiles,
|
||||||
pkg.XTestGoFiles,
|
pkg.XTestGoFiles,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
contexts = ctxs
|
importPath = parseImport(pkg.ImportPath, pkg.Root)
|
||||||
|
|
||||||
// for module support, query the module import path
|
|
||||||
// @TODO: maybe there is a better way to read it
|
|
||||||
out, err := exec.Command("go", "list", "-m").CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
// is not using modules or older go version
|
|
||||||
importPath = pkg.ImportPath
|
|
||||||
} else {
|
|
||||||
// otherwise read the module name from command output
|
|
||||||
importPath = strings.TrimSpace(string(out))
|
|
||||||
}
|
|
||||||
name = pkg.Name
|
name = pkg.Name
|
||||||
|
} else {
|
||||||
|
name = "main"
|
||||||
}
|
}
|
||||||
|
|
||||||
data := struct {
|
data := struct {
|
||||||
Name string
|
Name string
|
||||||
Contexts []string
|
Contexts []string
|
||||||
ImportPath string
|
ImportPath string
|
||||||
}{name, contexts, importPath}
|
}{
|
||||||
|
Name: name,
|
||||||
|
Contexts: contexts,
|
||||||
|
ImportPath: importPath,
|
||||||
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := runnerTemplate.Execute(&buf, data); err != nil {
|
if err = runnerTemplate.Execute(&buf, data); err != nil {
|
||||||
return nil, len(contexts) > 0, err
|
return nil, len(contexts) > 0, err
|
||||||
}
|
}
|
||||||
return buf.Bytes(), len(contexts) > 0, nil
|
return buf.Bytes(), len(contexts) > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseImport parses the import path to deal with go module.
|
||||||
|
func parseImport(rawPath, rootPath string) string {
|
||||||
|
// with go > 1.11 and go module enabled out of the GOPATH,
|
||||||
|
// the import path begins with an underscore and the GOPATH is unknown on build.
|
||||||
|
if rootPath != "" {
|
||||||
|
// go < 1.11 or it's a module inside the GOPATH
|
||||||
|
return rawPath
|
||||||
|
}
|
||||||
|
// for module support, query the module import path
|
||||||
|
cmd := exec.Command("go", "list", "-m", "-json")
|
||||||
|
out, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
// Unable to read stdout
|
||||||
|
return rawPath
|
||||||
|
}
|
||||||
|
if cmd.Start() != nil {
|
||||||
|
// Does not using modules
|
||||||
|
return rawPath
|
||||||
|
}
|
||||||
|
var mod struct {
|
||||||
|
Dir string `json:"Dir"`
|
||||||
|
Path string `json:"Path"`
|
||||||
|
}
|
||||||
|
if json.NewDecoder(out).Decode(&mod) != nil {
|
||||||
|
// Unexpected result
|
||||||
|
return rawPath
|
||||||
|
}
|
||||||
|
if cmd.Wait() != nil {
|
||||||
|
return rawPath
|
||||||
|
}
|
||||||
|
// Concatenates the module path with the current sub-folders if needed
|
||||||
|
return mod.Path + filepath.ToSlash(strings.TrimPrefix(strings.TrimPrefix(rawPath, "_"), mod.Dir))
|
||||||
|
}
|
||||||
|
|
||||||
// processPackageTestFiles runs through ast of each test
|
// processPackageTestFiles runs through ast of each test
|
||||||
// file pack and looks for godog suite contexts to register
|
// file pack and looks for godog suite contexts to register
|
||||||
// on run
|
// on run
|
||||||
|
@ -400,16 +419,13 @@ func dependencies(pkg *build.Package, visited map[string]string, vendor bool) er
|
||||||
if i := strings.LastIndex(name, "vendor/"); vendor && i == -1 {
|
if i := strings.LastIndex(name, "vendor/"); vendor && i == -1 {
|
||||||
continue // only interested in vendor packages
|
continue // only interested in vendor packages
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := visited[name]; ok {
|
if _, ok := visited[name]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
next, err := locatePackage(name)
|
next, err := locatePackage(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
visited[name] = pkg.PkgObj
|
visited[name] = pkg.PkgObj
|
||||||
if err := dependencies(next, visited, vendor); err != nil {
|
if err := dependencies(next, visited, vendor); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -27,7 +27,7 @@ func TestBuildTestRunnerWithoutGoFiles(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Chdir(pwd) // get back to current dir
|
_ = os.Chdir(pwd) // get back to current dir
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := Build(bin); err != nil {
|
if err := Build(bin); err != nil {
|
||||||
|
|
|
@ -3,19 +3,15 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
"go/build"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/DATA-DOG/godog"
|
"github.com/DATA-DOG/godog"
|
||||||
"github.com/DATA-DOG/godog/colors"
|
"github.com/DATA-DOG/godog/colors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var statusMatch = regexp.MustCompile("^exit status (\\d+)")
|
|
||||||
var parsedStatus int
|
var parsedStatus int
|
||||||
|
|
||||||
func buildAndRun() (int, error) {
|
func buildAndRun() (int, error) {
|
||||||
|
@ -107,20 +103,3 @@ func main() {
|
||||||
}
|
}
|
||||||
os.Exit(status)
|
os.Exit(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func statusOutputFilter(w io.Writer) io.Writer {
|
|
||||||
return writerFunc(func(b []byte) (int, error) {
|
|
||||||
if m := statusMatch.FindStringSubmatch(string(b)); len(m) > 1 {
|
|
||||||
parsedStatus, _ = strconv.Atoi(m[1])
|
|
||||||
// skip status stderr output
|
|
||||||
return len(b), nil
|
|
||||||
}
|
|
||||||
return w.Write(b)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type writerFunc func([]byte) (int, error)
|
|
||||||
|
|
||||||
func (w writerFunc) Write(b []byte) (int, error) {
|
|
||||||
return w(b)
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,8 +16,8 @@ const (
|
||||||
red
|
red
|
||||||
green
|
green
|
||||||
yellow
|
yellow
|
||||||
blue
|
blue // unused
|
||||||
magenta
|
magenta // unused
|
||||||
cyan
|
cyan
|
||||||
white
|
white
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,7 +16,7 @@ type outputMode int
|
||||||
const (
|
const (
|
||||||
_ outputMode = iota
|
_ outputMode = iota
|
||||||
discardNonColorEscSeq
|
discardNonColorEscSeq
|
||||||
outputNonColorEscSeq
|
outputNonColorEscSeq // unused
|
||||||
)
|
)
|
||||||
|
|
||||||
// Colored creates and initializes a new ansiColorWriter
|
// Colored creates and initializes a new ansiColorWriter
|
||||||
|
|
13
fmt.go
13
fmt.go
|
@ -381,9 +381,11 @@ func (f *basefmt) Summary() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *undefinedSnippet) Args() (ret string) {
|
func (s *undefinedSnippet) Args() (ret string) {
|
||||||
var args []string
|
var (
|
||||||
var pos, idx int
|
args []string
|
||||||
var breakLoop bool
|
pos int
|
||||||
|
breakLoop bool
|
||||||
|
)
|
||||||
for !breakLoop {
|
for !breakLoop {
|
||||||
part := s.Expr[pos:]
|
part := s.Expr[pos:]
|
||||||
ipos := strings.Index(part, "(\\d+)")
|
ipos := strings.Index(part, "(\\d+)")
|
||||||
|
@ -392,25 +394,20 @@ func (s *undefinedSnippet) Args() (ret string) {
|
||||||
case spos == -1 && ipos == -1:
|
case spos == -1 && ipos == -1:
|
||||||
breakLoop = true
|
breakLoop = true
|
||||||
case spos == -1:
|
case spos == -1:
|
||||||
idx++
|
|
||||||
pos += ipos + len("(\\d+)")
|
pos += ipos + len("(\\d+)")
|
||||||
args = append(args, reflect.Int.String())
|
args = append(args, reflect.Int.String())
|
||||||
case ipos == -1:
|
case ipos == -1:
|
||||||
idx++
|
|
||||||
pos += spos + len("\"([^\"]*)\"")
|
pos += spos + len("\"([^\"]*)\"")
|
||||||
args = append(args, reflect.String.String())
|
args = append(args, reflect.String.String())
|
||||||
case ipos < spos:
|
case ipos < spos:
|
||||||
idx++
|
|
||||||
pos += ipos + len("(\\d+)")
|
pos += ipos + len("(\\d+)")
|
||||||
args = append(args, reflect.Int.String())
|
args = append(args, reflect.Int.String())
|
||||||
case spos < ipos:
|
case spos < ipos:
|
||||||
idx++
|
|
||||||
pos += spos + len("\"([^\"]*)\"")
|
pos += spos + len("\"([^\"]*)\"")
|
||||||
args = append(args, reflect.String.String())
|
args = append(args, reflect.String.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.argument != nil {
|
if s.argument != nil {
|
||||||
idx++
|
|
||||||
switch s.argument.(type) {
|
switch s.argument.(type) {
|
||||||
case *gherkin.DocString:
|
case *gherkin.DocString:
|
||||||
args = append(args, "*gherkin.DocString")
|
args = append(args, "*gherkin.DocString")
|
||||||
|
|
|
@ -109,15 +109,14 @@ type cukefmt struct {
|
||||||
// this is sadly not passed by gherkin nodes.
|
// this is sadly not passed by gherkin nodes.
|
||||||
// it restricts this formatter to run only in synchronous single
|
// it restricts this formatter to run only in synchronous single
|
||||||
// threaded execution. Unless running a copy of formatter for each feature
|
// threaded execution. Unless running a copy of formatter for each feature
|
||||||
path string
|
path string
|
||||||
stat stepType // last step status, before skipped
|
stat stepType // last step status, before skipped
|
||||||
outlineSteps int // number of current outline scenario steps
|
ID string // current test id.
|
||||||
ID string // current test id.
|
results []cukeFeatureJSON // structure that represent cuke results
|
||||||
results []cukeFeatureJSON // structure that represent cuke results
|
curStep *cukeStep // track the current step
|
||||||
curStep *cukeStep // track the current step
|
curElement *cukeElement // track the current element
|
||||||
curElement *cukeElement // track the current element
|
curFeature *cukeFeatureJSON // track the current feature
|
||||||
curFeature *cukeFeatureJSON // track the current feature
|
curOutline cukeElement // Each example show up as an outline element but the outline is parsed only once
|
||||||
curOutline cukeElement // Each example show up as an outline element but the outline is parsed only once
|
|
||||||
// so I need to keep track of the current outline
|
// so I need to keep track of the current outline
|
||||||
curRow int // current row of the example table as it is being processed.
|
curRow int // current row of the example table as it is being processed.
|
||||||
curExampleTags []cukeTag // temporary storage for tags associate with the current example table.
|
curExampleTags []cukeTag // temporary storage for tags associate with the current example table.
|
||||||
|
|
|
@ -148,11 +148,13 @@ func (j *junitFormatter) Summary() {
|
||||||
j.current().Time = timeNowFunc().Sub(j.featStarted).String()
|
j.current().Time = timeNowFunc().Sub(j.featStarted).String()
|
||||||
}
|
}
|
||||||
j.suite.Time = timeNowFunc().Sub(j.started).String()
|
j.suite.Time = timeNowFunc().Sub(j.started).String()
|
||||||
io.WriteString(j.out, xml.Header)
|
_, err := io.WriteString(j.out, xml.Header)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "failed to write junit string:", err)
|
||||||
|
}
|
||||||
enc := xml.NewEncoder(j.out)
|
enc := xml.NewEncoder(j.out)
|
||||||
enc.Indent("", s(2))
|
enc.Indent("", s(2))
|
||||||
if err := enc.Encode(j.suite); err != nil {
|
if err = enc.Encode(j.suite); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "failed to write junit xml:", err)
|
fmt.Fprintln(os.Stderr, "failed to write junit xml:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,19 +152,18 @@ func TestJUnitFormatterOutput(t *testing.T) {
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.run()
|
s.run()
|
||||||
s.fmt.Summary()
|
s.fmt.Summary()
|
||||||
|
|
||||||
var exp bytes.Buffer
|
var exp bytes.Buffer
|
||||||
io.WriteString(&exp, xml.Header)
|
if _, err = io.WriteString(&exp, xml.Header); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
enc := xml.NewEncoder(&exp)
|
}
|
||||||
enc.Indent("", " ")
|
enc := xml.NewEncoder(&exp)
|
||||||
if err := enc.Encode(expected); err != nil {
|
enc.Indent("", " ")
|
||||||
|
if err = enc.Encode(expected); err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if buf.String() != exp.String() {
|
if buf.String() != exp.String() {
|
||||||
t.Fatalf("expected output does not match: %s", buf.String())
|
t.Fatalf("expected output does not match: %s", buf.String())
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ func (f *progress) Feature(ft *gherkin.Feature, p string, c []byte) {
|
||||||
func (f *progress) Summary() {
|
func (f *progress) Summary() {
|
||||||
left := math.Mod(float64(f.steps), float64(f.stepsPerRow))
|
left := math.Mod(float64(f.steps), float64(f.stepsPerRow))
|
||||||
if left != 0 {
|
if left != 0 {
|
||||||
if int(f.steps) > f.stepsPerRow {
|
if f.steps > f.stepsPerRow {
|
||||||
fmt.Fprintf(f.out, s(f.stepsPerRow-int(left))+fmt.Sprintf(" %d\n", f.steps))
|
fmt.Fprintf(f.out, s(f.stepsPerRow-int(left))+fmt.Sprintf(" %d\n", f.steps))
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(f.out, " %d\n", f.steps)
|
fmt.Fprintf(f.out, " %d\n", f.steps)
|
||||||
|
|
|
@ -178,8 +178,8 @@ func TestFailsWithUnknownFormatterOptionError(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out := strings.TrimSpace(string(b))
|
out := strings.TrimSpace(string(b))
|
||||||
if strings.Index(out, `unregistered formatter name: "unknown", use one of`) == -1 {
|
if !strings.Contains(out, `unregistered formatter name: "unknown", use one of`) {
|
||||||
t.Fatalf("unexpected error output: \"%s\"", out)
|
t.Fatalf("unexpected error output: %q", out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
suite.go
8
suite.go
|
@ -402,12 +402,6 @@ func (s *Suite) runSteps(steps []*gherkin.Step) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) skipSteps(steps []*gherkin.Step) {
|
|
||||||
for _, step := range steps {
|
|
||||||
s.fmt.Skipped(step, s.matchStep(step))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) runOutline(outline *gherkin.ScenarioOutline, b *gherkin.Background) (failErr error) {
|
func (s *Suite) runOutline(outline *gherkin.ScenarioOutline, b *gherkin.Background) (failErr error) {
|
||||||
s.fmt.Node(outline)
|
s.fmt.Node(outline)
|
||||||
|
|
||||||
|
@ -811,7 +805,7 @@ func matchesTags(filter string, tags []string) (ok bool) {
|
||||||
okComma = hasTag(tags, tag) || okComma
|
okComma = hasTag(tags, tag) || okComma
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ok = (false != okComma && ok && okComma) || false
|
ok = ok && okComma
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
22
utils.go
22
utils.go
|
@ -7,18 +7,16 @@ import (
|
||||||
"github.com/DATA-DOG/godog/colors"
|
"github.com/DATA-DOG/godog/colors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// empty struct value takes no space allocation
|
var (
|
||||||
type void struct{}
|
red = colors.Red
|
||||||
|
redb = colors.Bold(colors.Red)
|
||||||
var red = colors.Red
|
green = colors.Green
|
||||||
var redb = colors.Bold(colors.Red)
|
black = colors.Black
|
||||||
var green = colors.Green
|
yellow = colors.Yellow
|
||||||
var black = colors.Black
|
cyan = colors.Cyan
|
||||||
var blackb = colors.Bold(colors.Black)
|
cyanb = colors.Bold(colors.Cyan)
|
||||||
var yellow = colors.Yellow
|
whiteb = colors.Bold(colors.White)
|
||||||
var cyan = colors.Cyan
|
)
|
||||||
var cyanb = colors.Bold(colors.Cyan)
|
|
||||||
var whiteb = colors.Bold(colors.White)
|
|
||||||
|
|
||||||
// repeats a space n times
|
// repeats a space n times
|
||||||
func s(n int) string {
|
func s(n int) string {
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче