123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- 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))
- }
|