Merge pull request #317 from cucumber/fixed-the-random-flag-bug
Fixed the broken --random flag
Этот коммит содержится в:
коммит
1033ce083b
3 изменённых файлов: 120 добавлений и 8 удалений
22
run.go
22
run.go
|
@ -3,6 +3,7 @@ package godog
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -158,13 +159,30 @@ func (r *runner) scenarioConcurrent(rate int) (failed bool) {
|
||||||
|
|
||||||
queue := make(chan int, rate)
|
queue := make(chan int, rate)
|
||||||
for _, ft := range r.features {
|
for _, ft := range r.features {
|
||||||
r.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.content)
|
pickles := make([]*messages.Pickle, len(ft.pickles))
|
||||||
|
if r.randomSeed != 0 {
|
||||||
|
r := rand.New(rand.NewSource(r.randomSeed))
|
||||||
|
perm := r.Perm(len(ft.pickles))
|
||||||
|
for i, v := range perm {
|
||||||
|
pickles[v] = ft.pickles[i]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copy(pickles, ft.pickles)
|
||||||
|
}
|
||||||
|
|
||||||
for i, p := range ft.pickles {
|
if len(pickles) == 0 {
|
||||||
|
r.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, p := range pickles {
|
||||||
pickle := *p
|
pickle := *p
|
||||||
|
|
||||||
queue <- i // reserve space in queue
|
queue <- i // reserve space in queue
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
r.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.content)
|
||||||
|
}
|
||||||
|
|
||||||
go func(fail *bool, pickle *messages.Pickle) {
|
go func(fail *bool, pickle *messages.Pickle) {
|
||||||
defer func() {
|
defer func() {
|
||||||
<-queue // free a space in queue
|
<-queue // free a space in queue
|
||||||
|
|
92
run_test.go
92
run_test.go
|
@ -6,6 +6,8 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -256,8 +258,61 @@ func TestFeatureFilePathParser(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_RandomizeRun(t *testing.T) {
|
||||||
|
const noRandomFlag = 0
|
||||||
|
const createRandomSeedFlag = -1
|
||||||
|
const noConcurrencyFlag = 1
|
||||||
|
const formatter = "pretty"
|
||||||
|
const featurePath = "formatter-tests/features/with_few_empty_scenarios.feature"
|
||||||
|
|
||||||
|
fmtOutputScenarioInitializer := func(ctx *ScenarioContext) {
|
||||||
|
ctx.Step(`^(?:a )?failing step`, failingStepDef)
|
||||||
|
ctx.Step(`^(?:a )?pending step$`, pendingStepDef)
|
||||||
|
ctx.Step(`^(?:a )?passing step$`, passingStepDef)
|
||||||
|
ctx.Step(`^odd (\d+) and even (\d+) number$`, oddEvenStepDef)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedStatus, expectedOutput := testRun(t,
|
||||||
|
fmtOutputScenarioInitializer,
|
||||||
|
formatter, noConcurrencyFlag,
|
||||||
|
noRandomFlag, []string{featurePath},
|
||||||
|
)
|
||||||
|
|
||||||
|
actualStatus, actualOutput := testRun(t,
|
||||||
|
fmtOutputScenarioInitializer,
|
||||||
|
formatter, noConcurrencyFlag,
|
||||||
|
createRandomSeedFlag, []string{featurePath},
|
||||||
|
)
|
||||||
|
|
||||||
|
expectedSeed := parseSeed(actualOutput)
|
||||||
|
assert.NotZero(t, expectedSeed)
|
||||||
|
|
||||||
|
// Removes "Randomized with seed: <seed>" part of the output
|
||||||
|
actualOutputSplit := strings.Split(actualOutput, "\n")
|
||||||
|
actualOutputSplit = actualOutputSplit[:len(actualOutputSplit)-2]
|
||||||
|
actualOutputReduced := strings.Join(actualOutputSplit, "\n")
|
||||||
|
|
||||||
|
assert.NotEqual(t, expectedOutput, actualOutputReduced)
|
||||||
|
assertOutput(t, formatter, expectedOutput, actualOutputReduced)
|
||||||
|
|
||||||
|
expectedStatus, expectedOutput = actualStatus, actualOutput
|
||||||
|
|
||||||
|
actualStatus, actualOutput = testRun(t,
|
||||||
|
fmtOutputScenarioInitializer,
|
||||||
|
formatter, noConcurrencyFlag,
|
||||||
|
expectedSeed, []string{featurePath},
|
||||||
|
)
|
||||||
|
|
||||||
|
actualSeed := parseSeed(actualOutput)
|
||||||
|
|
||||||
|
assert.Equal(t, expectedSeed, actualSeed)
|
||||||
|
assert.Equal(t, expectedStatus, actualStatus)
|
||||||
|
assert.Equal(t, expectedOutput, actualOutput)
|
||||||
|
}
|
||||||
|
|
||||||
func Test_AllFeaturesRun(t *testing.T) {
|
func Test_AllFeaturesRun(t *testing.T) {
|
||||||
const concurrency = 100
|
const concurrency = 100
|
||||||
|
const noRandomFlag = 0
|
||||||
const format = "progress"
|
const format = "progress"
|
||||||
|
|
||||||
const expected = `...................................................................... 70
|
const expected = `...................................................................... 70
|
||||||
|
@ -285,7 +340,8 @@ func Test_AllFeaturesRun(t *testing.T) {
|
||||||
|
|
||||||
actualStatus, actualOutput = testRun(t,
|
actualStatus, actualOutput = testRun(t,
|
||||||
fmtOutputScenarioInitializer,
|
fmtOutputScenarioInitializer,
|
||||||
format, concurrency, []string{"features"},
|
format, concurrency,
|
||||||
|
noRandomFlag, []string{"features"},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.Equal(t, exitSuccess, actualStatus)
|
assert.Equal(t, exitSuccess, actualStatus)
|
||||||
|
@ -304,6 +360,8 @@ func TestFormatterConcurrencyRun(t *testing.T) {
|
||||||
featurePaths := []string{"formatter-tests/features"}
|
featurePaths := []string{"formatter-tests/features"}
|
||||||
|
|
||||||
const concurrency = 100
|
const concurrency = 100
|
||||||
|
const noRandomFlag = 0
|
||||||
|
const noConcurrency = 1
|
||||||
|
|
||||||
fmtOutputSuiteInitializer := func(s *Suite) {
|
fmtOutputSuiteInitializer := func(s *Suite) {
|
||||||
s.Step(`^(?:a )?failing step`, failingStepDef)
|
s.Step(`^(?:a )?failing step`, failingStepDef)
|
||||||
|
@ -325,7 +383,7 @@ func TestFormatterConcurrencyRun(t *testing.T) {
|
||||||
func(t *testing.T) {
|
func(t *testing.T) {
|
||||||
expectedStatus, expectedOutput := testRunWithOptions(t,
|
expectedStatus, expectedOutput := testRunWithOptions(t,
|
||||||
fmtOutputSuiteInitializer,
|
fmtOutputSuiteInitializer,
|
||||||
formatter, 1, featurePaths,
|
formatter, noConcurrency, featurePaths,
|
||||||
)
|
)
|
||||||
actualStatus, actualOutput := testRunWithOptions(t,
|
actualStatus, actualOutput := testRunWithOptions(t,
|
||||||
fmtOutputSuiteInitializer,
|
fmtOutputSuiteInitializer,
|
||||||
|
@ -337,11 +395,13 @@ func TestFormatterConcurrencyRun(t *testing.T) {
|
||||||
|
|
||||||
expectedStatus, expectedOutput = testRun(t,
|
expectedStatus, expectedOutput = testRun(t,
|
||||||
fmtOutputScenarioInitializer,
|
fmtOutputScenarioInitializer,
|
||||||
formatter, 1, featurePaths,
|
formatter, noConcurrency,
|
||||||
|
noRandomFlag, featurePaths,
|
||||||
)
|
)
|
||||||
actualStatus, actualOutput = testRun(t,
|
actualStatus, actualOutput = testRun(t,
|
||||||
fmtOutputScenarioInitializer,
|
fmtOutputScenarioInitializer,
|
||||||
formatter, concurrency, featurePaths,
|
formatter, concurrency,
|
||||||
|
noRandomFlag, featurePaths,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.Equal(t, expectedStatus, actualStatus)
|
assert.Equal(t, expectedStatus, actualStatus)
|
||||||
|
@ -370,7 +430,14 @@ func testRunWithOptions(t *testing.T, initializer func(*Suite), format string, c
|
||||||
return status, string(actual)
|
return status, string(actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRun(t *testing.T, scenarioInitializer func(*ScenarioContext), format string, concurrency int, featurePaths []string) (int, string) {
|
func testRun(
|
||||||
|
t *testing.T,
|
||||||
|
scenarioInitializer func(*ScenarioContext),
|
||||||
|
format string,
|
||||||
|
concurrency int,
|
||||||
|
randomSeed int64,
|
||||||
|
featurePaths []string,
|
||||||
|
) (int, string) {
|
||||||
output := new(bytes.Buffer)
|
output := new(bytes.Buffer)
|
||||||
|
|
||||||
opts := Options{
|
opts := Options{
|
||||||
|
@ -378,6 +445,7 @@ func testRun(t *testing.T, scenarioInitializer func(*ScenarioContext), format st
|
||||||
NoColors: true,
|
NoColors: true,
|
||||||
Paths: featurePaths,
|
Paths: featurePaths,
|
||||||
Concurrency: concurrency,
|
Concurrency: concurrency,
|
||||||
|
Randomize: randomSeed,
|
||||||
Output: output,
|
Output: output,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,3 +539,17 @@ func oddOrEven(odd, even int) error {
|
||||||
func pendingStepDef() error { return ErrPending }
|
func pendingStepDef() error { return ErrPending }
|
||||||
|
|
||||||
func failingStepDef() error { return fmt.Errorf("step failed") }
|
func failingStepDef() error { return fmt.Errorf("step failed") }
|
||||||
|
|
||||||
|
func parseSeed(str string) (seed int64) {
|
||||||
|
re := regexp.MustCompile(`Randomized with seed: (\d*)`)
|
||||||
|
match := re.FindStringSubmatch(str)
|
||||||
|
|
||||||
|
if len(match) > 0 {
|
||||||
|
var err error
|
||||||
|
if seed, err = strconv.ParseInt(match[1], 10, 64); err != nil {
|
||||||
|
seed = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
14
suite.go
14
suite.go
|
@ -2,6 +2,7 @@ package godog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -424,7 +425,18 @@ func (s *Suite) shouldFail(err error) bool {
|
||||||
func (s *Suite) runFeature(f *feature) {
|
func (s *Suite) runFeature(f *feature) {
|
||||||
s.fmt.Feature(f.GherkinDocument, f.Uri, f.content)
|
s.fmt.Feature(f.GherkinDocument, f.Uri, f.content)
|
||||||
|
|
||||||
for _, pickle := range f.pickles {
|
pickles := make([]*messages.Pickle, len(f.pickles))
|
||||||
|
if s.randomSeed != 0 {
|
||||||
|
r := rand.New(rand.NewSource(s.randomSeed))
|
||||||
|
perm := r.Perm(len(f.pickles))
|
||||||
|
for i, v := range perm {
|
||||||
|
pickles[v] = f.pickles[i]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copy(pickles, f.pickles)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pickle := range pickles {
|
||||||
err := s.runPickle(pickle)
|
err := s.runPickle(pickle)
|
||||||
if s.shouldFail(err) {
|
if s.shouldFail(err) {
|
||||||
s.failed = true
|
s.failed = true
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче