mirror-to-gitea/vendor/github.com/onsi/ginkgo/internal/suite/suite.go
vendor/github.com/onsi/ginkgo/internal/suite/suite.goRaw
1package suite
2
3import (
4 "math/rand"
5 "net/http"
6 "time"
7
8 "github.com/onsi/ginkgo/internal/spec_iterator"
9
10 "github.com/onsi/ginkgo/config"
11 "github.com/onsi/ginkgo/internal/containernode"
12 "github.com/onsi/ginkgo/internal/failer"
13 "github.com/onsi/ginkgo/internal/leafnodes"
14 "github.com/onsi/ginkgo/internal/spec"
15 "github.com/onsi/ginkgo/internal/specrunner"
16 "github.com/onsi/ginkgo/internal/writer"
17 "github.com/onsi/ginkgo/reporters"
18 "github.com/onsi/ginkgo/types"
19)
20
21type ginkgoTestingT interface {
22 Fail()
23}
24
25type deferredContainerNode struct {
26 text string
27 body func()
28 flag types.FlagType
29 codeLocation types.CodeLocation
30}
31
32type Suite struct {
33 topLevelContainer *containernode.ContainerNode
34 currentContainer *containernode.ContainerNode
35
36 deferredContainerNodes []deferredContainerNode
37
38 containerIndex int
39 beforeSuiteNode leafnodes.SuiteNode
40 afterSuiteNode leafnodes.SuiteNode
41 runner *specrunner.SpecRunner
42 failer *failer.Failer
43 running bool
44 expandTopLevelNodes bool
45}
46
47func New(failer *failer.Failer) *Suite {
48 topLevelContainer := containernode.New("[Top Level]", types.FlagTypeNone, types.CodeLocation{})
49
50 return &Suite{
51 topLevelContainer: topLevelContainer,
52 currentContainer: topLevelContainer,
53 failer: failer,
54 containerIndex: 1,
55 deferredContainerNodes: []deferredContainerNode{},
56 }
57}
58
59func (suite *Suite) Run(t ginkgoTestingT, description string, reporters []reporters.Reporter, writer writer.WriterInterface, config config.GinkgoConfigType) (bool, bool) {
60 if config.ParallelTotal < 1 {
61 panic("ginkgo.parallel.total must be >= 1")
62 }
63
64 if config.ParallelNode > config.ParallelTotal || config.ParallelNode < 1 {
65 panic("ginkgo.parallel.node is one-indexed and must be <= ginkgo.parallel.total")
66 }
67
68 suite.expandTopLevelNodes = true
69 for _, deferredNode := range suite.deferredContainerNodes {
70 suite.PushContainerNode(deferredNode.text, deferredNode.body, deferredNode.flag, deferredNode.codeLocation)
71 }
72
73 r := rand.New(rand.NewSource(config.RandomSeed))
74 suite.topLevelContainer.Shuffle(r)
75 iterator, hasProgrammaticFocus := suite.generateSpecsIterator(description, config)
76 suite.runner = specrunner.New(description, suite.beforeSuiteNode, iterator, suite.afterSuiteNode, reporters, writer, config)
77
78 suite.running = true
79 success := suite.runner.Run()
80 if !success {
81 t.Fail()
82 }
83 return success, hasProgrammaticFocus
84}
85
86func (suite *Suite) generateSpecsIterator(description string, config config.GinkgoConfigType) (spec_iterator.SpecIterator, bool) {
87 specsSlice := []*spec.Spec{}
88 suite.topLevelContainer.BackPropagateProgrammaticFocus()
89 for _, collatedNodes := range suite.topLevelContainer.Collate() {
90 specsSlice = append(specsSlice, spec.New(collatedNodes.Subject, collatedNodes.Containers, config.EmitSpecProgress))
91 }
92
93 specs := spec.NewSpecs(specsSlice)
94 specs.RegexScansFilePath = config.RegexScansFilePath
95
96 if config.RandomizeAllSpecs {
97 specs.Shuffle(rand.New(rand.NewSource(config.RandomSeed)))
98 }
99
100 specs.ApplyFocus(description, config.FocusStrings, config.SkipStrings)
101
102 if config.SkipMeasurements {
103 specs.SkipMeasurements()
104 }
105
106 var iterator spec_iterator.SpecIterator
107
108 if config.ParallelTotal > 1 {
109 iterator = spec_iterator.NewParallelIterator(specs.Specs(), config.SyncHost)
110 resp, err := http.Get(config.SyncHost + "/has-counter")
111 if err != nil || resp.StatusCode != http.StatusOK {
112 iterator = spec_iterator.NewShardedParallelIterator(specs.Specs(), config.ParallelTotal, config.ParallelNode)
113 }
114 } else {
115 iterator = spec_iterator.NewSerialIterator(specs.Specs())
116 }
117
118 return iterator, specs.HasProgrammaticFocus()
119}
120
121func (suite *Suite) CurrentRunningSpecSummary() (*types.SpecSummary, bool) {
122 if !suite.running {
123 return nil, false
124 }
125 return suite.runner.CurrentSpecSummary()
126}
127
128func (suite *Suite) SetBeforeSuiteNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
129 if suite.beforeSuiteNode != nil {
130 panic("You may only call BeforeSuite once!")
131 }
132 suite.beforeSuiteNode = leafnodes.NewBeforeSuiteNode(body, codeLocation, timeout, suite.failer)
133}
134
135func (suite *Suite) SetAfterSuiteNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
136 if suite.afterSuiteNode != nil {
137 panic("You may only call AfterSuite once!")
138 }
139 suite.afterSuiteNode = leafnodes.NewAfterSuiteNode(body, codeLocation, timeout, suite.failer)
140}
141
142func (suite *Suite) SetSynchronizedBeforeSuiteNode(bodyA interface{}, bodyB interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
143 if suite.beforeSuiteNode != nil {
144 panic("You may only call BeforeSuite once!")
145 }
146 suite.beforeSuiteNode = leafnodes.NewSynchronizedBeforeSuiteNode(bodyA, bodyB, codeLocation, timeout, suite.failer)
147}
148
149func (suite *Suite) SetSynchronizedAfterSuiteNode(bodyA interface{}, bodyB interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
150 if suite.afterSuiteNode != nil {
151 panic("You may only call AfterSuite once!")
152 }
153 suite.afterSuiteNode = leafnodes.NewSynchronizedAfterSuiteNode(bodyA, bodyB, codeLocation, timeout, suite.failer)
154}
155
156func (suite *Suite) PushContainerNode(text string, body func(), flag types.FlagType, codeLocation types.CodeLocation) {
157 /*
158 We defer walking the container nodes (which immediately evaluates the `body` function)
159 until `RunSpecs` is called. We do this by storing off the deferred container nodes. Then, when
160 `RunSpecs` is called we actually go through and add the container nodes to the test structure.
161
162 This allows us to defer calling all the `body` functions until _after_ the top level functions
163 have been walked, _after_ func init()s have been called, and _after_ `go test` has called `flag.Parse()`.
164
165 This allows users to load up configuration information in the `TestX` go test hook just before `RunSpecs`
166 is invoked and solves issues like #693 and makes the lifecycle easier to reason about.
167
168 */
169 if !suite.expandTopLevelNodes {
170 suite.deferredContainerNodes = append(suite.deferredContainerNodes, deferredContainerNode{text, body, flag, codeLocation})
171 return
172 }
173
174 container := containernode.New(text, flag, codeLocation)
175 suite.currentContainer.PushContainerNode(container)
176
177 previousContainer := suite.currentContainer
178 suite.currentContainer = container
179 suite.containerIndex++
180
181 body()
182
183 suite.containerIndex--
184 suite.currentContainer = previousContainer
185}
186
187func (suite *Suite) PushItNode(text string, body interface{}, flag types.FlagType, codeLocation types.CodeLocation, timeout time.Duration) {
188 if suite.running {
189 suite.failer.Fail("You may only call It from within a Describe, Context or When", codeLocation)
190 }
191 suite.currentContainer.PushSubjectNode(leafnodes.NewItNode(text, body, flag, codeLocation, timeout, suite.failer, suite.containerIndex))
192}
193
194func (suite *Suite) PushMeasureNode(text string, body interface{}, flag types.FlagType, codeLocation types.CodeLocation, samples int) {
195 if suite.running {
196 suite.failer.Fail("You may only call Measure from within a Describe, Context or When", codeLocation)
197 }
198 suite.currentContainer.PushSubjectNode(leafnodes.NewMeasureNode(text, body, flag, codeLocation, samples, suite.failer, suite.containerIndex))
199}
200
201func (suite *Suite) PushBeforeEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
202 if suite.running {
203 suite.failer.Fail("You may only call BeforeEach from within a Describe, Context or When", codeLocation)
204 }
205 suite.currentContainer.PushSetupNode(leafnodes.NewBeforeEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex))
206}
207
208func (suite *Suite) PushJustBeforeEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
209 if suite.running {
210 suite.failer.Fail("You may only call JustBeforeEach from within a Describe, Context or When", codeLocation)
211 }
212 suite.currentContainer.PushSetupNode(leafnodes.NewJustBeforeEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex))
213}
214
215func (suite *Suite) PushJustAfterEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
216 if suite.running {
217 suite.failer.Fail("You may only call JustAfterEach from within a Describe or Context", codeLocation)
218 }
219 suite.currentContainer.PushSetupNode(leafnodes.NewJustAfterEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex))
220}
221
222func (suite *Suite) PushAfterEachNode(body interface{}, codeLocation types.CodeLocation, timeout time.Duration) {
223 if suite.running {
224 suite.failer.Fail("You may only call AfterEach from within a Describe, Context or When", codeLocation)
225 }
226 suite.currentContainer.PushSetupNode(leafnodes.NewAfterEachNode(body, codeLocation, timeout, suite.failer, suite.containerIndex))
227}