mirror of https://github.com/gogs/gogs.git
179 lines
4.6 KiB
Go
179 lines
4.6 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/smartystreets/goconvey/web/server/contract"
|
|
)
|
|
|
|
var (
|
|
testNamePattern = regexp.MustCompile("^=== RUN:? +(.+)$")
|
|
)
|
|
|
|
func ParsePackageResults(result *contract.PackageResult, rawOutput string) {
|
|
newOutputParser(result, rawOutput).parse()
|
|
}
|
|
|
|
type outputParser struct {
|
|
raw string
|
|
lines []string
|
|
result *contract.PackageResult
|
|
tests []*contract.TestResult
|
|
|
|
// place holders for loops
|
|
line string
|
|
test *contract.TestResult
|
|
testMap map[string]*contract.TestResult
|
|
}
|
|
|
|
func newOutputParser(result *contract.PackageResult, rawOutput string) *outputParser {
|
|
self := new(outputParser)
|
|
self.raw = strings.TrimSpace(rawOutput)
|
|
self.lines = strings.Split(self.raw, "\n")
|
|
self.result = result
|
|
self.tests = []*contract.TestResult{}
|
|
self.testMap = make(map[string]*contract.TestResult)
|
|
return self
|
|
}
|
|
|
|
func (self *outputParser) parse() {
|
|
self.separateTestFunctionsAndMetadata()
|
|
self.parseEachTestFunction()
|
|
}
|
|
|
|
func (self *outputParser) separateTestFunctionsAndMetadata() {
|
|
for _, self.line = range self.lines {
|
|
if self.processNonTestOutput() {
|
|
break
|
|
}
|
|
self.processTestOutput()
|
|
}
|
|
}
|
|
func (self *outputParser) processNonTestOutput() bool {
|
|
if noGoFiles(self.line) {
|
|
self.recordFinalOutcome(contract.NoGoFiles)
|
|
|
|
} else if buildFailed(self.line) {
|
|
self.recordFinalOutcome(contract.BuildFailure)
|
|
|
|
} else if noTestFiles(self.line) {
|
|
self.recordFinalOutcome(contract.NoTestFiles)
|
|
|
|
} else if noTestFunctions(self.line) {
|
|
self.recordFinalOutcome(contract.NoTestFunctions)
|
|
|
|
} else {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (self *outputParser) recordFinalOutcome(outcome string) {
|
|
self.result.Outcome = outcome
|
|
self.result.BuildOutput = strings.Join(self.lines, "\n")
|
|
}
|
|
|
|
func (self *outputParser) processTestOutput() {
|
|
self.line = strings.TrimSpace(self.line)
|
|
if isNewTest(self.line) {
|
|
self.registerTestFunction()
|
|
|
|
} else if isTestResult(self.line) {
|
|
self.recordTestMetadata()
|
|
|
|
} else if isPackageReport(self.line) {
|
|
self.recordPackageMetadata()
|
|
|
|
} else {
|
|
self.saveLineForParsingLater()
|
|
|
|
}
|
|
}
|
|
|
|
func (self *outputParser) registerTestFunction() {
|
|
testNameReg := testNamePattern.FindStringSubmatch(self.line)
|
|
if len(testNameReg) < 2 { // Test-related lines that aren't about a new test
|
|
return
|
|
}
|
|
self.test = contract.NewTestResult(testNameReg[1])
|
|
self.tests = append(self.tests, self.test)
|
|
self.testMap[self.test.TestName] = self.test
|
|
}
|
|
func (self *outputParser) recordTestMetadata() {
|
|
testName := strings.Split(self.line, " ")[2]
|
|
if test, ok := self.testMap[testName]; ok {
|
|
self.test = test
|
|
self.test.Passed = !strings.HasPrefix(self.line, "--- FAIL: ")
|
|
self.test.Skipped = strings.HasPrefix(self.line, "--- SKIP: ")
|
|
self.test.Elapsed = parseTestFunctionDuration(self.line)
|
|
}
|
|
}
|
|
func (self *outputParser) recordPackageMetadata() {
|
|
if packageFailed(self.line) {
|
|
self.recordTestingOutcome(contract.Failed)
|
|
|
|
} else if packagePassed(self.line) {
|
|
self.recordTestingOutcome(contract.Passed)
|
|
|
|
} else if isCoverageSummary(self.line) {
|
|
self.recordCoverageSummary(self.line)
|
|
}
|
|
}
|
|
func (self *outputParser) recordTestingOutcome(outcome string) {
|
|
self.result.Outcome = outcome
|
|
fields := strings.Split(self.line, "\t")
|
|
self.result.PackageName = strings.TrimSpace(fields[1])
|
|
self.result.Elapsed = parseDurationInSeconds(fields[2], 3)
|
|
}
|
|
func (self *outputParser) recordCoverageSummary(summary string) {
|
|
start := len("coverage: ")
|
|
end := strings.Index(summary, "%")
|
|
value := summary[start:end]
|
|
parsed, err := strconv.ParseFloat(value, 64)
|
|
if err != nil {
|
|
self.result.Coverage = -1
|
|
} else {
|
|
self.result.Coverage = parsed
|
|
}
|
|
}
|
|
func (self *outputParser) saveLineForParsingLater() {
|
|
self.line = strings.TrimLeft(self.line, "\t")
|
|
if self.test == nil {
|
|
fmt.Println("Potential error parsing output of", self.result.PackageName, "; couldn't handle this stray line:", self.line)
|
|
return
|
|
}
|
|
self.test.RawLines = append(self.test.RawLines, self.line)
|
|
}
|
|
|
|
// TestResults is a collection of TestResults that implements sort.Interface.
|
|
type TestResults []contract.TestResult
|
|
|
|
func (r TestResults) Len() int {
|
|
return len(r)
|
|
}
|
|
|
|
// Less compares TestResults on TestName
|
|
func (r TestResults) Less(i, j int) bool {
|
|
return r[i].TestName < r[j].TestName
|
|
}
|
|
|
|
func (r TestResults) Swap(i, j int) {
|
|
r[i], r[j] = r[j], r[i]
|
|
}
|
|
|
|
func (self *outputParser) parseEachTestFunction() {
|
|
for _, self.test = range self.tests {
|
|
self.test = parseTestOutput(self.test)
|
|
if self.test.Error != "" {
|
|
self.result.Outcome = contract.Panicked
|
|
}
|
|
self.test.RawLines = []string{}
|
|
self.result.TestResults = append(self.result.TestResults, *self.test)
|
|
}
|
|
sort.Sort(TestResults(self.result.TestResults))
|
|
}
|