diff --git a/config/config.go b/config/config.go index 3220c095c..e028210d3 100644 --- a/config/config.go +++ b/config/config.go @@ -26,8 +26,8 @@ type GinkgoConfigType struct { RandomSeed int64 RandomizeAllSpecs bool RegexScansFilePath bool - FocusString string - SkipString string + FocusStrings []string + SkipStrings []string SkipMeasurements bool FailOnPending bool FailFast bool @@ -65,6 +65,11 @@ func processPrefix(prefix string) string { return prefix } +type flagFunc func(string) + +func (f flagFunc) String() string { return "" } +func (f flagFunc) Set(s string) error { f(s); return nil } + func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) { prefix = processPrefix(prefix) flagSet.Int64Var(&(GinkgoConfig.RandomSeed), prefix+"seed", time.Now().Unix(), "The seed used to randomize the spec suite.") @@ -75,8 +80,8 @@ func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) { flagSet.BoolVar(&(GinkgoConfig.DryRun), prefix+"dryRun", false, "If set, ginkgo will walk the test hierarchy without actually running anything. Best paired with -v.") - flagSet.StringVar(&(GinkgoConfig.FocusString), prefix+"focus", "", "If set, ginkgo will only run specs that match this regular expression.") - flagSet.StringVar(&(GinkgoConfig.SkipString), prefix+"skip", "", "If set, ginkgo will only run specs that do not match this regular expression.") + flagSet.Var(flagFunc(flagFocus), prefix+"focus", "If set, ginkgo will only run specs that match this regular expression. Can be specified multiple times, values are ORed.") + flagSet.Var(flagFunc(flagSkip), prefix+"skip", "If set, ginkgo will only run specs that do not match this regular expression. Can be specified multiple times, values are ORed.") flagSet.BoolVar(&(GinkgoConfig.RegexScansFilePath), prefix+"regexScansFilePath", false, "If set, ginkgo regex matching also will look at the file path (code location).") @@ -133,12 +138,12 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor result = append(result, fmt.Sprintf("--%sdryRun", prefix)) } - if ginkgo.FocusString != "" { - result = append(result, fmt.Sprintf("--%sfocus=%s", prefix, ginkgo.FocusString)) + for _, s := range ginkgo.FocusStrings { + result = append(result, fmt.Sprintf("--%sfocus=%s", prefix, s)) } - if ginkgo.SkipString != "" { - result = append(result, fmt.Sprintf("--%sskip=%s", prefix, ginkgo.SkipString)) + for _, s := range ginkgo.SkipStrings { + result = append(result, fmt.Sprintf("--%sskip=%s", prefix, s)) } if ginkgo.FlakeAttempts > 1 { @@ -211,3 +216,13 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor return result } + +// flagFocus implements the -focus flag. +func flagFocus(arg string) { + GinkgoConfig.FocusStrings = append(GinkgoConfig.FocusStrings, arg) +} + +// flagSkip implements the -skip flag. +func flagSkip(arg string) { + GinkgoConfig.SkipStrings = append(GinkgoConfig.SkipStrings, arg) +} diff --git a/go.mod b/go.mod index 1f7125228..212b2454c 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ require ( github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/nxadm/tail v1.4.4 github.com/onsi/gomega v1.10.1 + github.com/sclevine/agouti v3.0.0+incompatible // indirect golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 golang.org/x/text v0.3.2 // indirect ) diff --git a/go.sum b/go.sum index e14753507..abf5e118d 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,8 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/sclevine/agouti v3.0.0+incompatible h1:8IBJS6PWz3uTlMP3YBIR5f+KAldcGuOeFkFbUWfBgK4= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= diff --git a/integration/flags_test.go b/integration/flags_test.go index ad1e09098..20b049205 100644 --- a/integration/flags_test.go +++ b/integration/flags_test.go @@ -105,6 +105,19 @@ var _ = Describe("Flags Specs", func() { Ω(output).Should(ContainSubstring("3 Skipped")) }) + It("should override the programmatic focus when told to skip (multiple options)", func() { + session := startGinkgo(pathToTest, "--noColor", "--skip=marshmallow", "--skip=failing", "--skip=flaky") + Eventually(session).Should(gexec.Exit(0)) + output := string(session.Out.Contents()) + + Ω(output).ShouldNot(ContainSubstring("marshmallow")) + Ω(output).Should(ContainSubstring("chocolate")) + Ω(output).Should(ContainSubstring("smores")) + Ω(output).Should(ContainSubstring("11 Passed")) + Ω(output).Should(ContainSubstring("0 Failed")) + Ω(output).Should(ContainSubstring("1 Pending")) + Ω(output).Should(ContainSubstring("3 Skipped")) + }) It("should run the race detector when told to", func() { if !raceDetectorSupported() { Skip("race detection is not supported") diff --git a/internal/spec/specs.go b/internal/spec/specs.go index 8a2007137..0a24139fb 100644 --- a/internal/spec/specs.go +++ b/internal/spec/specs.go @@ -4,6 +4,7 @@ import ( "math/rand" "regexp" "sort" + "strings" ) type Specs struct { @@ -46,11 +47,11 @@ func (e *Specs) Shuffle(r *rand.Rand) { e.names = names } -func (e *Specs) ApplyFocus(description string, focusString string, skipString string) { - if focusString == "" && skipString == "" { +func (e *Specs) ApplyFocus(description string, focus, skip []string) { + if len(focus)+len(skip) == 0 { e.applyProgrammaticFocus() } else { - e.applyRegExpFocusAndSkip(description, focusString, skipString) + e.applyRegExpFocusAndSkip(description, focus, skip) } } @@ -90,14 +91,13 @@ func (e *Specs) toMatch(description string, i int) []byte { } } -func (e *Specs) applyRegExpFocusAndSkip(description string, focusString string, skipString string) { - var focusFilter *regexp.Regexp - if focusString != "" { - focusFilter = regexp.MustCompile(focusString) +func (e *Specs) applyRegExpFocusAndSkip(description string, focus, skip []string) { + var focusFilter, skipFilter *regexp.Regexp + if len(focus) > 0 { + focusFilter = regexp.MustCompile(strings.Join(focus, "|")) } - var skipFilter *regexp.Regexp - if skipString != "" { - skipFilter = regexp.MustCompile(skipString) + if len(skip) > 0 { + skipFilter = regexp.MustCompile(strings.Join(skip, "|")) } for i, spec := range e.specs { diff --git a/internal/spec/specs_test.go b/internal/spec/specs_test.go index 066fbbb3a..bfe35fd13 100644 --- a/internal/spec/specs_test.go +++ b/internal/spec/specs_test.go @@ -111,7 +111,7 @@ var _ = Describe("Specs", func() { Describe("with no programmatic focus", func() { BeforeEach(func() { specs = newSpecs("A1", noneFlag, "A2", noneFlag, "B1", noneFlag, "B2", pendingFlag) - specs.ApplyFocus("", "", "") + specs.ApplyFocus("", []string{}, []string{}) }) It("should not report as having programmatic specs", func() { @@ -120,15 +120,20 @@ var _ = Describe("Specs", func() { }) Describe("Applying focus/skip", func() { - var description, focusString, skipString string + var ( + description string + focus, skip []string + ) BeforeEach(func() { - description, focusString, skipString = "", "", "" + description = "" + focus = []string{} + skip = []string{} }) JustBeforeEach(func() { specs = newSpecs("A1", focusedFlag, "A2", noneFlag, "B1", focusedFlag, "B2", pendingFlag) - specs.ApplyFocus(description, focusString, skipString) + specs.ApplyFocus(description, focus, skip) }) Context("with neither a focus string nor a skip string", func() { @@ -145,7 +150,7 @@ var _ = Describe("Specs", func() { Context("with a focus regexp", func() { BeforeEach(func() { - focusString = "A" + focus = []string{"A"} }) It("should override the programmatic focus", func() { @@ -161,7 +166,7 @@ var _ = Describe("Specs", func() { Context("with a focus regexp", func() { BeforeEach(func() { - focusString = "B" + focus = []string{"B"} }) It("should not override any pendings", func() { @@ -174,7 +179,7 @@ var _ = Describe("Specs", func() { Context("with a description", func() { BeforeEach(func() { description = "C" - focusString = "C" + focus = []string{"C"} }) It("should include the description in the focus determination", func() { @@ -187,7 +192,7 @@ var _ = Describe("Specs", func() { Context("with a description", func() { BeforeEach(func() { description = "C" - skipString = "C" + skip = []string{"C"} }) It("should include the description in the focus determination", func() { @@ -199,7 +204,7 @@ var _ = Describe("Specs", func() { Context("with a skip regexp", func() { BeforeEach(func() { - skipString = "A" + skip = []string{"A"} }) It("should override the programmatic focus", func() { @@ -215,8 +220,8 @@ var _ = Describe("Specs", func() { Context("with both a focus and a skip regexp", func() { BeforeEach(func() { - focusString = "1" - skipString = "B" + focus = []string{"1"} + skip = []string{"B"} }) It("should AND the two", func() { @@ -251,7 +256,7 @@ var _ = Describe("Specs", func() { pendingInFocused, focusedInPending, }) - specs.ApplyFocus("", "", "") + specs.ApplyFocus("", []string{}, []string{}) }) It("should not have a programmatic focus and should run all tests", func() { diff --git a/internal/suite/suite.go b/internal/suite/suite.go index e75da1f89..b4a83c432 100644 --- a/internal/suite/suite.go +++ b/internal/suite/suite.go @@ -97,7 +97,7 @@ func (suite *Suite) generateSpecsIterator(description string, config config.Gink specs.Shuffle(rand.New(rand.NewSource(config.RandomSeed))) } - specs.ApplyFocus(description, config.FocusString, config.SkipString) + specs.ApplyFocus(description, config.FocusStrings, config.SkipStrings) if config.SkipMeasurements { specs.SkipMeasurements() diff --git a/internal/suite/suite_test.go b/internal/suite/suite_test.go index 8d1516ad8..74cb116e6 100644 --- a/internal/suite/suite_test.go +++ b/internal/suite/suite_test.go @@ -40,7 +40,7 @@ var _ = Describe("Suite", func() { runOrder []string randomizeAllSpecs bool randomSeed int64 - focusString string + focusStrings []string parallelNode int parallelTotal int runResult bool @@ -58,7 +58,7 @@ var _ = Describe("Suite", func() { randomSeed = 11 parallelNode = 1 parallelTotal = 1 - focusString = "" + focusStrings = []string{} runOrder = make([]string, 0) specSuite.SetBeforeSuiteNode(f("BeforeSuite"), codelocation.New(0), 0) @@ -91,7 +91,7 @@ var _ = Describe("Suite", func() { runResult, hasProgrammaticFocus = specSuite.Run(fakeT, "suite description", []reporters.Reporter{fakeR}, writer, config.GinkgoConfigType{ RandomSeed: randomSeed, RandomizeAllSpecs: randomizeAllSpecs, - FocusString: focusString, + FocusStrings: focusStrings, ParallelNode: parallelNode, ParallelTotal: parallelTotal, }) @@ -186,7 +186,7 @@ var _ = Describe("Suite", func() { Context("when provided with a filter", func() { BeforeEach(func() { - focusString = `inner|\d` + focusStrings = []string{`inner`, `\d`} }) It("converts the filter to a regular expression and uses it to filter the running specs", func() {