github.com/Secbyte/godog@v0.7.14-0.20200116175429-d8f0aeeb70cf/fmt_events.go (about) 1 package godog 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 8 "github.com/DATA-DOG/godog/gherkin" 9 ) 10 11 const nanoSec = 1000000 12 const spec = "0.1.0" 13 14 func init() { 15 Format("events", fmt.Sprintf("Produces JSON event stream, based on spec: %s.", spec), eventsFunc) 16 } 17 18 func eventsFunc(suite string, out io.Writer) Formatter { 19 formatter := &events{ 20 basefmt: basefmt{ 21 started: timeNowFunc(), 22 indent: 2, 23 out: out, 24 }, 25 } 26 27 formatter.event(&struct { 28 Event string `json:"event"` 29 Version string `json:"version"` 30 Timestamp int64 `json:"timestamp"` 31 Suite string `json:"suite"` 32 }{ 33 "TestRunStarted", 34 spec, 35 timeNowFunc().UnixNano() / nanoSec, 36 suite, 37 }) 38 39 return formatter 40 } 41 42 type events struct { 43 basefmt 44 45 // currently running feature path, to be part of id. 46 // this is sadly not passed by gherkin nodes. 47 // it restricts this formatter to run only in synchronous single 48 // threaded execution. Unless running a copy of formatter for each feature 49 path string 50 stat stepType // last step status, before skipped 51 outlineSteps int // number of current outline scenario steps 52 } 53 54 func (f *events) event(ev interface{}) { 55 data, err := json.Marshal(ev) 56 if err != nil { 57 panic(fmt.Sprintf("failed to marshal stream event: %+v - %v", ev, err)) 58 } 59 fmt.Fprintln(f.out, string(data)) 60 } 61 62 func (f *events) Node(n interface{}) { 63 f.basefmt.Node(n) 64 65 var id string 66 var undefined bool 67 switch t := n.(type) { 68 case *gherkin.Scenario: 69 id = fmt.Sprintf("%s:%d", f.path, t.Location.Line) 70 undefined = len(t.Steps) == 0 71 case *gherkin.TableRow: 72 id = fmt.Sprintf("%s:%d", f.path, t.Location.Line) 73 undefined = f.outlineSteps == 0 74 case *gherkin.ScenarioOutline: 75 f.outlineSteps = len(t.Steps) 76 } 77 78 if len(id) == 0 { 79 return 80 } 81 82 f.event(&struct { 83 Event string `json:"event"` 84 Location string `json:"location"` 85 Timestamp int64 `json:"timestamp"` 86 }{ 87 "TestCaseStarted", 88 id, 89 timeNowFunc().UnixNano() / nanoSec, 90 }) 91 92 if undefined { 93 // @TODO: is status undefined or passed? when there are no steps 94 // for this scenario 95 f.event(&struct { 96 Event string `json:"event"` 97 Location string `json:"location"` 98 Timestamp int64 `json:"timestamp"` 99 Status string `json:"status"` 100 }{ 101 "TestCaseFinished", 102 id, 103 timeNowFunc().UnixNano() / nanoSec, 104 "undefined", 105 }) 106 } 107 } 108 109 func (f *events) Feature(ft *gherkin.Feature, p string, c []byte) { 110 f.basefmt.Feature(ft, p, c) 111 f.path = p 112 f.event(&struct { 113 Event string `json:"event"` 114 Location string `json:"location"` 115 Source string `json:"source"` 116 }{ 117 "TestSource", 118 fmt.Sprintf("%s:%d", p, ft.Location.Line), 119 string(c), 120 }) 121 } 122 123 func (f *events) Summary() { 124 // @TODO: determine status 125 status := passed 126 if len(f.failed) > 0 { 127 status = failed 128 } else if len(f.passed) == 0 { 129 if len(f.undefined) > len(f.pending) { 130 status = undefined 131 } else { 132 status = pending 133 } 134 } 135 136 snips := f.snippets() 137 if len(snips) > 0 { 138 snips = "You can implement step definitions for undefined steps with these snippets:\n" + snips 139 } 140 141 f.event(&struct { 142 Event string `json:"event"` 143 Status string `json:"status"` 144 Timestamp int64 `json:"timestamp"` 145 Snippets string `json:"snippets"` 146 Memory string `json:"memory"` 147 }{ 148 "TestRunFinished", 149 status.String(), 150 timeNowFunc().UnixNano() / nanoSec, 151 snips, 152 "", // @TODO not sure that could be correctly implemented 153 }) 154 } 155 156 func (f *events) step(res *stepResult) { 157 var errMsg string 158 if res.err != nil { 159 errMsg = res.err.Error() 160 } 161 f.event(&struct { 162 Event string `json:"event"` 163 Location string `json:"location"` 164 Timestamp int64 `json:"timestamp"` 165 Status string `json:"status"` 166 Summary string `json:"summary,omitempty"` 167 }{ 168 "TestStepFinished", 169 fmt.Sprintf("%s:%d", f.path, res.step.Location.Line), 170 timeNowFunc().UnixNano() / nanoSec, 171 res.typ.String(), 172 errMsg, 173 }) 174 175 // determine if test case has finished 176 var finished bool 177 var line int 178 switch t := f.owner.(type) { 179 case *gherkin.TableRow: 180 line = t.Location.Line 181 finished = f.isLastStep(res.step) 182 case *gherkin.Scenario: 183 line = t.Location.Line 184 finished = f.isLastStep(res.step) 185 } 186 187 if finished { 188 f.event(&struct { 189 Event string `json:"event"` 190 Location string `json:"location"` 191 Timestamp int64 `json:"timestamp"` 192 Status string `json:"status"` 193 }{ 194 "TestCaseFinished", 195 fmt.Sprintf("%s:%d", f.path, line), 196 timeNowFunc().UnixNano() / nanoSec, 197 f.stat.String(), 198 }) 199 } 200 } 201 202 func (f *events) Defined(step *gherkin.Step, def *StepDef) { 203 if def != nil { 204 m := def.Expr.FindStringSubmatchIndex(step.Text)[2:] 205 var args [][2]int 206 for i := 0; i < len(m)/2; i++ { 207 pair := m[i : i*2+2] 208 var idxs [2]int 209 idxs[0] = pair[0] 210 idxs[1] = pair[1] 211 args = append(args, idxs) 212 } 213 214 if len(args) == 0 { 215 args = make([][2]int, 0) 216 } 217 218 f.event(&struct { 219 Event string `json:"event"` 220 Location string `json:"location"` 221 DefID string `json:"definition_id"` 222 Args [][2]int `json:"arguments"` 223 }{ 224 "StepDefinitionFound", 225 fmt.Sprintf("%s:%d", f.path, step.Location.Line), 226 def.definitionID(), 227 args, 228 }) 229 } 230 231 f.event(&struct { 232 Event string `json:"event"` 233 Location string `json:"location"` 234 Timestamp int64 `json:"timestamp"` 235 }{ 236 "TestStepStarted", 237 fmt.Sprintf("%s:%d", f.path, step.Location.Line), 238 timeNowFunc().UnixNano() / nanoSec, 239 }) 240 } 241 242 func (f *events) Passed(step *gherkin.Step, match *StepDef) { 243 f.basefmt.Passed(step, match) 244 f.stat = passed 245 f.step(f.passed[len(f.passed)-1]) 246 } 247 248 func (f *events) Skipped(step *gherkin.Step, match *StepDef) { 249 f.basefmt.Skipped(step, match) 250 f.step(f.skipped[len(f.skipped)-1]) 251 } 252 253 func (f *events) Undefined(step *gherkin.Step, match *StepDef) { 254 f.basefmt.Undefined(step, match) 255 f.stat = undefined 256 f.step(f.undefined[len(f.undefined)-1]) 257 } 258 259 func (f *events) Failed(step *gherkin.Step, match *StepDef, err error) { 260 f.basefmt.Failed(step, match, err) 261 f.stat = failed 262 f.step(f.failed[len(f.failed)-1]) 263 } 264 265 func (f *events) Pending(step *gherkin.Step, match *StepDef) { 266 f.stat = pending 267 f.basefmt.Pending(step, match) 268 f.step(f.pending[len(f.pending)-1]) 269 }