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