sort feature files by path when executing, separate the concurrent runner

Этот коммит содержится в:
gedi 2016-06-16 12:01:58 +03:00
родитель a6a318322b
коммит abc6b65c1c
4 изменённых файлов: 41 добавлений и 15 удалений

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

@ -42,6 +42,6 @@ Feature: load features
When I parse features When I parse features
Then I should have 2 feature files: Then I should have 2 feature files:
""" """
features/load.feature
features/events.feature features/events.feature
features/load.feature
""" """

44
run.go
Просмотреть файл

@ -3,29 +3,24 @@ package godog
import ( import (
"fmt" "fmt"
"os" "os"
"sync"
) )
type initializer func(*Suite) type initializer func(*Suite)
type runner struct { type runner struct {
sync.WaitGroup
semaphore chan int
stopOnFailure bool stopOnFailure bool
features []*feature features []*feature
fmt Formatter // needs to support concurrency fmt Formatter // needs to support concurrency
initializer initializer initializer initializer
} }
func (r *runner) run() (failed bool) { func (r *runner) concurrent(rate int) (failed bool) {
r.Add(len(r.features)) queue := make(chan int, rate)
for _, ft := range r.features { for i, ft := range r.features {
queue <- i // reserve space in queue
go func(fail *bool, feat *feature) { go func(fail *bool, feat *feature) {
r.semaphore <- 1
defer func() { defer func() {
r.Done() <-queue // free a space in queue
<-r.semaphore
}() }()
if r.stopOnFailure && *fail { if r.stopOnFailure && *fail {
return return
@ -42,12 +37,30 @@ func (r *runner) run() (failed bool) {
} }
}(&failed, ft) }(&failed, ft)
} }
r.Wait() // wait until last are processed
for i := 0; i < rate; i++ {
queue <- i
}
close(queue)
// print summary
r.fmt.Summary() r.fmt.Summary()
return return
} }
func (r *runner) run() (failed bool) {
suite := &Suite{
fmt: r.fmt,
stopOnFailure: r.stopOnFailure,
features: r.features,
}
r.initializer(suite)
suite.run()
r.fmt.Summary()
return suite.failed
}
// Run creates and runs the feature suite. // Run creates and runs the feature suite.
// uses contextInitializer to register contexts // uses contextInitializer to register contexts
// //
@ -94,12 +107,17 @@ func Run(contextInitializer func(suite *Suite)) int {
r := runner{ r := runner{
fmt: formatter, fmt: formatter,
initializer: contextInitializer, initializer: contextInitializer,
semaphore: make(chan int, concurrency),
features: features, features: features,
stopOnFailure: sof, stopOnFailure: sof,
} }
if failed := r.run(); failed { var failed bool
if concurrency > 1 {
failed = r.concurrent(concurrency)
} else {
failed = r.run()
}
if failed {
return 1 return 1
} }
return 0 return 0

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

@ -6,6 +6,7 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp" "regexp"
"sort"
"strconv" "strconv"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
@ -445,9 +446,16 @@ func parseFeatures(filter string, paths []string) (features []*feature, err erro
return features, err return features, err
} }
} }
sort.Sort(featuresSortedByPath(features))
return return
} }
type featuresSortedByPath []*feature
func (s featuresSortedByPath) Len() int { return len(s) }
func (s featuresSortedByPath) Less(i, j int) bool { return s[i].Path < s[j].Path }
func (s featuresSortedByPath) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func applyTagFilter(tags string, ft *gherkin.Feature) { func applyTagFilter(tags string, ft *gherkin.Feature) {
if len(tags) == 0 { if len(tags) == 0 {
return return

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

@ -44,7 +44,7 @@ func s(n int) string {
// checks the error and exits with error status code // checks the error and exits with error status code
func fatal(err error) { func fatal(err error) {
if err != nil { if err != nil {
fmt.Println(err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
} }
} }