| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 | 
							- package parser
 
- import (
 
- 	"encoding/json"
 
- 	"fmt"
 
- 	"strconv"
 
- 	"strings"
 
- 	"github.com/smartystreets/goconvey/convey/reporting"
 
- 	"github.com/smartystreets/goconvey/web/server/contract"
 
- )
 
- type testParser struct {
 
- 	test       *contract.TestResult
 
- 	line       string
 
- 	index      int
 
- 	inJson     bool
 
- 	jsonLines  []string
 
- 	otherLines []string
 
- }
 
- func parseTestOutput(test *contract.TestResult) *contract.TestResult {
 
- 	parser := newTestParser(test)
 
- 	parser.parseTestFunctionOutput()
 
- 	return test
 
- }
 
- func newTestParser(test *contract.TestResult) *testParser {
 
- 	self := new(testParser)
 
- 	self.test = test
 
- 	return self
 
- }
 
- func (self *testParser) parseTestFunctionOutput() {
 
- 	if len(self.test.RawLines) > 0 {
 
- 		self.processLines()
 
- 		self.deserializeJson()
 
- 		self.composeCapturedOutput()
 
- 	}
 
- }
 
- func (self *testParser) processLines() {
 
- 	for self.index, self.line = range self.test.RawLines {
 
- 		if !self.processLine() {
 
- 			break
 
- 		}
 
- 	}
 
- }
 
- func (self *testParser) processLine() bool {
 
- 	if strings.HasSuffix(self.line, reporting.OpenJson) {
 
- 		self.inJson = true
 
- 		self.accountForOutputWithoutNewline()
 
- 	} else if self.line == reporting.CloseJson {
 
- 		self.inJson = false
 
- 	} else if self.inJson {
 
- 		self.jsonLines = append(self.jsonLines, self.line)
 
- 	} else if isPanic(self.line) {
 
- 		self.parsePanicOutput()
 
- 		return false
 
- 	} else if isGoTestLogOutput(self.line) {
 
- 		self.parseLogLocation()
 
- 	} else {
 
- 		self.otherLines = append(self.otherLines, self.line)
 
- 	}
 
- 	return true
 
- }
 
- // If fmt.Print(f) produces output with no \n and that output
 
- // is that last output before the framework spits out json
 
- // (which starts with ''>>>>>'') then without this code
 
- // all of the json is counted as output, not as json to be
 
- // parsed and displayed by the web UI.
 
- func (self *testParser) accountForOutputWithoutNewline() {
 
- 	prefix := strings.Split(self.line, reporting.OpenJson)[0]
 
- 	if prefix != "" {
 
- 		self.otherLines = append(self.otherLines, prefix)
 
- 	}
 
- }
 
- func (self *testParser) deserializeJson() {
 
- 	formatted := createArrayForJsonItems(self.jsonLines)
 
- 	var scopes []reporting.ScopeResult
 
- 	err := json.Unmarshal(formatted, &scopes)
 
- 	if err != nil {
 
- 		panic(fmt.Sprintf(bugReportRequest, err, formatted))
 
- 	}
 
- 	self.test.Stories = scopes
 
- }
 
- func (self *testParser) parsePanicOutput() {
 
- 	for index, line := range self.test.RawLines[self.index:] {
 
- 		self.parsePanicLocation(index, line)
 
- 		self.preserveStackTraceIndentation(index, line)
 
- 	}
 
- 	self.test.Error = strings.Join(self.test.RawLines, "\n")
 
- }
 
- func (self *testParser) parsePanicLocation(index int, line string) {
 
- 	if !panicLineHasMetadata(line) {
 
- 		return
 
- 	}
 
- 	metaLine := self.test.RawLines[index+4]
 
- 	fields := strings.Split(metaLine, " ")
 
- 	fileAndLine := strings.Split(fields[0], ":")
 
- 	self.test.File = fileAndLine[0]
 
- 	if len(fileAndLine) >= 2 {
 
- 		self.test.Line, _ = strconv.Atoi(fileAndLine[1])
 
- 	}
 
- }
 
- func (self *testParser) preserveStackTraceIndentation(index int, line string) {
 
- 	if panicLineShouldBeIndented(index, line) {
 
- 		self.test.RawLines[index] = "\t" + line
 
- 	}
 
- }
 
- func (self *testParser) parseLogLocation() {
 
- 	self.otherLines = append(self.otherLines, self.line)
 
- 	lineFields := strings.TrimSpace(self.line)
 
- 	if strings.HasPrefix(lineFields, "Error Trace:") {
 
- 		lineFields = strings.TrimPrefix(lineFields, "Error Trace:")
 
- 	}
 
- 	fields := strings.Split(lineFields, ":")
 
- 	self.test.File = strings.TrimSpace(fields[0])
 
- 	self.test.Line, _ = strconv.Atoi(fields[1])
 
- }
 
- func (self *testParser) composeCapturedOutput() {
 
- 	self.test.Message = strings.Join(self.otherLines, "\n")
 
- }
 
- func createArrayForJsonItems(lines []string) []byte {
 
- 	jsonArrayItems := strings.Join(lines, "")
 
- 	jsonArrayItems = removeTrailingComma(jsonArrayItems)
 
- 	return []byte(fmt.Sprintf("[%s]\n", jsonArrayItems))
 
- }
 
- func removeTrailingComma(rawJson string) string {
 
- 	if trailingComma(rawJson) {
 
- 		return rawJson[:len(rawJson)-1]
 
- 	}
 
- 	return rawJson
 
- }
 
- func trailingComma(value string) bool {
 
- 	return strings.HasSuffix(value, ",")
 
- }
 
- func isGoTestLogOutput(line string) bool {
 
- 	return strings.Count(line, ":") == 2
 
- }
 
- func isPanic(line string) bool {
 
- 	return strings.HasPrefix(line, "panic: ")
 
- }
 
- func panicLineHasMetadata(line string) bool {
 
- 	return strings.HasPrefix(line, "goroutine") && strings.Contains(line, "[running]")
 
- }
 
- func panicLineShouldBeIndented(index int, line string) bool {
 
- 	return strings.Contains(line, "+") || (index > 0 && strings.Contains(line, "panic: "))
 
- }
 
- const bugReportRequest = `
 
- Uh-oh! Looks like something went wrong. Please copy the following text and file a bug report at: 
 
- https://github.com/smartystreets/goconvey/issues?state=open
 
- ======= BEGIN BUG REPORT =======
 
- ERROR: %v
 
- OUTPUT: %s
 
- ======= END BUG REPORT =======
 
- `
 
 
  |