blob: 884672690bbb83bd96ca2fdf62bf98b396279c61 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package testparser
import (
"fmt"
"regexp"
"strings"
"go.fuchsia.dev/fuchsia/tools/testing/runtests"
)
var (
rustTestPreamblePattern = regexp.MustCompile(`^running \d* tests?$`)
// ex: "test channel::tests::channel_call_etc_timeout ... ok"
// test_suite = "channel"
// test_case = "tests:channel_call_etc_timeout"
// status = "ok"
// ex: "test version::tests::get_version_string ... FAILED"
// test_suite = "version"
// test_case = "tests:get_version_string"
// status = "FAILED"
rustTestCasePattern = regexp.MustCompile(`^test (?:(?P<test_suite>.*?)::)?(?P<test_case>.*?) \.\.\. (?P<status>\w*)$`)
// ex: "---- version::tests::get_version_string stderr ----"
rustTestCaseStderrStart = regexp.MustCompile(`^-{4} (?P<test_name>.*?) stderr -{4}$`)
// If we see any of these keyword(s) in stdout line, we either want to stop capturing for
// test stderr, or think it's the last of the stderr section.
// "stack backtrace" - we don't want to capture the stacktrace it would make fail clustering hard
// "failures:" - this seem to be the last section in rusttest that reports a list of test fails
rustTestCaseStderrEnd = regexp.MustCompile(`^(stack backtrace|failures):$`)
)
func parseRustTest(lines [][]byte) []runtests.TestCaseResult {
var res []runtests.TestCaseResult
testCases := make(map[string]runtests.TestCaseResult)
errorMessages := make(map[string]*strings.Builder)
currentTestName := ""
for _, line := range lines {
line := string(line)
// This is just spam, we should stop printing this line.
// TODO(yuanzhi) Remove once we've stopped logging this line.
if strings.HasPrefix(line, "[stdout - legacy_test]") {
continue
}
if m := rustTestCasePattern.FindStringSubmatch(line); m != nil {
tc := createRustTestCase(m[1], m[2], m[3])
testCases[tc.DisplayName] = tc
continue
}
// Note: we should prioritize matching the start of the stderr than the end.
// Because matching the end is more fragile than the start.
if m := rustTestCaseStderrStart.FindStringSubmatch(line); m != nil {
currentTestName = m[1]
errorMessages[currentTestName] = &strings.Builder{}
continue
}
if m := rustTestCaseStderrEnd.MatchString(line); m {
currentTestName = ""
continue
}
if currentTestName != "" {
errorMessages[currentTestName].WriteString(line + "\n")
continue
}
}
for testName, testCase := range testCases {
if msg, ok := errorMessages[testName]; ok {
if testCase.Status == runtests.TestFailure {
testCase.FailReason = strings.TrimSuffix(msg.String(), "\n")
}
}
res = append(res, testCase)
}
return res
}
func createRustTestCase(suiteName, caseName, result string) runtests.TestCaseResult {
displayName := caseName
if suiteName != "" {
displayName = fmt.Sprintf("%s::%s", suiteName, caseName)
}
var status runtests.TestResult
switch result {
case "ok":
status = runtests.TestSuccess
case "FAILED":
status = runtests.TestFailure
case "ignored":
status = runtests.TestSkipped
}
return runtests.TestCaseResult{
DisplayName: displayName,
SuiteName: suiteName,
CaseName: caseName,
Status: status,
Format: "Rust",
}
}