diff --git a/builder.go b/builder.go index d27d25f..b19dfb7 100644 --- a/builder.go +++ b/builder.go @@ -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 { diff --git a/builder_go110.go b/builder_go110.go index c4ae376..9635102 100644 --- a/builder_go110.go +++ b/builder_go110.go @@ -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 diff --git a/builder_test.go b/builder_test.go index 4a1c5ac..820dd66 100644 --- a/builder_test.go +++ b/builder_test.go @@ -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 { diff --git a/cmd/godog/main.go b/cmd/godog/main.go index f409d07..2ac6adc 100644 --- a/cmd/godog/main.go +++ b/cmd/godog/main.go @@ -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) -} diff --git a/colors/colors.go b/colors/colors.go index df00cec..0232317 100644 --- a/colors/colors.go +++ b/colors/colors.go @@ -16,8 +16,8 @@ const ( red green yellow - blue - magenta + blue // unused + magenta // unused cyan white ) diff --git a/colors/writer.go b/colors/writer.go index 0d33659..469c7a5 100644 --- a/colors/writer.go +++ b/colors/writer.go @@ -16,7 +16,7 @@ type outputMode int const ( _ outputMode = iota discardNonColorEscSeq - outputNonColorEscSeq + outputNonColorEscSeq // unused ) // Colored creates and initializes a new ansiColorWriter diff --git a/fmt.go b/fmt.go index fdaf0ac..476708c 100644 --- a/fmt.go +++ b/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") diff --git a/fmt_cucumber.go b/fmt_cucumber.go index 19897ef..cbfe30d 100644 --- a/fmt_cucumber.go +++ b/fmt_cucumber.go @@ -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. diff --git a/fmt_junit.go b/fmt_junit.go index 8762cbe..0a0f27b 100644 --- a/fmt_junit.go +++ b/fmt_junit.go @@ -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) } } diff --git a/fmt_junit_test.go b/fmt_junit_test.go index 0a5fa5c..36f6257 100644 --- a/fmt_junit_test.go +++ b/fmt_junit_test.go @@ -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()) } diff --git a/fmt_progress.go b/fmt_progress.go index a63e474..dbbf32c 100644 --- a/fmt_progress.go +++ b/fmt_progress.go @@ -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) diff --git a/run_test.go b/run_test.go index 8cb5032..855a5e5 100644 --- a/run_test.go +++ b/run_test.go @@ -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) } } diff --git a/suite.go b/suite.go index 643e172..d262a40 100644 --- a/suite.go +++ b/suite.go @@ -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 } diff --git a/utils.go b/utils.go index 3fb1588..fe824a9 100644 --- a/utils.go +++ b/utils.go @@ -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 {