github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/plugin/builtin/attach/xunit/parser.go (about) 1 package xunit 2 3 import ( 4 "encoding/xml" 5 "fmt" 6 "io" 7 "strings" 8 "time" 9 10 "github.com/evergreen-ci/evergreen" 11 "github.com/evergreen-ci/evergreen/model" 12 "github.com/evergreen-ci/evergreen/model/task" 13 "github.com/evergreen-ci/evergreen/util" 14 ) 15 16 type XUnitResults []TestSuite 17 18 type TestSuite struct { 19 Errors int `xml:"errors,attr"` 20 Failures int `xml:"failures,attr"` 21 Skip int `xml:"skip,attr"` 22 Name string `xml:"name,attr"` 23 TestCases []TestCase `xml:"testcase"` 24 SysOut string `xml:"system-out"` 25 SysErr string `xml:"system-err"` 26 } 27 28 type TestCase struct { 29 Name string `xml:"name,attr"` 30 Time float64 `xml:"time,attr"` 31 ClassName string `xml:"classname,attr"` 32 Failure *FailureDetails `xml:"failure"` 33 Error *FailureDetails `xml:"error"` 34 Skipped *FailureDetails `xml:"skipped"` 35 } 36 37 type FailureDetails struct { 38 Message string `xml:"message,attr"` 39 Type string `xml:"type,attr"` 40 Content string `xml:",chardata"` 41 } 42 43 func ParseXMLResults(reader io.Reader) (XUnitResults, error) { 44 results := XUnitResults{} 45 if err := xml.NewDecoder(reader).Decode(&results); err != nil { 46 return nil, err 47 } 48 49 return results, nil 50 } 51 52 // ToModelTestResultAndLog converts an xunit test case into an 53 // mci task.TestResult and model.TestLog. Logs are only 54 // generated if the test case did not succeed (this is part of 55 // the xunit xml file design) 56 func (tc TestCase) ToModelTestResultAndLog(t *task.Task) (task.TestResult, *model.TestLog) { 57 58 res := task.TestResult{} 59 var log *model.TestLog 60 61 if tc.ClassName != "" { 62 res.TestFile = fmt.Sprintf("%v.%v", tc.ClassName, tc.Name) 63 } else { 64 res.TestFile = tc.Name 65 } 66 // replace spaces, dashes, etc. with underscores 67 res.TestFile = util.CleanForPath(res.TestFile) 68 69 res.StartTime = float64(time.Now().Unix()) 70 res.EndTime = res.StartTime + tc.Time 71 72 // the presence of the Failure, Error, or Skipped fields 73 // is used to indicate an unsuccessful test case. Logs 74 // can only be generated in failure cases, because xunit 75 // results only include messages if they did *not* succeed. 76 switch { 77 case tc.Failure != nil: 78 res.Status = evergreen.TestFailedStatus 79 log = tc.Failure.toBasicTestLog("FAILURE") 80 case tc.Error != nil: 81 res.Status = evergreen.TestFailedStatus 82 log = tc.Error.toBasicTestLog("ERROR") 83 case tc.Skipped != nil: 84 res.Status = evergreen.TestSkippedStatus 85 log = tc.Skipped.toBasicTestLog("SKIPPED") 86 default: 87 res.Status = evergreen.TestSucceededStatus 88 } 89 90 if log != nil { 91 log.Name = res.TestFile 92 log.Task = t.Id 93 log.TaskExecution = t.Execution 94 95 // update the URL of the result to the expected log URL 96 res.URL = log.URL() 97 } 98 99 return res, log 100 } 101 102 func (fd FailureDetails) toBasicTestLog(fdType string) *model.TestLog { 103 log := model.TestLog{ 104 Lines: []string{fmt.Sprintf("%v: %v (%v)", fdType, fd.Message, fd.Type)}, 105 } 106 logLines := strings.Split(strings.TrimSpace(fd.Content), "\n") 107 log.Lines = append(log.Lines, logLines...) 108 return &log 109 }