2016-05-22 13:04:37 +00:00
/ *
The stenographer is used by Ginkgo ' s reporters to generate output .
Move along , nothing to see here .
* /
package stenographer
import (
"fmt"
2018-10-08 13:14:49 +00:00
"io"
"runtime"
2016-05-22 13:04:37 +00:00
"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 )
2018-10-08 13:14:49 +00:00
AnnounceParallelRun ( node int , nodes int , succinct bool )
AnnounceTotalNumberOfSpecs ( total int , succinct bool )
2016-05-22 13:04:37 +00:00
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 )
}
2018-10-08 13:14:49 +00:00
func New ( color bool , enableFlakes bool , writer io . Writer ) Stenographer {
denoter := "•"
if runtime . GOOS == "windows" {
denoter = "+"
}
2016-05-22 13:04:37 +00:00
return & consoleStenographer {
2018-10-08 13:14:49 +00:00
color : color ,
denoter : denoter ,
cursorState : cursorStateTop ,
enableFlakes : enableFlakes ,
w : writer ,
2016-05-22 13:04:37 +00:00
}
}
type consoleStenographer struct {
2018-10-08 13:14:49 +00:00
color bool
denoter string
cursorState cursorStateType
enableFlakes bool
w io . Writer
2016-05-22 13:04:37 +00:00
}
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 ( )
}
2018-10-08 13:14:49 +00:00
func ( s * consoleStenographer ) AnnounceParallelRun ( node int , nodes int , succinct bool ) {
2016-05-22 13:04:37 +00:00
if succinct {
s . print ( 0 , "- node #%d " , node )
return
}
s . println ( 0 ,
2018-10-08 13:14:49 +00:00
"Parallel test node %s/%s." ,
2016-05-22 13:04:37 +00:00
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 ( )
}
2018-10-08 13:14:49 +00:00
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 ( )
}
2016-05-22 13:04:37 +00:00
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!" )
}
2018-10-08 13:14:49 +00:00
flakes := ""
if s . enableFlakes {
flakes = " | " + s . colorize ( yellowColor + boldStyle , "%d Flaked" , summary . NumberOfFlakedSpecs )
}
2016-05-22 13:04:37 +00:00
s . print ( 0 ,
2018-10-08 13:14:49 +00:00
"%s -- %s | %s | %s | %s\n" ,
2016-05-22 13:04:37 +00:00
status ,
s . colorize ( greenColor + boldStyle , "%d Passed" , summary . NumberOfPassedSpecs ) ,
2018-10-08 13:14:49 +00:00
s . colorize ( redColor + boldStyle , "%d Failed" , summary . NumberOfFailedSpecs ) + flakes ,
2016-05-22 13:04:37 +00:00
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 ) {
2018-10-08 13:14:49 +00:00
s . print ( 0 , s . colorize ( greenColor , s . denoter ) )
2016-05-22 13:04:37 +00:00
s . stream ( )
}
func ( s * consoleStenographer ) AnnounceSuccesfulSlowSpec ( spec * types . SpecSummary , succinct bool ) {
s . printBlockWithMessage (
2018-10-08 13:14:49 +00:00
s . colorize ( greenColor , "%s [SLOW TEST:%.3f seconds]" , s . denoter , spec . RunTime . Seconds ( ) ) ,
2016-05-22 13:04:37 +00:00
"" ,
spec ,
succinct ,
)
}
func ( s * consoleStenographer ) AnnounceSuccesfulMeasurement ( spec * types . SpecSummary , succinct bool ) {
s . printBlockWithMessage (
2018-10-08 13:14:49 +00:00
s . colorize ( greenColor , "%s [MEASUREMENT]" , s . denoter ) ,
2016-05-22 13:04:37 +00:00
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 ) {
2018-10-08 13:14:49 +00:00
s . printSpecFailure ( fmt . Sprintf ( "%s... Timeout" , s . denoter ) , spec , succinct , fullTrace )
2016-05-22 13:04:37 +00:00
}
func ( s * consoleStenographer ) AnnounceSpecPanicked ( spec * types . SpecSummary , succinct bool , fullTrace bool ) {
2018-10-08 13:14:49 +00:00
s . printSpecFailure ( fmt . Sprintf ( "%s! Panic" , s . denoter ) , spec , succinct , fullTrace )
2016-05-22 13:04:37 +00:00
}
func ( s * consoleStenographer ) AnnounceSpecFailed ( spec * types . SpecSummary , succinct bool , fullTrace bool ) {
2018-10-08 13:14:49 +00:00
s . printSpecFailure ( fmt . Sprintf ( "%s Failure" , s . denoter ) , spec , succinct , fullTrace )
2016-05-22 13:04:37 +00:00
}
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 ,
2018-10-08 13:14:49 +00:00
s . colorize ( greenColor , measurement . PrecisionFmt ( ) , measurement . Smallest ) ,
2016-05-22 13:04:37 +00:00
measurement . Units ,
measurement . AverageLabel ,
2018-10-08 13:14:49 +00:00
s . colorize ( cyanColor , measurement . PrecisionFmt ( ) , measurement . Average ) ,
2016-05-22 13:04:37 +00:00
measurement . Units ,
2018-10-08 13:14:49 +00:00
s . colorize ( cyanColor , measurement . PrecisionFmt ( ) , measurement . StdDeviation ) ,
2016-05-22 13:04:37 +00:00
measurement . Units ,
measurement . LargestLabel ,
2018-10-08 13:14:49 +00:00
s . colorize ( redColor , measurement . PrecisionFmt ( ) , measurement . Largest ) ,
2016-05-22 13:04:37 +00:00
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 ,
2018-10-08 13:14:49 +00:00
s . colorize ( greenColor , measurement . PrecisionFmt ( ) , measurement . Smallest ) ,
2016-05-22 13:04:37 +00:00
measurement . Units ,
measurement . LargestLabel ,
2018-10-08 13:14:49 +00:00
s . colorize ( redColor , measurement . PrecisionFmt ( ) , measurement . Largest ) ,
2016-05-22 13:04:37 +00:00
measurement . Units ,
measurement . AverageLabel ,
2018-10-08 13:14:49 +00:00
s . colorize ( cyanColor , measurement . PrecisionFmt ( ) , measurement . Average ) ,
2016-05-22 13:04:37 +00:00
measurement . Units ,
2018-10-08 13:14:49 +00:00
s . colorize ( cyanColor , measurement . PrecisionFmt ( ) , measurement . StdDeviation ) ,
2016-05-22 13:04:37 +00:00
measurement . Units ,
) )
}
}
return strings . Join ( message , "\n" )
}