123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 |
- /*
- The stenographer is used by Ginkgo's reporters to generate output.
- Move along, nothing to see here.
- */
- package stenographer
- import (
- "fmt"
- "io"
- "runtime"
- "strings"
- "github.com/onsi/ginkgo/types"
- )
- const defaultStyle = "\x1b[0m"
- const boldStyle = "\x1b[1m"
- const redColor = "\x1b[91m"
- const greenColor = "\x1b[32m"
- const yellowColor = "\x1b[33m"
- const cyanColor = "\x1b[36m"
- const grayColor = "\x1b[90m"
- const lightGrayColor = "\x1b[37m"
- type cursorStateType int
- const (
- cursorStateTop cursorStateType = iota
- cursorStateStreaming
- cursorStateMidBlock
- cursorStateEndBlock
- )
- type Stenographer interface {
- AnnounceSuite(description string, randomSeed int64, randomizingAll bool, succinct bool)
- AnnounceAggregatedParallelRun(nodes int, succinct bool)
- AnnounceParallelRun(node int, nodes int, succinct bool)
- AnnounceTotalNumberOfSpecs(total int, succinct bool)
- AnnounceNumberOfSpecs(specsToRun int, total int, succinct bool)
- AnnounceSpecRunCompletion(summary *types.SuiteSummary, succinct bool)
- AnnounceSpecWillRun(spec *types.SpecSummary)
- AnnounceBeforeSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool)
- AnnounceAfterSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool)
- AnnounceCapturedOutput(output string)
- AnnounceSuccesfulSpec(spec *types.SpecSummary)
- AnnounceSuccesfulSlowSpec(spec *types.SpecSummary, succinct bool)
- AnnounceSuccesfulMeasurement(spec *types.SpecSummary, succinct bool)
- AnnouncePendingSpec(spec *types.SpecSummary, noisy bool)
- AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool)
- AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool)
- AnnounceSpecPanicked(spec *types.SpecSummary, succinct bool, fullTrace bool)
- AnnounceSpecFailed(spec *types.SpecSummary, succinct bool, fullTrace bool)
- SummarizeFailures(summaries []*types.SpecSummary)
- }
- func New(color bool, enableFlakes bool, writer io.Writer) Stenographer {
- denoter := "•"
- if runtime.GOOS == "windows" {
- denoter = "+"
- }
- return &consoleStenographer{
- color: color,
- denoter: denoter,
- cursorState: cursorStateTop,
- enableFlakes: enableFlakes,
- w: writer,
- }
- }
- type consoleStenographer struct {
- color bool
- denoter string
- cursorState cursorStateType
- enableFlakes bool
- w io.Writer
- }
- var alternatingColors = []string{defaultStyle, grayColor}
- func (s *consoleStenographer) AnnounceSuite(description string, randomSeed int64, randomizingAll bool, succinct bool) {
- if succinct {
- s.print(0, "[%d] %s ", randomSeed, s.colorize(boldStyle, description))
- return
- }
- s.printBanner(fmt.Sprintf("Running Suite: %s", description), "=")
- s.print(0, "Random Seed: %s", s.colorize(boldStyle, "%d", randomSeed))
- if randomizingAll {
- s.print(0, " - Will randomize all specs")
- }
- s.printNewLine()
- }
- func (s *consoleStenographer) AnnounceParallelRun(node int, nodes int, succinct bool) {
- if succinct {
- s.print(0, "- node #%d ", node)
- return
- }
- s.println(0,
- "Parallel test node %s/%s.",
- s.colorize(boldStyle, "%d", node),
- s.colorize(boldStyle, "%d", nodes),
- )
- s.printNewLine()
- }
- func (s *consoleStenographer) AnnounceAggregatedParallelRun(nodes int, succinct bool) {
- if succinct {
- s.print(0, "- %d nodes ", nodes)
- return
- }
- s.println(0,
- "Running in parallel across %s nodes",
- s.colorize(boldStyle, "%d", nodes),
- )
- s.printNewLine()
- }
- func (s *consoleStenographer) AnnounceNumberOfSpecs(specsToRun int, total int, succinct bool) {
- if succinct {
- s.print(0, "- %d/%d specs ", specsToRun, total)
- s.stream()
- return
- }
- s.println(0,
- "Will run %s of %s specs",
- s.colorize(boldStyle, "%d", specsToRun),
- s.colorize(boldStyle, "%d", total),
- )
- s.printNewLine()
- }
- func (s *consoleStenographer) AnnounceTotalNumberOfSpecs(total int, succinct bool) {
- if succinct {
- s.print(0, "- %d specs ", total)
- s.stream()
- return
- }
- s.println(0,
- "Will run %s specs",
- s.colorize(boldStyle, "%d", total),
- )
- s.printNewLine()
- }
- func (s *consoleStenographer) AnnounceSpecRunCompletion(summary *types.SuiteSummary, succinct bool) {
- if succinct && summary.SuiteSucceeded {
- s.print(0, " %s %s ", s.colorize(greenColor, "SUCCESS!"), summary.RunTime)
- return
- }
- s.printNewLine()
- color := greenColor
- if !summary.SuiteSucceeded {
- color = redColor
- }
- s.println(0, s.colorize(boldStyle+color, "Ran %d of %d Specs in %.3f seconds", summary.NumberOfSpecsThatWillBeRun, summary.NumberOfTotalSpecs, summary.RunTime.Seconds()))
- status := ""
- if summary.SuiteSucceeded {
- status = s.colorize(boldStyle+greenColor, "SUCCESS!")
- } else {
- status = s.colorize(boldStyle+redColor, "FAIL!")
- }
- flakes := ""
- if s.enableFlakes {
- flakes = " | " + s.colorize(yellowColor+boldStyle, "%d Flaked", summary.NumberOfFlakedSpecs)
- }
- s.print(0,
- "%s -- %s | %s | %s | %s\n",
- status,
- s.colorize(greenColor+boldStyle, "%d Passed", summary.NumberOfPassedSpecs),
- s.colorize(redColor+boldStyle, "%d Failed", summary.NumberOfFailedSpecs)+flakes,
- s.colorize(yellowColor+boldStyle, "%d Pending", summary.NumberOfPendingSpecs),
- s.colorize(cyanColor+boldStyle, "%d Skipped", summary.NumberOfSkippedSpecs),
- )
- }
- func (s *consoleStenographer) AnnounceSpecWillRun(spec *types.SpecSummary) {
- s.startBlock()
- for i, text := range spec.ComponentTexts[1 : len(spec.ComponentTexts)-1] {
- s.print(0, s.colorize(alternatingColors[i%2], text)+" ")
- }
- indentation := 0
- if len(spec.ComponentTexts) > 2 {
- indentation = 1
- s.printNewLine()
- }
- index := len(spec.ComponentTexts) - 1
- s.print(indentation, s.colorize(boldStyle, spec.ComponentTexts[index]))
- s.printNewLine()
- s.print(indentation, s.colorize(lightGrayColor, spec.ComponentCodeLocations[index].String()))
- s.printNewLine()
- s.midBlock()
- }
- func (s *consoleStenographer) AnnounceBeforeSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool) {
- s.announceSetupFailure("BeforeSuite", summary, succinct, fullTrace)
- }
- func (s *consoleStenographer) AnnounceAfterSuiteFailure(summary *types.SetupSummary, succinct bool, fullTrace bool) {
- s.announceSetupFailure("AfterSuite", summary, succinct, fullTrace)
- }
- func (s *consoleStenographer) announceSetupFailure(name string, summary *types.SetupSummary, succinct bool, fullTrace bool) {
- s.startBlock()
- var message string
- switch summary.State {
- case types.SpecStateFailed:
- message = "Failure"
- case types.SpecStatePanicked:
- message = "Panic"
- case types.SpecStateTimedOut:
- message = "Timeout"
- }
- s.println(0, s.colorize(redColor+boldStyle, "%s [%.3f seconds]", message, summary.RunTime.Seconds()))
- indentation := s.printCodeLocationBlock([]string{name}, []types.CodeLocation{summary.CodeLocation}, summary.ComponentType, 0, summary.State, true)
- s.printNewLine()
- s.printFailure(indentation, summary.State, summary.Failure, fullTrace)
- s.endBlock()
- }
- func (s *consoleStenographer) AnnounceCapturedOutput(output string) {
- if output == "" {
- return
- }
- s.startBlock()
- s.println(0, output)
- s.midBlock()
- }
- func (s *consoleStenographer) AnnounceSuccesfulSpec(spec *types.SpecSummary) {
- s.print(0, s.colorize(greenColor, s.denoter))
- s.stream()
- }
- func (s *consoleStenographer) AnnounceSuccesfulSlowSpec(spec *types.SpecSummary, succinct bool) {
- s.printBlockWithMessage(
- s.colorize(greenColor, "%s [SLOW TEST:%.3f seconds]", s.denoter, spec.RunTime.Seconds()),
- "",
- spec,
- succinct,
- )
- }
- func (s *consoleStenographer) AnnounceSuccesfulMeasurement(spec *types.SpecSummary, succinct bool) {
- s.printBlockWithMessage(
- s.colorize(greenColor, "%s [MEASUREMENT]", s.denoter),
- s.measurementReport(spec, succinct),
- spec,
- succinct,
- )
- }
- func (s *consoleStenographer) AnnouncePendingSpec(spec *types.SpecSummary, noisy bool) {
- if noisy {
- s.printBlockWithMessage(
- s.colorize(yellowColor, "P [PENDING]"),
- "",
- spec,
- false,
- )
- } else {
- s.print(0, s.colorize(yellowColor, "P"))
- s.stream()
- }
- }
- func (s *consoleStenographer) AnnounceSkippedSpec(spec *types.SpecSummary, succinct bool, fullTrace bool) {
- // Skips at runtime will have a non-empty spec.Failure. All others should be succinct.
- if succinct || spec.Failure == (types.SpecFailure{}) {
- s.print(0, s.colorize(cyanColor, "S"))
- s.stream()
- } else {
- s.startBlock()
- s.println(0, s.colorize(cyanColor+boldStyle, "S [SKIPPING]%s [%.3f seconds]", s.failureContext(spec.Failure.ComponentType), spec.RunTime.Seconds()))
- indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, spec.Failure.ComponentType, spec.Failure.ComponentIndex, spec.State, succinct)
- s.printNewLine()
- s.printSkip(indentation, spec.Failure)
- s.endBlock()
- }
- }
- func (s *consoleStenographer) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) {
- s.printSpecFailure(fmt.Sprintf("%s... Timeout", s.denoter), spec, succinct, fullTrace)
- }
- func (s *consoleStenographer) AnnounceSpecPanicked(spec *types.SpecSummary, succinct bool, fullTrace bool) {
- s.printSpecFailure(fmt.Sprintf("%s! Panic", s.denoter), spec, succinct, fullTrace)
- }
- func (s *consoleStenographer) AnnounceSpecFailed(spec *types.SpecSummary, succinct bool, fullTrace bool) {
- s.printSpecFailure(fmt.Sprintf("%s Failure", s.denoter), spec, succinct, fullTrace)
- }
- func (s *consoleStenographer) SummarizeFailures(summaries []*types.SpecSummary) {
- failingSpecs := []*types.SpecSummary{}
- for _, summary := range summaries {
- if summary.HasFailureState() {
- failingSpecs = append(failingSpecs, summary)
- }
- }
- if len(failingSpecs) == 0 {
- return
- }
- s.printNewLine()
- s.printNewLine()
- plural := "s"
- if len(failingSpecs) == 1 {
- plural = ""
- }
- s.println(0, s.colorize(redColor+boldStyle, "Summarizing %d Failure%s:", len(failingSpecs), plural))
- for _, summary := range failingSpecs {
- s.printNewLine()
- if summary.HasFailureState() {
- if summary.TimedOut() {
- s.print(0, s.colorize(redColor+boldStyle, "[Timeout...] "))
- } else if summary.Panicked() {
- s.print(0, s.colorize(redColor+boldStyle, "[Panic!] "))
- } else if summary.Failed() {
- s.print(0, s.colorize(redColor+boldStyle, "[Fail] "))
- }
- s.printSpecContext(summary.ComponentTexts, summary.ComponentCodeLocations, summary.Failure.ComponentType, summary.Failure.ComponentIndex, summary.State, true)
- s.printNewLine()
- s.println(0, s.colorize(lightGrayColor, summary.Failure.Location.String()))
- }
- }
- }
- func (s *consoleStenographer) startBlock() {
- if s.cursorState == cursorStateStreaming {
- s.printNewLine()
- s.printDelimiter()
- } else if s.cursorState == cursorStateMidBlock {
- s.printNewLine()
- }
- }
- func (s *consoleStenographer) midBlock() {
- s.cursorState = cursorStateMidBlock
- }
- func (s *consoleStenographer) endBlock() {
- s.printDelimiter()
- s.cursorState = cursorStateEndBlock
- }
- func (s *consoleStenographer) stream() {
- s.cursorState = cursorStateStreaming
- }
- func (s *consoleStenographer) printBlockWithMessage(header string, message string, spec *types.SpecSummary, succinct bool) {
- s.startBlock()
- s.println(0, header)
- indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, types.SpecComponentTypeInvalid, 0, spec.State, succinct)
- if message != "" {
- s.printNewLine()
- s.println(indentation, message)
- }
- s.endBlock()
- }
- func (s *consoleStenographer) printSpecFailure(message string, spec *types.SpecSummary, succinct bool, fullTrace bool) {
- s.startBlock()
- s.println(0, s.colorize(redColor+boldStyle, "%s%s [%.3f seconds]", message, s.failureContext(spec.Failure.ComponentType), spec.RunTime.Seconds()))
- indentation := s.printCodeLocationBlock(spec.ComponentTexts, spec.ComponentCodeLocations, spec.Failure.ComponentType, spec.Failure.ComponentIndex, spec.State, succinct)
- s.printNewLine()
- s.printFailure(indentation, spec.State, spec.Failure, fullTrace)
- s.endBlock()
- }
- func (s *consoleStenographer) failureContext(failedComponentType types.SpecComponentType) string {
- switch failedComponentType {
- case types.SpecComponentTypeBeforeSuite:
- return " in Suite Setup (BeforeSuite)"
- case types.SpecComponentTypeAfterSuite:
- return " in Suite Teardown (AfterSuite)"
- case types.SpecComponentTypeBeforeEach:
- return " in Spec Setup (BeforeEach)"
- case types.SpecComponentTypeJustBeforeEach:
- return " in Spec Setup (JustBeforeEach)"
- case types.SpecComponentTypeAfterEach:
- return " in Spec Teardown (AfterEach)"
- }
- return ""
- }
- func (s *consoleStenographer) printSkip(indentation int, spec types.SpecFailure) {
- s.println(indentation, s.colorize(cyanColor, spec.Message))
- s.printNewLine()
- s.println(indentation, spec.Location.String())
- }
- func (s *consoleStenographer) printFailure(indentation int, state types.SpecState, failure types.SpecFailure, fullTrace bool) {
- if state == types.SpecStatePanicked {
- s.println(indentation, s.colorize(redColor+boldStyle, failure.Message))
- s.println(indentation, s.colorize(redColor, failure.ForwardedPanic))
- s.println(indentation, failure.Location.String())
- s.printNewLine()
- s.println(indentation, s.colorize(redColor, "Full Stack Trace"))
- s.println(indentation, failure.Location.FullStackTrace)
- } else {
- s.println(indentation, s.colorize(redColor, failure.Message))
- s.printNewLine()
- s.println(indentation, failure.Location.String())
- if fullTrace {
- s.printNewLine()
- s.println(indentation, s.colorize(redColor, "Full Stack Trace"))
- s.println(indentation, failure.Location.FullStackTrace)
- }
- }
- }
- func (s *consoleStenographer) printSpecContext(componentTexts []string, componentCodeLocations []types.CodeLocation, failedComponentType types.SpecComponentType, failedComponentIndex int, state types.SpecState, succinct bool) int {
- startIndex := 1
- indentation := 0
- if len(componentTexts) == 1 {
- startIndex = 0
- }
- for i := startIndex; i < len(componentTexts); i++ {
- if (state.IsFailure() || state == types.SpecStateSkipped) && i == failedComponentIndex {
- color := redColor
- if state == types.SpecStateSkipped {
- color = cyanColor
- }
- blockType := ""
- switch failedComponentType {
- case types.SpecComponentTypeBeforeSuite:
- blockType = "BeforeSuite"
- case types.SpecComponentTypeAfterSuite:
- blockType = "AfterSuite"
- case types.SpecComponentTypeBeforeEach:
- blockType = "BeforeEach"
- case types.SpecComponentTypeJustBeforeEach:
- blockType = "JustBeforeEach"
- case types.SpecComponentTypeAfterEach:
- blockType = "AfterEach"
- case types.SpecComponentTypeIt:
- blockType = "It"
- case types.SpecComponentTypeMeasure:
- blockType = "Measurement"
- }
- if succinct {
- s.print(0, s.colorize(color+boldStyle, "[%s] %s ", blockType, componentTexts[i]))
- } else {
- s.println(indentation, s.colorize(color+boldStyle, "%s [%s]", componentTexts[i], blockType))
- s.println(indentation, s.colorize(grayColor, "%s", componentCodeLocations[i]))
- }
- } else {
- if succinct {
- s.print(0, s.colorize(alternatingColors[i%2], "%s ", componentTexts[i]))
- } else {
- s.println(indentation, componentTexts[i])
- s.println(indentation, s.colorize(grayColor, "%s", componentCodeLocations[i]))
- }
- }
- indentation++
- }
- return indentation
- }
- func (s *consoleStenographer) printCodeLocationBlock(componentTexts []string, componentCodeLocations []types.CodeLocation, failedComponentType types.SpecComponentType, failedComponentIndex int, state types.SpecState, succinct bool) int {
- indentation := s.printSpecContext(componentTexts, componentCodeLocations, failedComponentType, failedComponentIndex, state, succinct)
- if succinct {
- if len(componentTexts) > 0 {
- s.printNewLine()
- s.print(0, s.colorize(lightGrayColor, "%s", componentCodeLocations[len(componentCodeLocations)-1]))
- }
- s.printNewLine()
- indentation = 1
- } else {
- indentation--
- }
- return indentation
- }
- func (s *consoleStenographer) orderedMeasurementKeys(measurements map[string]*types.SpecMeasurement) []string {
- orderedKeys := make([]string, len(measurements))
- for key, measurement := range measurements {
- orderedKeys[measurement.Order] = key
- }
- return orderedKeys
- }
- func (s *consoleStenographer) measurementReport(spec *types.SpecSummary, succinct bool) string {
- if len(spec.Measurements) == 0 {
- return "Found no measurements"
- }
- message := []string{}
- orderedKeys := s.orderedMeasurementKeys(spec.Measurements)
- if succinct {
- message = append(message, fmt.Sprintf("%s samples:", s.colorize(boldStyle, "%d", spec.NumberOfSamples)))
- for _, key := range orderedKeys {
- measurement := spec.Measurements[key]
- message = append(message, fmt.Sprintf(" %s - %s: %s%s, %s: %s%s ± %s%s, %s: %s%s",
- s.colorize(boldStyle, "%s", measurement.Name),
- measurement.SmallestLabel,
- s.colorize(greenColor, measurement.PrecisionFmt(), measurement.Smallest),
- measurement.Units,
- measurement.AverageLabel,
- s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.Average),
- measurement.Units,
- s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.StdDeviation),
- measurement.Units,
- measurement.LargestLabel,
- s.colorize(redColor, measurement.PrecisionFmt(), measurement.Largest),
- measurement.Units,
- ))
- }
- } else {
- message = append(message, fmt.Sprintf("Ran %s samples:", s.colorize(boldStyle, "%d", spec.NumberOfSamples)))
- for _, key := range orderedKeys {
- measurement := spec.Measurements[key]
- info := ""
- if measurement.Info != nil {
- message = append(message, fmt.Sprintf("%v", measurement.Info))
- }
- message = append(message, fmt.Sprintf("%s:\n%s %s: %s%s\n %s: %s%s\n %s: %s%s ± %s%s",
- s.colorize(boldStyle, "%s", measurement.Name),
- info,
- measurement.SmallestLabel,
- s.colorize(greenColor, measurement.PrecisionFmt(), measurement.Smallest),
- measurement.Units,
- measurement.LargestLabel,
- s.colorize(redColor, measurement.PrecisionFmt(), measurement.Largest),
- measurement.Units,
- measurement.AverageLabel,
- s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.Average),
- measurement.Units,
- s.colorize(cyanColor, measurement.PrecisionFmt(), measurement.StdDeviation),
- measurement.Units,
- ))
- }
- }
- return strings.Join(message, "\n")
- }
|