208 строки
5 КиБ
Go
208 строки
5 КиБ
Go
package godog
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/DATA-DOG/godog/gherkin"
|
|
)
|
|
|
|
func init() {
|
|
Format("junit", "Prints junit compatible xml to stdout", junitFunc)
|
|
}
|
|
|
|
func junitFunc(suite string, out io.Writer) Formatter {
|
|
return &junitFormatter{
|
|
suite: &junitPackageSuite{
|
|
Name: suite,
|
|
TestSuites: make([]*junitTestSuite, 0),
|
|
},
|
|
out: out,
|
|
started: timeNowFunc(),
|
|
}
|
|
}
|
|
|
|
type junitFormatter struct {
|
|
suite *junitPackageSuite
|
|
out io.Writer
|
|
|
|
// timing
|
|
started time.Time
|
|
caseStarted time.Time
|
|
featStarted time.Time
|
|
|
|
outline *gherkin.ScenarioOutline
|
|
outlineExample int
|
|
}
|
|
|
|
func (j *junitFormatter) Feature(feature *gherkin.Feature, path string, c []byte) {
|
|
testSuite := &junitTestSuite{
|
|
TestCases: make([]*junitTestCase, 0),
|
|
Name: feature.Name,
|
|
}
|
|
|
|
if len(j.suite.TestSuites) > 0 {
|
|
j.current().Time = timeNowFunc().Sub(j.featStarted).String()
|
|
}
|
|
j.featStarted = timeNowFunc()
|
|
j.suite.TestSuites = append(j.suite.TestSuites, testSuite)
|
|
}
|
|
|
|
func (j *junitFormatter) Defined(*gherkin.Step, *StepDef) {
|
|
|
|
}
|
|
|
|
func (j *junitFormatter) Node(node interface{}) {
|
|
suite := j.current()
|
|
tcase := &junitTestCase{}
|
|
|
|
switch t := node.(type) {
|
|
case *gherkin.ScenarioOutline:
|
|
j.outline = t
|
|
j.outlineExample = 0
|
|
return
|
|
case *gherkin.Scenario:
|
|
tcase.Name = t.Name
|
|
suite.Tests++
|
|
j.suite.Tests++
|
|
case *gherkin.TableRow:
|
|
j.outlineExample++
|
|
tcase.Name = fmt.Sprintf("%s #%d", j.outline.Name, j.outlineExample)
|
|
suite.Tests++
|
|
j.suite.Tests++
|
|
default:
|
|
return
|
|
}
|
|
j.caseStarted = timeNowFunc()
|
|
suite.TestCases = append(suite.TestCases, tcase)
|
|
}
|
|
|
|
func (j *junitFormatter) Failed(step *gherkin.Step, match *StepDef, err error) {
|
|
suite := j.current()
|
|
suite.Failures++
|
|
j.suite.Failures++
|
|
|
|
tcase := suite.current()
|
|
tcase.Time = timeNowFunc().Sub(j.caseStarted).String()
|
|
tcase.Status = "failed"
|
|
tcase.Failure = &junitFailure{
|
|
Message: fmt.Sprintf("%s %s: %s", step.Type, step.Text, err.Error()),
|
|
}
|
|
}
|
|
|
|
func (j *junitFormatter) Passed(step *gherkin.Step, match *StepDef) {
|
|
suite := j.current()
|
|
|
|
tcase := suite.current()
|
|
tcase.Time = timeNowFunc().Sub(j.caseStarted).String()
|
|
tcase.Status = "passed"
|
|
}
|
|
|
|
func (j *junitFormatter) Skipped(step *gherkin.Step) {
|
|
suite := j.current()
|
|
|
|
tcase := suite.current()
|
|
tcase.Time = timeNowFunc().Sub(j.caseStarted).String()
|
|
tcase.Error = append(tcase.Error, &junitError{
|
|
Type: "skipped",
|
|
Message: fmt.Sprintf("%s %s", step.Type, step.Text),
|
|
})
|
|
}
|
|
|
|
func (j *junitFormatter) Undefined(step *gherkin.Step) {
|
|
suite := j.current()
|
|
tcase := suite.current()
|
|
if tcase.Status != "undefined" {
|
|
// do not count two undefined steps as another error
|
|
suite.Errors++
|
|
j.suite.Errors++
|
|
}
|
|
|
|
tcase.Time = timeNowFunc().Sub(j.caseStarted).String()
|
|
tcase.Status = "undefined"
|
|
tcase.Error = append(tcase.Error, &junitError{
|
|
Type: "undefined",
|
|
Message: fmt.Sprintf("%s %s", step.Type, step.Text),
|
|
})
|
|
}
|
|
|
|
func (j *junitFormatter) Pending(step *gherkin.Step, match *StepDef) {
|
|
suite := j.current()
|
|
suite.Errors++
|
|
j.suite.Errors++
|
|
|
|
tcase := suite.current()
|
|
tcase.Time = timeNowFunc().Sub(j.caseStarted).String()
|
|
tcase.Status = "pending"
|
|
tcase.Error = append(tcase.Error, &junitError{
|
|
Type: "pending",
|
|
Message: fmt.Sprintf("%s %s: TODO: write pending definition", step.Type, step.Text),
|
|
})
|
|
}
|
|
|
|
func (j *junitFormatter) Summary() {
|
|
if j.current() != nil {
|
|
j.current().Time = timeNowFunc().Sub(j.featStarted).String()
|
|
}
|
|
j.suite.Time = timeNowFunc().Sub(j.started).String()
|
|
io.WriteString(j.out, xml.Header)
|
|
|
|
enc := xml.NewEncoder(j.out)
|
|
enc.Indent("", s(2))
|
|
if err := enc.Encode(j.suite); err != nil {
|
|
fmt.Fprintln(os.Stderr, "failed to write junit xml:", err)
|
|
}
|
|
}
|
|
|
|
type junitFailure struct {
|
|
Message string `xml:"message,attr"`
|
|
Type string `xml:"type,attr,omitempty"`
|
|
}
|
|
|
|
type junitError struct {
|
|
XMLName xml.Name `xml:"error,omitempty"`
|
|
Message string `xml:"message,attr"`
|
|
Type string `xml:"type,attr"`
|
|
}
|
|
|
|
type junitTestCase struct {
|
|
XMLName xml.Name `xml:"testcase"`
|
|
Name string `xml:"name,attr"`
|
|
Status string `xml:"status,attr"`
|
|
Time string `xml:"time,attr"`
|
|
Failure *junitFailure `xml:"failure,omitempty"`
|
|
Error []*junitError
|
|
}
|
|
|
|
type junitTestSuite struct {
|
|
XMLName xml.Name `xml:"testsuite"`
|
|
Name string `xml:"name,attr"`
|
|
Tests int `xml:"tests,attr"`
|
|
Skipped int `xml:"skipped,attr"`
|
|
Failures int `xml:"failures,attr"`
|
|
Errors int `xml:"errors,attr"`
|
|
Time string `xml:"time,attr"`
|
|
TestCases []*junitTestCase
|
|
}
|
|
|
|
func (ts *junitTestSuite) current() *junitTestCase {
|
|
return ts.TestCases[len(ts.TestCases)-1]
|
|
}
|
|
|
|
type junitPackageSuite struct {
|
|
XMLName xml.Name `xml:"testsuites"`
|
|
Name string `xml:"name,attr"`
|
|
Tests int `xml:"tests,attr"`
|
|
Skipped int `xml:"skipped,attr"`
|
|
Failures int `xml:"failures,attr"`
|
|
Errors int `xml:"errors,attr"`
|
|
Time string `xml:"time,attr"`
|
|
TestSuites []*junitTestSuite
|
|
}
|
|
|
|
func (j *junitFormatter) current() *junitTestSuite {
|
|
return j.suite.TestSuites[len(j.suite.TestSuites)-1]
|
|
}
|