arnaucube
arnaucube/mirror-to-giteaPublic Self-hosted repository
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}