Skip to content

Commit 6c84b35

Browse files
committed
Preview now works alongside run and in parallel
1 parent e1d0b38 commit 6c84b35

File tree

10 files changed

+135
-41
lines changed

10 files changed

+135
-41
lines changed

core_dsl.go

+17-12
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ var flagSet types.GinkgoFlagSet
3838
var deprecationTracker = types.NewDeprecationTracker()
3939
var suiteConfig = types.NewDefaultSuiteConfig()
4040
var reporterConfig = types.NewDefaultReporterConfig()
41-
var suiteDidRun, suiteDidPreview = false, false
41+
var suiteDidRun = false
4242
var outputInterceptor internal.OutputInterceptor
4343
var client parallel_support.Client
4444

@@ -247,10 +247,12 @@ func RunSpecs(t GinkgoTestingT, description string, args ...interface{}) bool {
247247
if suiteDidRun {
248248
exitIfErr(types.GinkgoErrors.RerunningSuite())
249249
}
250-
if suiteDidPreview {
251-
exitIfErr(types.GinkgoErrors.RunAndPreviewSuite())
252-
}
253250
suiteDidRun = true
251+
err := global.PushClone()
252+
if err != nil {
253+
exitIfErr(err)
254+
}
255+
defer global.PopClone()
254256

255257
suiteLabels := extractSuiteConfiguration(args)
256258

@@ -288,7 +290,7 @@ func RunSpecs(t GinkgoTestingT, description string, args ...interface{}) bool {
288290
registerReportAfterSuiteNodeForAutogeneratedReports(reporterConfig)
289291
}
290292

291-
err := global.Suite.BuildTree()
293+
err = global.Suite.BuildTree()
292294
exitIfErr(err)
293295
suitePath, err := os.Getwd()
294296
exitIfErr(err)
@@ -348,21 +350,24 @@ PreviewSpecs walks the testing tree and produces a report without actually invok
348350
See http://onsi.github.io/ginkgo/#previewing-specs for more information.
349351
*/
350352
func PreviewSpecs(description string, args ...any) Report {
351-
if suiteDidRun {
352-
exitIfErr(types.GinkgoErrors.RunAndPreviewSuite())
353+
err := global.PushClone()
354+
if err != nil {
355+
exitIfErr(err)
353356
}
357+
defer global.PopClone()
354358

355359
suiteLabels := extractSuiteConfiguration(args)
356-
if suiteConfig.ParallelTotal != 1 {
357-
exitIfErr(types.GinkgoErrors.PreviewInParallelConfiguration())
358-
}
359-
suiteConfig.DryRun = true
360+
priorDryRun, priorParallelTotal, priorParallelProcess := suiteConfig.DryRun, suiteConfig.ParallelTotal, suiteConfig.ParallelProcess
361+
suiteConfig.DryRun, suiteConfig.ParallelTotal, suiteConfig.ParallelProcess = true, 1, 1
362+
defer func() {
363+
suiteConfig.DryRun, suiteConfig.ParallelTotal, suiteConfig.ParallelProcess = priorDryRun, priorParallelTotal, priorParallelProcess
364+
}()
360365
reporter := reporters.NoopReporter{}
361366
outputInterceptor = internal.NoopOutputInterceptor{}
362367
client = nil
363368
writer := GinkgoWriter.(*internal.Writer)
364369

365-
err := global.Suite.BuildTree()
370+
err = global.Suite.BuildTree()
366371
exitIfErr(err)
367372
suitePath, err := os.Getwd()
368373
exitIfErr(err)

docs/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3191,7 +3191,7 @@ For a more complete preview you can run `ginkgo --dry-run -v`. This compiles th
31913191

31923192
If, you need finer-grained control over previews you can use `PreviewSpecs` in your suite in lieu of `RunSpecs`. `PreviewSpecs` behaves like `--dry-run` in that it will compile the suite, build the spec tree, and then walk the tree while honoring any filter and randomization flags. However `PreviewSpecs` generates and returns a full [`Report` object](#reporting-nodes---reportbeforesuite-and-reportaftersuite) that can be manipulated and inspected as needed. Specs that will be run will have `State = SpecStatePassed` and specs that will be skipped will have `SpecStateSkipped`.
31933193

3194-
Currently you must run in series to invoke `PreviewSpecs` and you cannot run both `PreviewSpecs` and `RunSpecs` in the same suite. If you are opting into `PreviewSpecs` in lieu of `--dry-run` one suggested pattern is to key off of the `--dry-run` configuration to run `PreviewSpecs` instead of `RunSpecs`:
3194+
If you are opting into `PreviewSpecs` in lieu of `--dry-run` one suggested pattern is to key off of the `--dry-run` configuration to run `PreviewSpecs` instead of `RunSpecs`:
31953195

31963196
```go
31973197
func TestMySuite(t *testing.T) {

integration/_fixtures/preview_fixture/preview_fixture_suite_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import (
1111

1212
func TestPreviewFixture(t *testing.T) {
1313
RegisterFailHandler(Fail)
14-
if os.Getenv("RUN") == "true" {
15-
RunSpecs(t, "PreviewFixture Suite", Label("suite-label"))
16-
}
1714
if os.Getenv("PREVIEW") == "true" {
1815
report := PreviewSpecs("PreviewFixture Suite", Label("suite-label"))
1916
for _, spec := range report.SpecReports {
2017
fmt.Println(spec.State, spec.FullText())
2118
}
2219
}
20+
if os.Getenv("RUN") == "true" {
21+
RunSpecs(t, "PreviewFixture Suite", Label("suite-label"))
22+
}
2323
}
2424

2525
var _ = Describe("specs", func() {

integration/preview_test.go

+16-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package integration_test
22

33
import (
4+
"fmt"
45
"os"
56

67
. "github.com/onsi/ginkgo/v2"
@@ -25,21 +26,28 @@ var _ = Describe("Preview", func() {
2526
Ω(session).Should(gbytes.Say("skipped specs D"))
2627
})
2728

28-
It("fails if running in parallel", func() {
29+
It("succeeds if you attempt to both run and preview specs", func() {
2930
os.Setenv("PREVIEW", "true")
3031
DeferCleanup(os.Unsetenv, "PREVIEW")
31-
session := startGinkgo(fm.PathTo("preview"), "--procs=2")
32-
Eventually(session).Should(gexec.Exit(1))
33-
Ω(session.Err).Should(gbytes.Say(`Ginkgo only supports PreviewSpecs\(\) in serial mode\.`))
32+
os.Setenv("RUN", "true")
33+
DeferCleanup(os.Unsetenv, "RUN")
34+
session := startGinkgo(fm.PathTo("preview"))
35+
Eventually(session).Should(gexec.Exit(0))
36+
Ω(session).Should(gbytes.Say(`passed specs A`))
37+
Ω(session).Should(gbytes.Say(`passed specs B`))
38+
Ω(session).Should(gbytes.Say(`passed specs C`))
39+
Ω(session).Should(gbytes.Say(`passed specs D`))
40+
Ω(session).Should(gbytes.Say(`Ran 4 of 4 Specs`))
3441
})
3542

36-
It("fails if you attempt to both run and preview specs", func() {
43+
It("works if you run in parallel", func() {
3744
os.Setenv("PREVIEW", "true")
3845
DeferCleanup(os.Unsetenv, "PREVIEW")
3946
os.Setenv("RUN", "true")
4047
DeferCleanup(os.Unsetenv, "RUN")
41-
session := startGinkgo(fm.PathTo("preview"))
42-
Eventually(session).Should(gexec.Exit(1))
43-
Ω(session).Should(gbytes.Say(`It looks like you are calling RunSpecs and PreviewSpecs in the same invocation`))
48+
session := startGinkgo(fm.PathTo("preview"), "-p")
49+
Eventually(session).Should(gexec.Exit(0))
50+
fmt.Println(string(session.Out.Contents()))
51+
Ω(session).Should(gbytes.Say(`Ran 4 of 4 Specs`))
4452
})
4553
})

internal/global/init.go

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
var Suite *internal.Suite
88
var Failer *internal.Failer
9+
var backupSuite *internal.Suite
910

1011
func init() {
1112
InitializeGlobals()
@@ -15,3 +16,13 @@ func InitializeGlobals() {
1516
Failer = internal.NewFailer()
1617
Suite = internal.NewSuite()
1718
}
19+
20+
func PushClone() error {
21+
var err error
22+
backupSuite, err = Suite.Clone()
23+
return err
24+
}
25+
26+
func PopClone() {
27+
Suite = backupSuite
28+
}

internal/node.go

+6
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,12 @@ func (n Node) IsZero() bool {
597597
/* Nodes */
598598
type Nodes []Node
599599

600+
func (n Nodes) Clone() Nodes {
601+
nodes := make(Nodes, len(n))
602+
copy(nodes, n)
603+
return nodes
604+
}
605+
600606
func (n Nodes) CopyAppend(nodes ...Node) Nodes {
601607
numN := len(n)
602608
out := make(Nodes, numN+len(nodes))

internal/node_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,22 @@ var _ = Describe("Node", func() {
11811181
})
11821182

11831183
var _ = Describe("Nodes", func() {
1184+
Describe("Clone", func() {
1185+
var n1, n2, n3, n4 Node
1186+
1187+
BeforeEach(func() {
1188+
n1, n2, n3, n4 = N(), N(), N(), N()
1189+
})
1190+
1191+
It("clones the slice", func() {
1192+
original := Nodes{n1, n2, n3}
1193+
clone := original.Clone()
1194+
Ω(original).Should(Equal(clone))
1195+
clone[2] = n4
1196+
Ω(original).Should(Equal(Nodes{n1, n2, n3}))
1197+
})
1198+
})
1199+
11841200
Describe("CopyAppend", func() {
11851201
var n1, n2, n3, n4 Node
11861202

internal/suite.go

+14
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,20 @@ func NewSuite() *Suite {
7777
}
7878
}
7979

80+
func (suite *Suite) Clone() (*Suite, error) {
81+
if suite.phase != PhaseBuildTopLevel {
82+
return nil, fmt.Errorf("cnanot clone suite after tree has been built")
83+
}
84+
return &Suite{
85+
tree: &TreeNode{},
86+
phase: PhaseBuildTopLevel,
87+
ProgressReporterManager: NewProgressReporterManager(),
88+
topLevelContainers: suite.topLevelContainers.Clone(),
89+
suiteNodes: suite.suiteNodes.Clone(),
90+
selectiveLock: &sync.Mutex{},
91+
}, nil
92+
}
93+
8094
func (suite *Suite) BuildTree() error {
8195
// During PhaseBuildTopLevel, the top level containers are stored in suite.topLevelCotainers and entered
8296
// We now enter PhaseBuildTree where these top level containers are entered and added to the spec tree

internal/suite_test.go

+51-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package internal_test
22

33
import (
4-
"fmt"
54
"io"
65

76
. "github.com/onsi/ginkgo/v2"
@@ -57,7 +56,6 @@ var _ = Describe("Suite", func() {
5756
})
5857

5958
It("only traverses top-level containers when told to BuildTree", func() {
60-
fmt.Fprintln(GinkgoWriter, "HELLO!")
6159
Ω(rt).Should(HaveTrackedNothing())
6260
Ω(suite.BuildTree()).Should(Succeed())
6361
Ω(rt).Should(HaveTracked("traversing outer", "traversing nested"))
@@ -72,6 +70,57 @@ var _ = Describe("Suite", func() {
7270
})
7371
})
7472

73+
Describe("cloning a suite", func() {
74+
var err1, err2, err3 error
75+
BeforeEach(func() {
76+
suite.PushNode(N(types.NodeTypeBeforeSuite, "before suite", func() {
77+
rt.Run("before-suite")
78+
}))
79+
err1 = suite.PushNode(N(ntCon, "a top-level container", func() {
80+
rt.Run("traversing outer")
81+
err2 = suite.PushNode(N(ntCon, "a nested container", func() {
82+
rt.Run("traversing nested")
83+
err3 = suite.PushNode(N(ntIt, "an it", rt.T("running it")))
84+
}))
85+
}))
86+
})
87+
88+
It("fails if the tree has already been built", func() {
89+
Ω(suite.BuildTree()).Should(Succeed())
90+
_, err := suite.Clone()
91+
Ω(err).Should(MatchError("cnanot clone suite after tree has been built"))
92+
})
93+
94+
It("generates the same tree as the original", func() {
95+
clone, err := suite.Clone()
96+
Ω(err).ShouldNot(HaveOccurred())
97+
98+
Ω(suite.BuildTree()).Should(Succeed())
99+
Ω(rt).Should(HaveTracked("traversing outer", "traversing nested"))
100+
rt.Reset()
101+
suite.Run("suite", Labels{}, "/path/to/suite", failer, reporter, writer, outputInterceptor, interruptHandler, client, internal.RegisterForProgressSignal, conf)
102+
Ω(rt).Should(HaveTracked("before-suite", "running it"))
103+
104+
Ω(err1).ShouldNot(HaveOccurred())
105+
Ω(err2).ShouldNot(HaveOccurred())
106+
Ω(err3).ShouldNot(HaveOccurred())
107+
108+
suite = clone // this is what the swapping of globals looks in reality
109+
110+
rt.Reset()
111+
Ω(clone.BuildTree()).Should(Succeed())
112+
Ω(rt).Should(HaveTracked("traversing outer", "traversing nested"))
113+
rt.Reset()
114+
clone.Run("suite", Labels{}, "/path/to/suite", failer, reporter, writer, outputInterceptor, interruptHandler, client, internal.RegisterForProgressSignal, conf)
115+
Ω(rt).Should(HaveTracked("before-suite", "running it"))
116+
117+
Ω(err1).ShouldNot(HaveOccurred())
118+
Ω(err2).ShouldNot(HaveOccurred())
119+
Ω(err3).ShouldNot(HaveOccurred())
120+
})
121+
122+
})
123+
75124
Describe("InRunPhase", func() {
76125
It("returns true when in the run phase and false when not in the run phase", func() {
77126
falsey := true

types/errors.go

-15
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,6 @@ func (g ginkgoErrors) RerunningSuite() error {
7070
}
7171
}
7272

73-
func (g ginkgoErrors) RunAndPreviewSuite() error {
74-
return GinkgoError{
75-
Heading: "Running and Previewing Suite",
76-
Message: formatter.F(`It looks like you are calling RunSpecs and PreviewSpecs in the same invocation of Ginkgo. Ginkgo does not currently support that. Please change your code to only call one or the other.`),
77-
DocLink: "previewing-specs",
78-
}
79-
}
80-
8173
/* Tree construction errors */
8274

8375
func (g ginkgoErrors) PushingNodeInRunPhase(nodeType NodeType, cl CodeLocation) error {
@@ -586,13 +578,6 @@ func (g ginkgoErrors) DryRunInParallelConfiguration() error {
586578
}
587579
}
588580

589-
func (g ginkgoErrors) PreviewInParallelConfiguration() error {
590-
return GinkgoError{
591-
Heading: "Ginkgo only supports PreviewSpecs() in serial mode.",
592-
Message: "Please try running ginkgo again, but without -p or -procs to ensure the suite is running in series.",
593-
}
594-
}
595-
596581
func (g ginkgoErrors) GracePeriodCannotBeZero() error {
597582
return GinkgoError{
598583
Heading: "Ginkgo requires a positive --grace-period.",

0 commit comments

Comments
 (0)