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
}
type void struct{}
func uniqStringList(strs []string) (unique []string) {
uniq := make(map[string]void, len(strs))
for _, s := range strs {

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

@ -4,6 +4,7 @@ package godog
import (
"bytes"
"encoding/json"
"fmt"
"go/build"
"go/parser"
@ -19,16 +20,15 @@ import (
"unicode"
)
var tooldir = findToolDir()
var compiler = filepath.Join(tooldir, "compile")
var linker = filepath.Join(tooldir, "link")
var gopaths = filepath.SplitList(build.Default.GOPATH)
var goarch = build.Default.GOARCH
var goroot = build.Default.GOROOT
var goos = build.Default.GOOS
var (
tooldir = findToolDir()
compiler = filepath.Join(tooldir, "compile")
linker = filepath.Join(tooldir, "link")
gopaths = filepath.SplitList(build.Default.GOPATH)
godogImportPath = "github.com/DATA-DOG/godog"
var godogImportPath = "github.com/DATA-DOG/godog"
var runnerTemplate = template.Must(template.New("testmain").Parse(`package main
// godep
runnerTemplate = template.Must(template.New("testmain").Parse(`package main
import (
"github.com/DATA-DOG/godog"
@ -45,6 +45,7 @@ func main() {
})
os.Exit(status)
}`))
)
// Build creates a test package like go test command at given target path.
// If there are no go files in tested directory, then
@ -299,60 +300,78 @@ func makeImportValid(r rune) rune {
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
// it scans test files for contexts
// and produces a testmain source code.
func buildTestMain(pkg *build.Package) ([]byte, bool, error) {
var contexts []string
var importPath string
name := "main"
var (
contexts []string
err error
name, importPath string
)
if nil != pkg {
ctxs, err := processPackageTestFiles(
contexts, err = processPackageTestFiles(
pkg.TestGoFiles,
pkg.XTestGoFiles,
)
if err != nil {
return nil, false, err
}
contexts = ctxs
// 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))
}
importPath = parseImport(pkg.ImportPath, pkg.Root)
name = pkg.Name
} else {
name = "main"
}
data := struct {
Name string
Contexts []string
ImportPath string
}{name, contexts, importPath}
}{
Name: name,
Contexts: contexts,
ImportPath: importPath,
}
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 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
// file pack and looks for godog suite contexts to register
// 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 {
continue // only interested in vendor packages
}
if _, ok := visited[name]; ok {
continue
}
next, err := locatePackage(name)
if err != nil {
return err
}
visited[name] = pkg.PkgObj
if err := dependencies(next, visited, vendor); err != nil {
return err

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

@ -27,7 +27,7 @@ func TestBuildTestRunnerWithoutGoFiles(t *testing.T) {
}
defer func() {
os.Chdir(pwd) // get back to current dir
_ = os.Chdir(pwd) // get back to current dir
}()
if err := Build(bin); err != nil {

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

@ -3,19 +3,15 @@ package main
import (
"fmt"
"go/build"
"io"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"syscall"
"github.com/DATA-DOG/godog"
"github.com/DATA-DOG/godog/colors"
)
var statusMatch = regexp.MustCompile("^exit status (\\d+)")
var parsedStatus int
func buildAndRun() (int, error) {
@ -107,20 +103,3 @@ func main() {
}
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
green
yellow
blue
magenta
blue // unused
magenta // unused
cyan
white
)

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

@ -16,7 +16,7 @@ type outputMode int
const (
_ outputMode = iota
discardNonColorEscSeq
outputNonColorEscSeq
outputNonColorEscSeq // unused
)
// Colored creates and initializes a new ansiColorWriter

13
fmt.go
Просмотреть файл

@ -381,9 +381,11 @@ func (f *basefmt) Summary() {
}
func (s *undefinedSnippet) Args() (ret string) {
var args []string
var pos, idx int
var breakLoop bool
var (
args []string
pos int
breakLoop bool
)
for !breakLoop {
part := s.Expr[pos:]
ipos := strings.Index(part, "(\\d+)")
@ -392,25 +394,20 @@ func (s *undefinedSnippet) Args() (ret string) {
case spos == -1 && ipos == -1:
breakLoop = true
case spos == -1:
idx++
pos += ipos + len("(\\d+)")
args = append(args, reflect.Int.String())
case ipos == -1:
idx++
pos += spos + len("\"([^\"]*)\"")
args = append(args, reflect.String.String())
case ipos < spos:
idx++
pos += ipos + len("(\\d+)")
args = append(args, reflect.Int.String())
case spos < ipos:
idx++
pos += spos + len("\"([^\"]*)\"")
args = append(args, reflect.String.String())
}
}
if s.argument != nil {
idx++
switch s.argument.(type) {
case *gherkin.DocString:
args = append(args, "*gherkin.DocString")

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

@ -109,15 +109,14 @@ type cukefmt struct {
// this is sadly not passed by gherkin nodes.
// it restricts this formatter to run only in synchronous single
// threaded execution. Unless running a copy of formatter for each feature
path string
stat stepType // last step status, before skipped
outlineSteps int // number of current outline scenario steps
ID string // current test id.
results []cukeFeatureJSON // structure that represent cuke results
curStep *cukeStep // track the current step
curElement *cukeElement // track the current element
curFeature *cukeFeatureJSON // track the current feature
curOutline cukeElement // Each example show up as an outline element but the outline is parsed only once
path string
stat stepType // last step status, before skipped
ID string // current test id.
results []cukeFeatureJSON // structure that represent cuke results
curStep *cukeStep // track the current step
curElement *cukeElement // track the current element
curFeature *cukeFeatureJSON // track the current feature
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
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.

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

@ -148,11 +148,13 @@ func (j *junitFormatter) Summary() {
j.current().Time = timeNowFunc().Sub(j.featStarted).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.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)
}
}

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

@ -152,19 +152,18 @@ func TestJUnitFormatterOutput(t *testing.T) {
},
}},
}
s.run()
s.fmt.Summary()
var exp bytes.Buffer
io.WriteString(&exp, xml.Header)
enc := xml.NewEncoder(&exp)
enc.Indent("", " ")
if err := enc.Encode(expected); err != nil {
if _, err = io.WriteString(&exp, xml.Header); err != nil {
t.Fatalf("unexpected error: %v", err)
}
enc := xml.NewEncoder(&exp)
enc.Indent("", " ")
if err = enc.Encode(expected); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if buf.String() != exp.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() {
left := math.Mod(float64(f.steps), float64(f.stepsPerRow))
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))
} else {
fmt.Fprintf(f.out, " %d\n", f.steps)

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

@ -178,8 +178,8 @@ func TestFailsWithUnknownFormatterOptionError(t *testing.T) {
}
out := strings.TrimSpace(string(b))
if strings.Index(out, `unregistered formatter name: "unknown", use one of`) == -1 {
t.Fatalf("unexpected error output: \"%s\"", out)
if !strings.Contains(out, `unregistered formatter name: "unknown", use one of`) {
t.Fatalf("unexpected error output: %q", out)
}
}

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

@ -402,12 +402,6 @@ func (s *Suite) runSteps(steps []*gherkin.Step) (err error) {
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) {
s.fmt.Node(outline)
@ -811,7 +805,7 @@ func matchesTags(filter string, tags []string) (ok bool) {
okComma = hasTag(tags, tag) || okComma
}
}
ok = (false != okComma && ok && okComma) || false
ok = ok && okComma
}
return
}

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

@ -7,18 +7,16 @@ import (
"github.com/DATA-DOG/godog/colors"
)
// empty struct value takes no space allocation
type void struct{}
var red = colors.Red
var redb = colors.Bold(colors.Red)
var green = colors.Green
var black = colors.Black
var blackb = colors.Bold(colors.Black)
var yellow = colors.Yellow
var cyan = colors.Cyan
var cyanb = colors.Bold(colors.Cyan)
var whiteb = colors.Bold(colors.White)
var (
red = colors.Red
redb = colors.Bold(colors.Red)
green = colors.Green
black = colors.Black
yellow = colors.Yellow
cyan = colors.Cyan
cyanb = colors.Bold(colors.Cyan)
whiteb = colors.Bold(colors.White)
)
// repeats a space n times
func s(n int) string {