github.com/tiagovtristao/plz@v13.4.0+incompatible/src/test/go_results.go (about) 1 // Parser for output from Go's testing package. 2 // 3 // This is a fairly straightforward microformat so pretty easy to parse ourselves. 4 // There's at least one package out there to convert it to JUnit XML but not worth 5 // the complexity of getting that installed as a standalone tool. 6 7 package test 8 9 import ( 10 "bytes" 11 "fmt" 12 "regexp" 13 "strconv" 14 "strings" 15 "time" 16 17 "github.com/thought-machine/please/src/core" 18 ) 19 20 // Not sure what the -6 suffixes are about. 21 var testStart = regexp.MustCompile(`^=== RUN (.*)(?:-6)?$`) 22 var testResult = regexp.MustCompile(`^ *--- (PASS|FAIL|SKIP): (.*)(?:-6)? \(([0-9]+\.[0-9]+)s\)$`) 23 24 func parseGoTestResults(data []byte) (core.TestSuite, error) { 25 results := core.TestSuite{} 26 lines := bytes.Split(data, []byte{'\n'}) 27 testsStarted := map[string]bool{} 28 var suiteDuration time.Duration 29 testOutput := make([]string, 0) 30 for i, line := range lines { 31 testStartMatches := testStart.FindSubmatch(line) 32 testResultMatches := testResult.FindSubmatch(line) 33 if testStartMatches != nil { 34 testsStarted[strings.TrimSpace(string(testStartMatches[1]))] = true 35 } else if testResultMatches != nil { 36 testName := strings.TrimSpace(string(testResultMatches[2])) 37 if !testsStarted[testName] { 38 continue 39 } 40 f, _ := strconv.ParseFloat(string(testResultMatches[3]), 64) 41 duration := time.Duration(f * float64(time.Second)) 42 suiteDuration += duration 43 testCase := core.TestCase{ 44 Name: testName, 45 } 46 if bytes.Equal(testResultMatches[1], []byte("PASS")) { 47 testCase.Executions = append(testCase.Executions, core.TestExecution{ 48 Duration: &duration, 49 Stderr: strings.Join(testOutput, ""), 50 }) 51 } else if bytes.Equal(testResultMatches[1], []byte("SKIP")) { 52 i++ // Following line has the reason for being skipped 53 testCase.Executions = append(testCase.Executions, core.TestExecution{ 54 Skip: &core.TestResultSkip{ 55 Message: string(bytes.TrimSpace(lines[i])), 56 }, 57 Stderr: strings.Join(testOutput, ""), 58 Duration: &duration, 59 }) 60 } else { 61 output := "" 62 for j := i + 1; j < len(lines) && !bytes.HasPrefix(lines[j], []byte("===")); j++ { 63 output += string(lines[j]) + "\n" 64 } 65 testCase.Executions = append(testCase.Executions, core.TestExecution{ 66 Failure: &core.TestResultFailure{ 67 Traceback: output, 68 }, 69 Stderr: strings.Join(testOutput, ""), 70 Duration: &duration, 71 }) 72 } 73 results.TestCases = append(results.TestCases, testCase) 74 testOutput = make([]string, 0) 75 } else if bytes.Equal(line, []byte("PASS")) { 76 // Do nothing, all's well. 77 } else if bytes.Equal(line, []byte("FAIL")) { 78 if results.Failures() == 0 { 79 return results, fmt.Errorf("Test indicated final failure but no failures found yet") 80 } 81 } else { 82 testOutput = append(testOutput, string(line), "\n") 83 } 84 } 85 results.Duration = suiteDuration 86 return results, nil 87 }