mirror-to-gitea/vendor/github.com/onsi/ginkgo/internal/specrunner/spec_runner.go
vendor/github.com/onsi/ginkgo/internal/specrunner/spec_runner.goRaw
1package specrunner
2
3import (
4 "fmt"
5 "os"
6 "os/signal"
7 "sync"
8 "syscall"
9
10 "github.com/onsi/ginkgo/internal/spec_iterator"
11
12 "github.com/onsi/ginkgo/config"
13 "github.com/onsi/ginkgo/internal/leafnodes"
14 "github.com/onsi/ginkgo/internal/spec"
15 Writer "github.com/onsi/ginkgo/internal/writer"
16 "github.com/onsi/ginkgo/reporters"
17 "github.com/onsi/ginkgo/types"
18
19 "time"
20)
21
22type SpecRunner struct {
23 description string
24 beforeSuiteNode leafnodes.SuiteNode
25 iterator spec_iterator.SpecIterator
26 afterSuiteNode leafnodes.SuiteNode
27 reporters []reporters.Reporter
28 startTime time.Time
29 suiteID string
30 runningSpec *spec.Spec
31 writer Writer.WriterInterface
32 config config.GinkgoConfigType
33 interrupted bool
34 processedSpecs []*spec.Spec
35 lock *sync.Mutex
36}
37
38func New(description string, beforeSuiteNode leafnodes.SuiteNode, iterator spec_iterator.SpecIterator, afterSuiteNode leafnodes.SuiteNode, reporters []reporters.Reporter, writer Writer.WriterInterface, config config.GinkgoConfigType) *SpecRunner {
39 return &SpecRunner{
40 description: description,
41 beforeSuiteNode: beforeSuiteNode,
42 iterator: iterator,
43 afterSuiteNode: afterSuiteNode,
44 reporters: reporters,
45 writer: writer,
46 config: config,
47 suiteID: randomID(),
48 lock: &sync.Mutex{},
49 }
50}
51
52func (runner *SpecRunner) Run() bool {
53 if runner.config.DryRun {
54 runner.performDryRun()
55 return true
56 }
57
58 runner.reportSuiteWillBegin()
59 signalRegistered := make(chan struct{})
60 go runner.registerForInterrupts(signalRegistered)
61 <-signalRegistered
62
63 suitePassed := runner.runBeforeSuite()
64
65 if suitePassed {
66 suitePassed = runner.runSpecs()
67 }
68
69 runner.blockForeverIfInterrupted()
70
71 suitePassed = runner.runAfterSuite() && suitePassed
72
73 runner.reportSuiteDidEnd(suitePassed)
74
75 return suitePassed
76}
77
78func (runner *SpecRunner) performDryRun() {
79 runner.reportSuiteWillBegin()
80
81 if runner.beforeSuiteNode != nil {
82 summary := runner.beforeSuiteNode.Summary()
83 summary.State = types.SpecStatePassed
84 runner.reportBeforeSuite(summary)
85 }
86
87 for {
88 spec, err := runner.iterator.Next()
89 if err == spec_iterator.ErrClosed {
90 break
91 }
92 if err != nil {
93 fmt.Println("failed to iterate over tests:\n" + err.Error())
94 break
95 }
96
97 runner.processedSpecs = append(runner.processedSpecs, spec)
98
99 summary := spec.Summary(runner.suiteID)
100 runner.reportSpecWillRun(summary)
101 if summary.State == types.SpecStateInvalid {
102 summary.State = types.SpecStatePassed
103 }
104 runner.reportSpecDidComplete(summary, false)
105 }
106
107 if runner.afterSuiteNode != nil {
108 summary := runner.afterSuiteNode.Summary()
109 summary.State = types.SpecStatePassed
110 runner.reportAfterSuite(summary)
111 }
112
113 runner.reportSuiteDidEnd(true)
114}
115
116func (runner *SpecRunner) runBeforeSuite() bool {
117 if runner.beforeSuiteNode == nil || runner.wasInterrupted() {
118 return true
119 }
120
121 runner.writer.Truncate()
122 conf := runner.config
123 passed := runner.beforeSuiteNode.Run(conf.ParallelNode, conf.ParallelTotal, conf.SyncHost)
124 if !passed {
125 runner.writer.DumpOut()
126 }
127 runner.reportBeforeSuite(runner.beforeSuiteNode.Summary())
128 return passed
129}
130
131func (runner *SpecRunner) runAfterSuite() bool {
132 if runner.afterSuiteNode == nil {
133 return true
134 }
135
136 runner.writer.Truncate()
137 conf := runner.config
138 passed := runner.afterSuiteNode.Run(conf.ParallelNode, conf.ParallelTotal, conf.SyncHost)
139 if !passed {
140 runner.writer.DumpOut()
141 }
142 runner.reportAfterSuite(runner.afterSuiteNode.Summary())
143 return passed
144}
145
146func (runner *SpecRunner) runSpecs() bool {
147 suiteFailed := false
148 skipRemainingSpecs := false
149 for {
150 spec, err := runner.iterator.Next()
151 if err == spec_iterator.ErrClosed {
152 break
153 }
154 if err != nil {
155 fmt.Println("failed to iterate over tests:\n" + err.Error())
156 suiteFailed = true
157 break
158 }
159
160 runner.processedSpecs = append(runner.processedSpecs, spec)
161
162 if runner.wasInterrupted() {
163 break
164 }
165 if skipRemainingSpecs {
166 spec.Skip()
167 }
168
169 if !spec.Skipped() && !spec.Pending() {
170 if passed := runner.runSpec(spec); !passed {
171 suiteFailed = true
172 }
173 } else if spec.Pending() && runner.config.FailOnPending {
174 runner.reportSpecWillRun(spec.Summary(runner.suiteID))
175 suiteFailed = true
176 runner.reportSpecDidComplete(spec.Summary(runner.suiteID), spec.Failed())
177 } else {
178 runner.reportSpecWillRun(spec.Summary(runner.suiteID))
179 runner.reportSpecDidComplete(spec.Summary(runner.suiteID), spec.Failed())
180 }
181
182 if spec.Failed() && runner.config.FailFast {
183 skipRemainingSpecs = true
184 }
185 }
186
187 return !suiteFailed
188}
189
190func (runner *SpecRunner) runSpec(spec *spec.Spec) (passed bool) {
191 maxAttempts := 1
192 if runner.config.FlakeAttempts > 0 {
193 // uninitialized configs count as 1
194 maxAttempts = runner.config.FlakeAttempts
195 }
196
197 for i := 0; i < maxAttempts; i++ {
198 runner.reportSpecWillRun(spec.Summary(runner.suiteID))
199 runner.runningSpec = spec
200 spec.Run(runner.writer)
201 runner.runningSpec = nil
202 runner.reportSpecDidComplete(spec.Summary(runner.suiteID), spec.Failed())
203 if !spec.Failed() {
204 return true
205 }
206 }
207 return false
208}
209
210func (runner *SpecRunner) CurrentSpecSummary() (*types.SpecSummary, bool) {
211 if runner.runningSpec == nil {
212 return nil, false
213 }
214
215 return runner.runningSpec.Summary(runner.suiteID), true
216}
217
218func (runner *SpecRunner) registerForInterrupts(signalRegistered chan struct{}) {
219 c := make(chan os.Signal, 1)
220 signal.Notify(c, os.Interrupt, syscall.SIGTERM)
221 close(signalRegistered)
222
223 <-c
224 signal.Stop(c)
225 runner.markInterrupted()
226 go runner.registerForHardInterrupts()
227 runner.writer.DumpOutWithHeader(`
228Received interrupt. Emitting contents of GinkgoWriter...
229---------------------------------------------------------
230`)
231 if runner.afterSuiteNode != nil {
232 fmt.Fprint(os.Stderr, `
233---------------------------------------------------------
234Received interrupt. Running AfterSuite...
235^C again to terminate immediately
236`)
237 runner.runAfterSuite()
238 }
239 runner.reportSuiteDidEnd(false)
240 os.Exit(1)
241}
242
243func (runner *SpecRunner) registerForHardInterrupts() {
244 c := make(chan os.Signal, 1)
245 signal.Notify(c, os.Interrupt, syscall.SIGTERM)
246
247 <-c
248 fmt.Fprintln(os.Stderr, "\nReceived second interrupt. Shutting down.")
249 os.Exit(1)
250}
251
252func (runner *SpecRunner) blockForeverIfInterrupted() {
253 runner.lock.Lock()
254 interrupted := runner.interrupted
255 runner.lock.Unlock()
256
257 if interrupted {
258 select {}
259 }
260}
261
262func (runner *SpecRunner) markInterrupted() {
263 runner.lock.Lock()
264 defer runner.lock.Unlock()
265 runner.interrupted = true
266}
267
268func (runner *SpecRunner) wasInterrupted() bool {
269 runner.lock.Lock()
270 defer runner.lock.Unlock()
271 return runner.interrupted
272}
273
274func (runner *SpecRunner) reportSuiteWillBegin() {
275 runner.startTime = time.Now()
276 summary := runner.suiteWillBeginSummary()
277 for _, reporter := range runner.reporters {
278 reporter.SpecSuiteWillBegin(runner.config, summary)
279 }
280}
281
282func (runner *SpecRunner) reportBeforeSuite(summary *types.SetupSummary) {
283 for _, reporter := range runner.reporters {
284 reporter.BeforeSuiteDidRun(summary)
285 }
286}
287
288func (runner *SpecRunner) reportAfterSuite(summary *types.SetupSummary) {
289 for _, reporter := range runner.reporters {
290 reporter.AfterSuiteDidRun(summary)
291 }
292}
293
294func (runner *SpecRunner) reportSpecWillRun(summary *types.SpecSummary) {
295 runner.writer.Truncate()
296
297 for _, reporter := range runner.reporters {
298 reporter.SpecWillRun(summary)
299 }
300}
301
302func (runner *SpecRunner) reportSpecDidComplete(summary *types.SpecSummary, failed bool) {
303 if len(summary.CapturedOutput) == 0 {
304 summary.CapturedOutput = string(runner.writer.Bytes())
305 }
306 for i := len(runner.reporters) - 1; i >= 1; i-- {
307 runner.reporters[i].SpecDidComplete(summary)
308 }
309
310 if failed {
311 runner.writer.DumpOut()
312 }
313
314 runner.reporters[0].SpecDidComplete(summary)
315}
316
317func (runner *SpecRunner) reportSuiteDidEnd(success bool) {
318 summary := runner.suiteDidEndSummary(success)
319 summary.RunTime = time.Since(runner.startTime)
320 for _, reporter := range runner.reporters {
321 reporter.SpecSuiteDidEnd(summary)
322 }
323}
324
325func (runner *SpecRunner) countSpecsThatRanSatisfying(filter func(ex *spec.Spec) bool) (count int) {
326 count = 0
327
328 for _, spec := range runner.processedSpecs {
329 if filter(spec) {
330 count++
331 }
332 }
333
334 return count
335}
336
337func (runner *SpecRunner) suiteDidEndSummary(success bool) *types.SuiteSummary {
338 numberOfSpecsThatWillBeRun := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
339 return !ex.Skipped() && !ex.Pending()
340 })
341
342 numberOfPendingSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
343 return ex.Pending()
344 })
345
346 numberOfSkippedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
347 return ex.Skipped()
348 })
349
350 numberOfPassedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
351 return ex.Passed()
352 })
353
354 numberOfFlakedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
355 return ex.Flaked()
356 })
357
358 numberOfFailedSpecs := runner.countSpecsThatRanSatisfying(func(ex *spec.Spec) bool {
359 return ex.Failed()
360 })
361
362 if runner.beforeSuiteNode != nil && !runner.beforeSuiteNode.Passed() && !runner.config.DryRun {
363 var known bool
364 numberOfSpecsThatWillBeRun, known = runner.iterator.NumberOfSpecsThatWillBeRunIfKnown()
365 if !known {
366 numberOfSpecsThatWillBeRun = runner.iterator.NumberOfSpecsPriorToIteration()
367 }
368 numberOfFailedSpecs = numberOfSpecsThatWillBeRun
369 }
370
371 return &types.SuiteSummary{
372 SuiteDescription: runner.description,
373 SuiteSucceeded: success,
374 SuiteID: runner.suiteID,
375
376 NumberOfSpecsBeforeParallelization: runner.iterator.NumberOfSpecsPriorToIteration(),
377 NumberOfTotalSpecs: len(runner.processedSpecs),
378 NumberOfSpecsThatWillBeRun: numberOfSpecsThatWillBeRun,
379 NumberOfPendingSpecs: numberOfPendingSpecs,
380 NumberOfSkippedSpecs: numberOfSkippedSpecs,
381 NumberOfPassedSpecs: numberOfPassedSpecs,
382 NumberOfFailedSpecs: numberOfFailedSpecs,
383 NumberOfFlakedSpecs: numberOfFlakedSpecs,
384 }
385}
386
387func (runner *SpecRunner) suiteWillBeginSummary() *types.SuiteSummary {
388 numTotal, known := runner.iterator.NumberOfSpecsToProcessIfKnown()
389 if !known {
390 numTotal = -1
391 }
392
393 numToRun, known := runner.iterator.NumberOfSpecsThatWillBeRunIfKnown()
394 if !known {
395 numToRun = -1
396 }
397
398 return &types.SuiteSummary{
399 SuiteDescription: runner.description,
400 SuiteID: runner.suiteID,
401
402 NumberOfSpecsBeforeParallelization: runner.iterator.NumberOfSpecsPriorToIteration(),
403 NumberOfTotalSpecs: numTotal,
404 NumberOfSpecsThatWillBeRun: numToRun,
405 NumberOfPendingSpecs: -1,
406 NumberOfSkippedSpecs: -1,
407 NumberOfPassedSpecs: -1,
408 NumberOfFailedSpecs: -1,
409 NumberOfFlakedSpecs: -1,
410 }
411}