Merge pull request #138 from rvflash/master

godog command is on error with go modules and sub-packages
Этот коммит содержится в:
Gediminas Morkevicius 2018-10-04 15:47:43 +03:00 коммит произвёл GitHub
родитель 5c352074bc f92dc4c9b7
коммит 138e107f34
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
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 {

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

@ -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
Просмотреть файл

@ -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)
} }
} }

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

@ -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
} }

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

@ -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 {