godog/fmt_events.go
gedi e71d596404 must be able to customize output for formatters
since it maybe configured by flag values in the future
example:
  - godog -f junit:stdout
  - godog -f junit:output.xml
2016-10-30 18:27:34 +02:00

289 строки
6,4 КиБ
Go

package godog
import (
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"time"
"gopkg.in/cucumber/gherkin-go.v3"
)
const nanoSec = 1000000
const spec = "0.1.0"
func init() {
Format("events", fmt.Sprintf("Produces JSON event stream, based on spec: %s.", spec), eventsFunc)
}
func eventsFunc(out io.Writer) Formatter {
data, err := json.Marshal(&struct {
Time int64
Runner string
}{time.Now().UnixNano(), "godog"})
if err != nil {
panic("failed to marshal run id")
}
hasher := sha1.New()
hasher.Write(data)
formatter := &events{
basefmt: basefmt{
started: time.Now(),
indent: 2,
out: out,
},
runID: hex.EncodeToString(hasher.Sum(nil)),
suite: "main",
}
formatter.event(&struct {
Event string `json:"event"`
RunID string `json:"run_id"`
Version string `json:"version"`
Timestamp int64 `json:"timestamp"`
}{
"TestRunStarted",
formatter.runID,
spec,
time.Now().UnixNano() / nanoSec,
})
return formatter
}
type events struct {
basefmt
runID string
suite string
// currently running feature path, to be part of id.
// this is sadly not passed by gherkin nodes.
// it restricts this formatter to run only in synchronous single
// threaded execution. Unless running a copy of formatter for each feature
path string
stat stepType // last step status, before skipped
}
func (f *events) event(ev interface{}) {
data, err := json.Marshal(ev)
if err != nil {
panic(fmt.Sprintf("failed to marshal stream event: %+v - %v", ev, err))
}
fmt.Fprintln(f.out, string(data))
}
func (f *events) Node(n interface{}) {
f.basefmt.Node(n)
switch t := n.(type) {
case *gherkin.Scenario:
f.event(&struct {
Event string `json:"event"`
RunID string `json:"run_id"`
Suite string `json:"suite"`
Location string `json:"location"`
Timestamp int64 `json:"timestamp"`
}{
"TestCaseStarted",
f.runID,
"main",
fmt.Sprintf("%s:%d", f.path, t.Location.Line),
time.Now().UnixNano() / nanoSec,
})
case *gherkin.TableRow:
f.event(&struct {
Event string `json:"event"`
RunID string `json:"run_id"`
Suite string `json:"suite"`
Location string `json:"location"`
Timestamp int64 `json:"timestamp"`
}{
"TestCaseStarted",
f.runID,
"main",
fmt.Sprintf("%s:%d", f.path, t.Location.Line),
time.Now().UnixNano() / nanoSec,
})
}
}
func (f *events) Feature(ft *gherkin.Feature, p string, c []byte) {
f.basefmt.Feature(ft, p, c)
f.path = p
f.event(&struct {
Event string `json:"event"`
RunID string `json:"run_id"`
Location string `json:"location"`
Source string `json:"source"`
}{
"TestSource",
f.runID,
fmt.Sprintf("%s:%d", p, ft.Location.Line),
string(c),
})
}
func (f *events) Summary() {
// @TODO: determine status
status := passed
if len(f.failed) > 0 {
status = failed
} else if len(f.passed) == 0 {
if len(f.undefined) > len(f.pending) {
status = undefined
} else {
status = pending
}
}
f.event(&struct {
Event string `json:"event"`
RunID string `json:"run_id"`
Status string `json:"status"`
Timestamp int64 `json:"timestamp"`
}{
"TestRunFinished",
f.runID,
status.String(),
time.Now().UnixNano() / nanoSec,
})
}
func (f *events) step(res *stepResult) {
var errMsg string
if res.err != nil {
errMsg = res.err.Error()
}
f.event(&struct {
Event string `json:"event"`
RunID string `json:"run_id"`
Suite string `json:"suite"`
Location string `json:"location"`
Timestamp int64 `json:"timestamp"`
Status string `json:"status"`
Summary string `json:"summary,omitempty"`
}{
"TestStepFinished",
f.runID,
"main",
fmt.Sprintf("%s:%d", f.path, res.step.Location.Line),
time.Now().UnixNano() / nanoSec,
res.typ.String(),
errMsg,
})
// determine if test case has finished
var finished bool
var line int
switch t := f.owner.(type) {
case *gherkin.ScenarioOutline:
if t.Steps[len(t.Steps)-1].Location.Line == res.step.Location.Line {
finished = true
last := t.Examples[len(t.Examples)-1]
line = last.TableBody[len(last.TableBody)-1].Location.Line
}
case *gherkin.Scenario:
line = t.Location.Line
finished = t.Steps[len(t.Steps)-1].Location.Line == res.step.Location.Line
}
if finished {
f.event(&struct {
Event string `json:"event"`
RunID string `json:"run_id"`
Suite string `json:"suite"`
Location string `json:"location"`
Timestamp int64 `json:"timestamp"`
Status string `json:"status"`
}{
"TestCaseFinished",
f.runID,
"main",
fmt.Sprintf("%s:%d", f.path, line),
time.Now().UnixNano() / nanoSec,
f.stat.String(),
})
}
}
func (f *events) Defined(step *gherkin.Step, def *StepDef) {
if def != nil {
m := def.Expr.FindStringSubmatchIndex(step.Text)[2:]
var args [][2]int
for i := 0; i < len(m)/2; i++ {
pair := m[i : i*2+2]
var idxs [2]int
idxs[0] = pair[0]
idxs[1] = pair[1]
args = append(args, idxs)
}
if len(args) == 0 {
args = make([][2]int, 0)
}
f.event(&struct {
Event string `json:"event"`
RunID string `json:"run_id"`
Suite string `json:"suite"`
Location string `json:"location"`
DefID string `json:"definition_id"`
Args [][2]int `json:"arguments"`
}{
"StepDefinitionFound",
f.runID,
"main",
fmt.Sprintf("%s:%d", f.path, step.Location.Line),
def.definitionID(),
args,
})
}
f.event(&struct {
Event string `json:"event"`
RunID string `json:"run_id"`
Suite string `json:"suite"`
Location string `json:"location"`
Timestamp int64 `json:"timestamp"`
}{
"TestStepStarted",
f.runID,
"main",
fmt.Sprintf("%s:%d", f.path, step.Location.Line),
time.Now().UnixNano() / nanoSec,
})
}
func (f *events) Passed(step *gherkin.Step, match *StepDef) {
f.basefmt.Passed(step, match)
f.step(f.passed[len(f.passed)-1])
f.stat = passed
}
func (f *events) Skipped(step *gherkin.Step) {
f.basefmt.Skipped(step)
f.step(f.skipped[len(f.skipped)-1])
}
func (f *events) Undefined(step *gherkin.Step) {
f.basefmt.Undefined(step)
f.step(f.undefined[len(f.undefined)-1])
f.stat = undefined
}
func (f *events) Failed(step *gherkin.Step, match *StepDef, err error) {
f.basefmt.Failed(step, match, err)
f.step(f.failed[len(f.failed)-1])
f.stat = failed
}
func (f *events) Pending(step *gherkin.Step, match *StepDef) {
f.basefmt.Pending(step, match)
f.step(f.pending[len(f.pending)-1])
f.stat = pending
}