mirror-to-gitea/vendor/github.com/onsi/ginkgo/reporters/stenographer/stenographer.go
vendor/github.com/onsi/ginkgo/reporters/stenographer/stenographer.goRaw
1/*
2The stenographer is used by Ginkgo's reporters to generate output.
3
4Move along, nothing to see here.
5*/
6
7package stenographer
8
9import (
10 "fmt"
11 "io"
12 "runtime"
13 "strings"
14
15 "github.com/onsi/ginkgo/types"
16)
17
18const defaultStyle = "\x1b[0m"
19const boldStyle = "\x1b[1m"
20const redColor = "\x1b[91m"
21const greenColor = "\x1b[32m"
22const yellowColor = "\x1b[33m"
23const cyanColor = "\x1b[36m"
24const grayColor = "\x1b[90m"
25const lightGrayColor = "\x1b[37m"
26
27type cursorStateType int
28
29const (
30 cursorStateTop cursorStateType = iota
31 cursorStateStreaming
32 cursorStateMidBlock
33 cursorStateEndBlock
34)
35
36type Stenographer interface {
37 AnnounceSuite(description string, randomSeed int64, randomizingAll bool, succinct bool)
38 AnnounceAggregatedParallelRun(nodes int, succinct bool)
39 AnnounceParallelRun(node int, nodes int, succinct bool)
40 AnnounceTotalNumberOfSpecs(total int, succinct bool)
41 AnnounceNumberOfSpecs(specsToRun int, total int, succinct bool)
42 AnnounceSpecRunCompletion(summary *types.SuiteSummary, succinct bool)
43
44 AnnounceSpecWillRun(spec *types.SpecSummary)
45 AnnounceBeforeSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool)
46 AnnounceAfterSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool)
47
48 AnnounceCapturedOutput(output string)
49
50 AnnounceSuccessfulSpec(spec *types.SpecSummary)
51 AnnounceSuccessfulSlowSpec(spec *types.SpecSummary, succinct bool)
52 AnnounceSuccessfulMeasurement(spec *types.SpecSummary, succinct bool)
53
54 AnnouncePendingSpec(spec *types.SpecSummary, noisy bool)
55 AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool)
56
57 AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool)
58 AnnounceSpecPanicked(spec *types.SpecSummary, succinct bool, fullTrace bool)
59 AnnounceSpecFailed(spec *types.SpecSummary, succinct bool, fullTrace bool)
60
61 SummarizeFailures(summaries []*types.SpecSummary)
62}
63
64func New(color bool, enableFlakes bool, writer io.Writer) Stenographer {
65 denoter := "•"
66 if runtime.GOOS == "windows" {
67 denoter = "+"
68 }
69 return &consoleStenographer{
70 color: color,
71 denoter: denoter,
72 cursorState: cursorStateTop,
73 enableFlakes: enableFlakes,
74 w: writer,
75 }
76}
77
78type consoleStenographer struct {
79 color bool
80 denoter string
81 cursorState cursorStateType
82 enableFlakes bool
83 w io.Writer
84}
85
86var alternatingColors = []string{defaultStyle, grayColor}
87
88func (s *consoleStenographer) AnnounceSuite(description string, randomSeed int64, randomizingAll bool, succinct bool) {
89 if succinct {
90 s.print(0, "[%d] %s ", randomSeed, s.colorize(boldStyle, description))
91 return
92 }
93 s.printBanner(fmt.Sprintf("Running Suite: %s", description), "=")
94 s.print(0, "Random Seed: %s", s.colorize(boldStyle, "%d", randomSeed))
95 if randomizingAll {
96 s.print(0, " - Will randomize all specs")
97 }
98 s.printNewLine()
99}
100
101func (s *consoleStenographer) AnnounceParallelRun(node int, nodes int, succinct bool) {
102 if succinct {
103 s.print(0, "- node #%d ", node)
104 return
105 }
106 s.println(0,
107 "Parallel test node %s/%s.",
108 s.colorize(boldStyle, "%d", node),
109 s.colorize(boldStyle, "%d", nodes),
110 )
111 s.printNewLine()
112}
113
114func (s *consoleStenographer) AnnounceAggregatedParallelRun(nodes int, succinct bool) {
115 if succinct {
116 s.print(0, "- %d nodes ", nodes)
117 return
118 }
119 s.println(0,
120 "Running in parallel across %s nodes",
121 s.colorize(boldStyle, "%d", nodes),
122 )
123 s.printNewLine()
124}
125
126func (s *consoleStenographer) AnnounceNumberOfSpecs(specsToRun int, total int, succinct bool) {
127 if succinct {
128 s.print(0, "- %d/%d specs ", specsToRun, total)
129 s.stream()
130 return
131 }
132 s.println(0,
133 "Will run %s of %s specs",
134 s.colorize(boldStyle, "%d", specsToRun),
135 s.colorize(boldStyle, "%d", total),
136 )
137
138 s.printNewLine()
139}
140
141func (s *consoleStenographer) AnnounceTotalNumberOfSpecs(total int, succinct bool) {
142 if succinct {
143 s.print(0, "- %d specs ", total)
144 s.stream()
145 return
146 }
147 s.println(0,
148 "Will run %s specs",
149 s.colorize(boldStyle, "%d", total),
150 )
151
152 s.printNewLine()
153}
154
155func (s *consoleStenographer) AnnounceSpecRunCompletion(summary *types.SuiteSummary, succinct bool) {
156 if succinct && summary.SuiteSucceeded {
157 s.print(0, " %s %s ", s.colorize(greenColor, "SUCCESS!"), summary.RunTime)
158 return
159 }
160 s.printNewLine()
161 color := greenColor
162 if !summary.SuiteSucceeded {
163 color = redColor
164 }
165 s.println(0, s.colorize(boldStyle+color, "Ran %d of %d Specs in %.3f seconds", summary.NumberOfSpecsThatWillBeRun, summary.NumberOfTotalSpecs, summary.RunTime.Seconds()))
166
167 status := ""
168 if summary.SuiteSucceeded {
169 status = s.colorize(boldStyle+greenColor, "SUCCESS!")
170 } else {
171 status = s.colorize(boldStyle+redColor, "FAIL!")
172 }
173
174 flakes := ""
175 if s.enableFlakes {
176 flakes = " | " + s.colorize(yellowColor+boldStyle, "%d Flaked", summary.NumberOfFlakedSpecs)
177 }
178
179 s.print(0,
180 "%s -- %s | %s | %s | %s\n",
181 status,
182 s.colorize(greenColor+boldStyle, "%d Passed", summary.NumberOfPassedSpecs),
183 s.colorize(redColor+boldStyle, "%d Failed", summary.NumberOfFailedSpecs)+flakes,
184 s.colorize(yellowColor+boldStyle, "%d Pending", summary.NumberOfPendingSpecs),
185 s.colorize(cyanColor+boldStyle, "%d Skipped", summary.NumberOfSkippedSpecs),
186 )
187}
188
189func (s *consoleStenographer) AnnounceSpecWillRun(spec *types.SpecSummary) {
190 s.startBlock()
191 for i, text := range spec.ComponentTexts[1 : len(spec.ComponentTexts)-1] {
192 s.print(0, s.colorize(alternatingColors[i%2], text)+" ")
193 }
194
195 indentation := 0
196 if len(spec.ComponentTexts) > 2 {
197 indentation = 1
198 s.printNewLine()
199 }
200 index := len(spec.ComponentTexts) - 1
201 s.print(indentation, s.colorize(boldStyle, spec.ComponentTexts[index]))
202 s.printNewLine()
203 s.print(indentation, s.colorize(lightGrayColor, spec.ComponentCodeLocations[index].String()))
204 s.printNewLine()
205 s.midBlock()
206}
207
208func (s *consoleStenographer) AnnounceBeforeSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool) {
209 s.announceSetupFailure("BeforeSuite", summary, succinct, fullTrace)
210}
211
212func (s *consoleStenographer) AnnounceAfterSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool) {
213 s.announceSetupFailure("AfterSuite", summary, succinct, fullTrace)
214}
215
216func (s *consoleStenographer) announceSetupFailure(name string, summary *types.SetupSummary, succinct bool, fullTrace bool) {
217 s.startBlock()
218 var message string
219 switch summary.State {
220 case types.SpecStateFailed:
221 message = "Failure"
222 case types.SpecStatePanicked:
223 message = "Panic"
224 case types.SpecStateTimedOut:
225 message = "Timeout"
226 }
227
228 s.println(0, s.colorize(redColor+boldStyle, "%s [%.3f seconds]", message, summary.RunTime.Seconds()))
229
230 indentation := s.printCodeLocationBlock([]string{name}, []types.CodeLocation{summary.CodeLocation}, summary.ComponentType, 0, summary.State, true)
231
232 s.printNewLine()
233 s.printFailure(indentation, summary.State, summary.Failure, fullTrace)
234
235 s.endBlock()
236}
237
238func (s *consoleStenographer) AnnounceCapturedOutput(output string) {
239 if output == "" {
240 return
241 }
242
243 s.startBlock()
244 s.println(0, output)
245 s.midBlock()
246}
247
248func (s *consoleStenographer) AnnounceSuccessfulSpec(spec *types.SpecSummary) {
249 s.print(0, s.colorize(greenColor, s.denoter))
250 s.stream()
251}
252
253func (s *consoleStenographer) AnnounceSuccessfulSlowSpec(spec *types.SpecSummary, succinct bool) {
254 s.printBlockWithMessage(
255 s.colorize(greenColor, "%s [SLOW TEST:%.3f seconds]", s.denoter, spec.RunTime.Seconds()),
256 "",
257 spec,
258 succinct,
259 )
260}
261
262func (s *consoleStenographer) AnnounceSuccessfulMeasurement(spec *types.SpecSummary, succinct bool) {
263 s.printBlockWithMessage(
264 s.colorize(greenColor, "%s [MEASUREMENT]", s.denoter),
265 s.measurementReport(spec, succinct),
266 spec,
267 succinct,
268 )
269}
270
271func (s *consoleStenographer) AnnouncePendingSpec(spec *types.SpecSummary, noisy bool) {
272 if noisy {
273 s.printBlockWithMessage(
274 s.colorize(yellowColor, "P [PENDING]"),
275 "",
276 spec,
277 false,
278 )
279 } else {
280 s.print(0, s.colorize(yellowColor, "P"))
281 s.stream()
282 }
283}
284
285func (s *consoleStenographer) AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool) {
286 // Skips at runtime will have a non-empty spec.Failure. All others should be succinct.
287 if succinct || spec.Failure == (types.SpecFailure{}) {
288 s.print(0, s.colorize(cyanColor, "S"))
289 s.stream()
290 } else {
291 s.startBlock()
292 s.println(0, s.colorize(cyanColor+boldStyle, "S [SKIPPING]%s [%.3f seconds]", s.failureContext(spec.Failure.ComponentType), spec.RunTime.Seconds()))
293
294 indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, spec.Failure.ComponentType, spec.Failure.ComponentIndex, spec.State, succinct)
295
296 s.printNewLine()
297 s.printSkip(indentation, spec.Failure)
298 s.endBlock()
299 }
300}
301
302func (s *consoleStenographer) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) {
303 s.printSpecFailure(fmt.Sprintf("%s... Timeout", s.denoter), spec, succinct, fullTrace)
304}
305
306func (s *consoleStenographer) AnnounceSpecPanicked(spec *types.SpecSummary, succinct bool, fullTrace bool) {
307 s.printSpecFailure(fmt.Sprintf("%s! Panic", s.denoter), spec, succinct, fullTrace)
308}
309
310func (s *consoleStenographer) AnnounceSpecFailed(spec *types.SpecSummary, succinct bool, fullTrace bool) {
311 s.printSpecFailure(fmt.Sprintf("%s Failure", s.denoter), spec, succinct, fullTrace)
312}
313
314func (s *consoleStenographer) SummarizeFailures(summaries []*types.SpecSummary) {
315 failingSpecs := []*types.SpecSummary{}
316
317 for _, summary := range summaries {
318 if summary.HasFailureState() {
319 failingSpecs = append(failingSpecs, summary)
320 }
321 }
322
323 if len(failingSpecs) == 0 {
324 return
325 }
326
327 s.printNewLine()
328 s.printNewLine()
329 plural := "s"
330 if len(failingSpecs) == 1 {
331 plural = ""
332 }
333 s.println(0, s.colorize(redColor+boldStyle, "Summarizing %d Failure%s:", len(failingSpecs), plural))
334 for _, summary := range failingSpecs {
335 s.printNewLine()
336 if summary.HasFailureState() {
337 if summary.TimedOut() {
338 s.print(0, s.colorize(redColor+boldStyle, "[Timeout...] "))
339 } else if summary.Panicked() {
340 s.print(0, s.colorize(redColor+boldStyle, "[Panic!] "))
341 } else if summary.Failed() {
342 s.print(0, s.colorize(redColor+boldStyle, "[Fail] "))
343 }
344 s.printSpecContext(summary.ComponentTexts, summary.ComponentCodeLocations, summary.Failure.ComponentType, summary.Failure.ComponentIndex, summary.State, true)
345 s.printNewLine()
346 s.println(0, s.colorize(lightGrayColor, summary.Failure.Location.String()))
347 }
348 }
349}
350
351func (s *consoleStenographer) startBlock() {
352 if s.cursorState == cursorStateStreaming {
353 s.printNewLine()
354 s.printDelimiter()
355 } else if s.cursorState == cursorStateMidBlock {
356 s.printNewLine()
357 }
358}
359
360func (s *consoleStenographer) midBlock() {
361 s.cursorState = cursorStateMidBlock
362}
363
364func (s *consoleStenographer) endBlock() {
365 s.printDelimiter()
366 s.cursorState = cursorStateEndBlock
367}
368
369func (s *consoleStenographer) stream() {
370 s.cursorState = cursorStateStreaming
371}
372
373func (s *consoleStenographer) printBlockWithMessage(header string, message string, spec *types.SpecSummary, succinct bool) {
374 s.startBlock()
375 s.println(0, header)
376
377 indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, types.SpecComponentTypeInvalid, 0, spec.State, succinct)
378
379 if message != "" {
380 s.printNewLine()
381 s.println(indentation, message)
382 }
383
384 s.endBlock()
385}
386
387func (s *consoleStenographer) printSpecFailure(message string, spec *types.SpecSummary, succinct bool, fullTrace bool) {
388 s.startBlock()
389 s.println(0, s.colorize(redColor+boldStyle, "%s%s [%.3f seconds]", message, s.failureContext(spec.Failure.ComponentType), spec.RunTime.Seconds()))
390
391 indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, spec.Failure.ComponentType, spec.Failure.ComponentIndex, spec.State, succinct)
392
393 s.printNewLine()
394 s.printFailure(indentation, spec.State, spec.Failure, fullTrace)
395 s.endBlock()
396}
397
398func (s *consoleStenographer) failureContext(failedComponentType types.SpecComponentType) string {
399 switch failedComponentType {
400 case types.SpecComponentTypeBeforeSuite:
401 return " in Suite Setup (BeforeSuite)"
402 case types.SpecComponentTypeAfterSuite:
403 return " in Suite Teardown (AfterSuite)"
404 case types.SpecComponentTypeBeforeEach:
405 return " in Spec Setup (BeforeEach)"
406 case types.SpecComponentTypeJustBeforeEach:
407 return " in Spec Setup (JustBeforeEach)"
408 case types.SpecComponentTypeAfterEach:
409 return " in Spec Teardown (AfterEach)"
410 }
411
412 return ""
413}
414
415func (s *consoleStenographer) printSkip(indentation int, spec types.SpecFailure) {
416 s.println(indentation, s.colorize(cyanColor, spec.Message))
417 s.printNewLine()
418 s.println(indentation, spec.Location.String())
419}
420
421func (s *consoleStenographer) printFailure(indentation int, state types.SpecState, failure types.SpecFailure, fullTrace bool) {
422 if state == types.SpecStatePanicked {
423 s.println(indentation, s.colorize(redColor+boldStyle, failure.Message))
424 s.println(indentation, s.colorize(redColor, failure.ForwardedPanic))
425 s.println(indentation, failure.Location.String())
426 s.printNewLine()
427 s.println(indentation, s.colorize(redColor, "Full Stack Trace"))
428 s.println(indentation, failure.Location.FullStackTrace)
429 } else {
430 s.println(indentation, s.colorize(redColor, failure.Message))
431 s.printNewLine()
432 s.println(indentation, failure.Location.String())
433 if fullTrace {
434 s.printNewLine()
435 s.println(indentation, s.colorize(redColor, "Full Stack Trace"))
436 s.println(indentation, failure.Location.FullStackTrace)
437 }
438 }
439}
440
441func (s *consoleStenographer) printSpecContext(componentTexts []string, componentCodeLocations []types.CodeLocation, failedComponentType types.SpecComponentType, failedComponentIndex int, state types.SpecState, succinct bool) int {
442 startIndex := 1
443 indentation := 0
444
445 if len(componentTexts) == 1 {
446 startIndex = 0
447 }
448
449 for i := startIndex; i < len(componentTexts); i++ {
450 if (state.IsFailure() || state == types.SpecStateSkipped) && i == failedComponentIndex {
451 color := redColor
452 if state == types.SpecStateSkipped {
453 color = cyanColor
454 }
455 blockType := ""
456 switch failedComponentType {
457 case types.SpecComponentTypeBeforeSuite:
458 blockType = "BeforeSuite"
459 case types.SpecComponentTypeAfterSuite:
460 blockType = "AfterSuite"
461 case types.SpecComponentTypeBeforeEach:
462 blockType = "BeforeEach"
463 case types.SpecComponentTypeJustBeforeEach:
464 blockType = "JustBeforeEach"
465 case types.SpecComponentTypeAfterEach:
466 blockType = "AfterEach"
467 case types.SpecComponentTypeIt:
468 blockType = "It"
469 case types.SpecComponentTypeMeasure:
470 blockType = "Measurement"
471 }
472 if succinct {
473 s.print(0, s.colorize(color+boldStyle, "[%s] %s ", blockType, componentTexts[i]))
474 } else {
475 s.println(indentation, s.colorize(color+boldStyle, "%s [%s]", componentTexts[i], blockType))
476 s.println(indentation, s.colorize(grayColor, "%s", componentCodeLocations[i]))
477 }
478 } else {
479 if succinct {
480 s.print(0, s.colorize(alternatingColors[i%2], "%s ", componentTexts[i]))
481 } else {
482 s.println(indentation, componentTexts[i])
483 s.println(indentation, s.colorize(grayColor, "%s", componentCodeLocations[i]))
484 }
485 }
486 indentation++
487 }
488
489 return indentation
490}
491
492func (s *consoleStenographer) printCodeLocationBlock(componentTexts []string, componentCodeLocations []types.CodeLocation, failedComponentType types.SpecComponentType, failedComponentIndex int, state types.SpecState, succinct bool) int {
493 indentation := s.printSpecContext(componentTexts, componentCodeLocations, failedComponentType, failedComponentIndex, state, succinct)
494
495 if succinct {
496 if len(componentTexts) > 0 {
497 s.printNewLine()
498 s.print(0, s.colorize(lightGrayColor, "%s", componentCodeLocations[len(componentCodeLocations)-1]))
499 }
500 s.printNewLine()
501 indentation = 1
502 } else {
503 indentation--
504 }
505
506 return indentation
507}
508
509func (s *consoleStenographer) orderedMeasurementKeys(measurements map[string]*types.SpecMeasurement) []string {
510 orderedKeys := make([]string, len(measurements))
511 for key, measurement := range measurements {
512 orderedKeys[measurement.Order] = key
513 }
514 return orderedKeys
515}
516
517func (s *consoleStenographer) measurementReport(spec *types.SpecSummary, succinct bool) string {
518 if len(spec.Measurements) == 0 {
519 return "Found no measurements"
520 }
521
522 message := []string{}
523 orderedKeys := s.orderedMeasurementKeys(spec.Measurements)
524
525 if succinct {
526 message = append(message, fmt.Sprintf("%s samples:", s.colorize(boldStyle, "%d", spec.NumberOfSamples)))
527 for _, key := range orderedKeys {
528 measurement := spec.Measurements[key]
529 message = append(message, fmt.Sprintf(" %s - %s: %s%s, %s: %s%s ± %s%s, %s: %s%s",
530 s.colorize(boldStyle, "%s", measurement.Name),
531 measurement.SmallestLabel,
532 s.colorize(greenColor, measurement.PrecisionFmt(), measurement.Smallest),
533 measurement.Units,
534 measurement.AverageLabel,
535 s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.Average),
536 measurement.Units,
537 s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.StdDeviation),
538 measurement.Units,
539 measurement.LargestLabel,
540 s.colorize(redColor, measurement.PrecisionFmt(), measurement.Largest),
541 measurement.Units,
542 ))
543 }
544 } else {
545 message = append(message, fmt.Sprintf("Ran %s samples:", s.colorize(boldStyle, "%d", spec.NumberOfSamples)))
546 for _, key := range orderedKeys {
547 measurement := spec.Measurements[key]
548 info := ""
549 if measurement.Info != nil {
550 message = append(message, fmt.Sprintf("%v", measurement.Info))
551 }
552
553 message = append(message, fmt.Sprintf("%s:\n%s %s: %s%s\n %s: %s%s\n %s: %s%s ± %s%s",
554 s.colorize(boldStyle, "%s", measurement.Name),
555 info,
556 measurement.SmallestLabel,
557 s.colorize(greenColor, measurement.PrecisionFmt(), measurement.Smallest),
558 measurement.Units,
559 measurement.LargestLabel,
560 s.colorize(redColor, measurement.PrecisionFmt(), measurement.Largest),
561 measurement.Units,
562 measurement.AverageLabel,
563 s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.Average),
564 measurement.Units,
565 s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.StdDeviation),
566 measurement.Units,
567 ))
568 }
569 }
570
571 return strings.Join(message, "\n")
572}