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  }