...

Source file src/github.com/joshdk/go-junit/ingest.go

Documentation: github.com/joshdk/go-junit

     1  // Copyright Josh Komoroske. All rights reserved.
     2  // Use of this source code is governed by the MIT license,
     3  // a copy of which can be found in the LICENSE.txt file.
     4  
     5  package junit
     6  
     7  import (
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  )
    12  
    13  // findSuites performs a depth-first search through the XML document, and
    14  // attempts to ingest any "testsuite" tags that are encountered.
    15  func findSuites(nodes []xmlNode, suites chan Suite) {
    16  	for _, node := range nodes {
    17  		switch node.XMLName.Local {
    18  		case "testsuite":
    19  			suites <- ingestSuite(node)
    20  		default:
    21  			findSuites(node.Nodes, suites)
    22  		}
    23  	}
    24  }
    25  
    26  func ingestSuite(root xmlNode) Suite {
    27  	suite := Suite{
    28  		Name:       root.Attr("name"),
    29  		Package:    root.Attr("package"),
    30  		Properties: root.Attrs,
    31  	}
    32  
    33  	for _, node := range root.Nodes {
    34  		switch node.XMLName.Local {
    35  		case "testsuite":
    36  			testsuite := ingestSuite(node)
    37  			suite.Suites = append(suite.Suites, testsuite)
    38  		case "testcase":
    39  			testcase := ingestTestcase(node)
    40  			suite.Tests = append(suite.Tests, testcase)
    41  		case "properties":
    42  			props := ingestProperties(node)
    43  			suite.Properties = props
    44  		case "system-out":
    45  			suite.SystemOut = string(node.Content)
    46  		case "system-err":
    47  			suite.SystemErr = string(node.Content)
    48  		}
    49  	}
    50  
    51  	suite.Aggregate()
    52  	return suite
    53  }
    54  
    55  func ingestProperties(root xmlNode) map[string]string {
    56  	props := make(map[string]string, len(root.Nodes))
    57  
    58  	for _, node := range root.Nodes {
    59  		if node.XMLName.Local == "property" {
    60  			name := node.Attr("name")
    61  			value := node.Attr("value")
    62  			props[name] = value
    63  		}
    64  	}
    65  
    66  	return props
    67  }
    68  
    69  func ingestTestcase(root xmlNode) Test {
    70  	test := Test{
    71  		Name:       root.Attr("name"),
    72  		Classname:  root.Attr("classname"),
    73  		Duration:   duration(root.Attr("time")),
    74  		Status:     StatusPassed,
    75  		Properties: root.Attrs,
    76  	}
    77  
    78  	for _, node := range root.Nodes {
    79  		switch node.XMLName.Local {
    80  		case "skipped":
    81  			test.Status = StatusSkipped
    82  			test.Message = node.Attr("message")
    83  		case "failure":
    84  			test.Status = StatusFailed
    85  			test.Message = node.Attr("message")
    86  			test.Error = ingestError(node)
    87  		case "error":
    88  			test.Status = StatusError
    89  			test.Message = node.Attr("message")
    90  			test.Error = ingestError(node)
    91  		case "system-out":
    92  			test.SystemOut = string(node.Content)
    93  		case "system-err":
    94  			test.SystemErr = string(node.Content)
    95  		}
    96  	}
    97  
    98  	return test
    99  }
   100  
   101  func ingestError(root xmlNode) Error {
   102  	return Error{
   103  		Body:    string(root.Content),
   104  		Type:    root.Attr("type"),
   105  		Message: root.Attr("message"),
   106  	}
   107  }
   108  
   109  func duration(t string) time.Duration {
   110  	// Remove commas for larger durations
   111  	t = strings.ReplaceAll(t, ",", "")
   112  
   113  	// Check if there was a valid decimal value
   114  	if s, err := strconv.ParseFloat(t, 64); err == nil {
   115  		return time.Duration(s*1000000) * time.Microsecond
   116  	}
   117  
   118  	// Check if there was a valid duration string
   119  	if d, err := time.ParseDuration(t); err == nil {
   120  		return d
   121  	}
   122  
   123  	return 0
   124  }
   125  

View as plain text