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