github.com/getgauge/gauge@v1.6.9/reporter/reporter.go (about) 1 /*---------------------------------------------------------------- 2 * Copyright (c) ThoughtWorks, Inc. 3 * Licensed under the Apache License, Version 2.0 4 * See LICENSE in the project root for license information. 5 *----------------------------------------------------------------*/ 6 7 package reporter 8 9 import ( 10 "fmt" 11 "io" 12 "os" 13 "runtime/debug" 14 15 "sync" 16 17 "github.com/getgauge/gauge-proto/go/gauge_messages" 18 "github.com/getgauge/gauge/execution/event" 19 "github.com/getgauge/gauge/execution/result" 20 "github.com/getgauge/gauge/formatter" 21 "github.com/getgauge/gauge/gauge" 22 "github.com/getgauge/gauge/logger" 23 ) 24 25 // IsParallel represents console reporting format based on simple/parallel execution 26 var IsParallel bool 27 28 // NumberOfExecutionStreams indicates the total number of parallel execution streams 29 var NumberOfExecutionStreams int 30 31 // SimpleConsoleOutput represents if coloring should be removed from the Console output 32 var SimpleConsoleOutput bool 33 34 // Verbose represents level of console Reporting. If true its at step level, else at scenario level. 35 var Verbose bool 36 37 // MachineReadable represents if output should be in JSON format. 38 var MachineReadable bool 39 40 const newline = "\n" 41 42 // Reporter reports the progress of spec execution. It reports 43 // 1. Which spec / scenarion / step (if verbose) is currently executing. 44 // 2. Status (pass/fail) of the spec / scenario / step (if verbose) once its executed. 45 type Reporter interface { 46 SuiteStart() 47 SpecStart(*gauge.Specification, result.Result) 48 SpecEnd(*gauge.Specification, result.Result) 49 ScenarioStart(*gauge.Scenario, *gauge_messages.ExecutionInfo, result.Result) 50 ScenarioEnd(*gauge.Scenario, result.Result, *gauge_messages.ExecutionInfo) 51 StepStart(string) 52 StepEnd(gauge.Step, result.Result, *gauge_messages.ExecutionInfo) 53 ConceptStart(string) 54 ConceptEnd(result.Result) 55 DataTable(string) 56 SuiteEnd(result.Result) 57 58 Errorf(string, ...interface{}) 59 60 io.Writer 61 } 62 63 var currentReporter Reporter 64 65 func reporter(e event.ExecutionEvent) Reporter { 66 if IsParallel { 67 return ParallelReporter(e.Stream) 68 } 69 return Current() 70 } 71 72 // Current returns the current instance of Reporter, if present. Else, it returns a new Reporter. 73 func Current() Reporter { 74 if currentReporter == nil { 75 if MachineReadable { 76 currentReporter = newJSONConsole(os.Stdout, IsParallel, 0) 77 } else if SimpleConsoleOutput { 78 currentReporter = newSimpleConsole(os.Stdout) 79 } else if Verbose { 80 currentReporter = newVerboseColoredConsole(os.Stdout) 81 } else { 82 currentReporter = newColoredConsole(os.Stdout) 83 } 84 } 85 return currentReporter 86 } 87 88 type parallelReportWriter struct { 89 nRunner int 90 } 91 92 func (p *parallelReportWriter) Write(b []byte) (int, error) { 93 return fmt.Printf("[runner: %d] %s", p.nRunner, string(b)) 94 } 95 96 // ParallelReporter returns the instance of parallel console reporter 97 func ParallelReporter(n int) Reporter { 98 if r, ok := parallelReporters[n]; ok { 99 return r 100 } 101 return Current() 102 } 103 104 var parallelReporters map[int]Reporter 105 106 func initParallelReporters() { 107 parallelReporters = make(map[int]Reporter, NumberOfExecutionStreams) 108 for i := 1; i <= NumberOfExecutionStreams; i++ { 109 if MachineReadable { 110 parallelReporters[i] = newJSONConsole(os.Stdout, true, i) 111 } else { 112 writer := ¶llelReportWriter{nRunner: i} 113 parallelReporters[i] = newSimpleConsole(writer) 114 } 115 } 116 } 117 118 // ListenExecutionEvents listens to all execution events for reporting on console 119 func ListenExecutionEvents(wg *sync.WaitGroup) { 120 ch := make(chan event.ExecutionEvent) 121 initParallelReporters() 122 event.Register(ch, event.SuiteStart, event.SpecStart, event.SpecEnd, event.ScenarioStart, event.ScenarioEnd, event.StepStart, event.StepEnd, event.ConceptStart, event.ConceptEnd, event.SuiteEnd) 123 var r Reporter 124 wg.Add(1) 125 126 go func() { 127 defer recoverPanic() 128 for { 129 e := <-ch 130 r = reporter(e) 131 switch e.Topic { 132 case event.SuiteStart: 133 r.SuiteStart() 134 case event.SpecStart: 135 r.SpecStart(e.Item.(*gauge.Specification), e.Result) 136 case event.ScenarioStart: 137 skipped := e.Result.(*result.ScenarioResult).ProtoScenario.GetExecutionStatus() == gauge_messages.ExecutionStatus_SKIPPED 138 sce := e.Item.(*gauge.Scenario) 139 // if it is datatable driven execution 140 if !skipped { 141 if sce.SpecDataTableRow.GetRowCount() != 0 { 142 r.DataTable(formatter.FormatTable(&sce.SpecDataTableRow)) 143 } 144 if sce.ScenarioDataTableRow.GetRowCount() != 0 { 145 r.DataTable(formatter.FormatTable(&sce.ScenarioDataTableRow)) 146 } 147 } 148 r.ScenarioStart(sce, e.ExecutionInfo, e.Result) 149 case event.ConceptStart: 150 r.ConceptStart(formatter.FormatStep(e.Item.(*gauge.Step))) 151 case event.StepStart: 152 r.StepStart(formatter.FormatStepWithResolvedArgs(e.Item.(*gauge.Step))) 153 case event.StepEnd: 154 r.StepEnd(e.Item.(gauge.Step), e.Result, e.ExecutionInfo) 155 case event.ConceptEnd: 156 r.ConceptEnd(e.Result) 157 case event.ScenarioEnd: 158 r.ScenarioEnd(e.Item.(*gauge.Scenario), e.Result, e.ExecutionInfo) 159 case event.SpecEnd: 160 r.SpecEnd(e.Item.(*gauge.Specification), e.Result) 161 case event.SuiteEnd: 162 r.SuiteEnd(e.Result) 163 wg.Done() 164 } 165 } 166 }() 167 } 168 169 func recoverPanic() { 170 if r := recover(); r != nil { 171 logger.Fatalf(true, "%v\n%s", r, string(debug.Stack())) 172 } 173 }