sort feature files by path when executing, separate the concurrent runner
Этот коммит содержится в:
родитель
a6a318322b
коммит
abc6b65c1c
4 изменённых файлов: 41 добавлений и 15 удалений
|
@ -42,6 +42,6 @@ Feature: load features
|
|||
When I parse features
|
||||
Then I should have 2 feature files:
|
||||
"""
|
||||
features/load.feature
|
||||
features/events.feature
|
||||
features/load.feature
|
||||
"""
|
||||
|
|
44
run.go
44
run.go
|
@ -3,29 +3,24 @@ package godog
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type initializer func(*Suite)
|
||||
|
||||
type runner struct {
|
||||
sync.WaitGroup
|
||||
|
||||
semaphore chan int
|
||||
stopOnFailure bool
|
||||
features []*feature
|
||||
fmt Formatter // needs to support concurrency
|
||||
initializer initializer
|
||||
}
|
||||
|
||||
func (r *runner) run() (failed bool) {
|
||||
r.Add(len(r.features))
|
||||
for _, ft := range r.features {
|
||||
func (r *runner) concurrent(rate int) (failed bool) {
|
||||
queue := make(chan int, rate)
|
||||
for i, ft := range r.features {
|
||||
queue <- i // reserve space in queue
|
||||
go func(fail *bool, feat *feature) {
|
||||
r.semaphore <- 1
|
||||
defer func() {
|
||||
r.Done()
|
||||
<-r.semaphore
|
||||
<-queue // free a space in queue
|
||||
}()
|
||||
if r.stopOnFailure && *fail {
|
||||
return
|
||||
|
@ -42,12 +37,30 @@ func (r *runner) run() (failed bool) {
|
|||
}
|
||||
}(&failed, ft)
|
||||
}
|
||||
r.Wait()
|
||||
// wait until last are processed
|
||||
for i := 0; i < rate; i++ {
|
||||
queue <- i
|
||||
}
|
||||
close(queue)
|
||||
|
||||
// print summary
|
||||
r.fmt.Summary()
|
||||
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.
|
||||
// uses contextInitializer to register contexts
|
||||
//
|
||||
|
@ -94,12 +107,17 @@ func Run(contextInitializer func(suite *Suite)) int {
|
|||
r := runner{
|
||||
fmt: formatter,
|
||||
initializer: contextInitializer,
|
||||
semaphore: make(chan int, concurrency),
|
||||
features: features,
|
||||
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 0
|
||||
|
|
8
suite.go
8
suite.go
|
@ -6,6 +6,7 @@ import (
|
|||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
@ -445,9 +446,16 @@ func parseFeatures(filter string, paths []string) (features []*feature, err erro
|
|||
return features, err
|
||||
}
|
||||
}
|
||||
sort.Sort(featuresSortedByPath(features))
|
||||
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) {
|
||||
if len(tags) == 0 {
|
||||
return
|
||||
|
|
2
utils.go
2
utils.go
|
@ -44,7 +44,7 @@ func s(n int) string {
|
|||
// checks the error and exits with error status code
|
||||
func fatal(err error) {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче