Commits

Miki Tebeka committed c435fa8 Merge

merge of dev

Comments (0)

Files changed (3)

 	}
 }
 
-func loadTests(filename string, t *testing.T) []*Suite {
+func loadTests(filename string, t *testing.T) ([]*Suite, error) {
 	file, err := os.Open(filename)
 	if err != nil {
 		t.Fatalf("can't open %s - %s", filename, err)
 	}
 
-	suites, err := gt_Parse(file)
-	if err != nil {
-		t.Fatalf("error parsing - %s", err)
-	}
-
-	return suites
+	return gt_Parse(file)
 }
 
 func Test_parseOutput(t *testing.T) {
-	suites := loadTests("data/gotest.out", t)
+	filename := "data/gotest.out"
+	suites, err := loadTests(filename, t)
+	if err != nil {
+		t.Fatalf("error loading %s - %s", filename, err)
+	}
+
 	if len(suites) != 2 {
 		t.Fatalf("got %d suites instead of 2", len(suites))
 	}
 }
 
 func Test_parseOutputBad(t *testing.T) {
-	suites := loadTests("go2xunit.go", t)
-	if len(suites) != 0 {
+	_, err := loadTests("go2xunit.go", t)
+	if err == nil {
 		t.Fatalf("managed to find suites in junk")
 	}
 }
 			suiteName = tokens[1]
 			test = &Test{Name: tokens[2]}
 			out = []string{}
-		} else {
-			tokens := find_end(line)
-			if len(tokens) > 0 {
-				if test == nil {
-					return nil, fmt.Errorf("%d: orphan end", lnum)
-				}
-				if (tokens[2] != suiteName) || (tokens[3] != test.Name) {
-					return nil, fmt.Errorf("%d: suite/name mismatch", lnum)
-				}
-				test.Message = strings.Join(out, "\n")
-				test.Time = tokens[4]
-				test.Failed = (tokens[1] == "FAIL")
+			continue
+		}
 
-				suite, ok := suites[suiteName]
-				if !ok {
-					suite = &Suite{Name:suiteName}
-				}
-				suite.Tests = append(suite.Tests, test)
-				suites[suiteName] = suite
+		tokens = find_end(line)
+		if len(tokens) > 0 {
+			if test == nil {
+				return nil, fmt.Errorf("%d: orphan end", lnum)
+			}
+			if (tokens[2] != suiteName) || (tokens[3] != test.Name) {
+				return nil, fmt.Errorf("%d: suite/name mismatch", lnum)
+			}
+			test.Message = strings.Join(out, "\n")
+			test.Time = tokens[4]
+			test.Failed = (tokens[1] == "FAIL")
 
-				test = nil
-				suiteName = ""
-				out = []string{}
+			suite, ok := suites[suiteName]
+			if !ok {
+				suite = &Suite{Name:suiteName}
+			}
+			suite.Tests = append(suite.Tests, test)
+			suites[suiteName] = suite
 
-			} else { // No start or end
-				if test != nil {
-					out = append(out, line)
-				}
-			}
+			test = nil
+			suiteName = ""
+			out = []string{}
+
+			continue
+		}
+
+		if test != nil {
+			out = append(out, line)
 		}
 	}
 
 	"strings"
 )
 
-// Since mucking with local package is a PITA, just prefix everything with gt_
+const (
+	// === RUN TestAdd
+	gt_startRE = "^=== RUN ([a-zA-Z_][[:word:]]*)"
 
-// parseEnd parses "end of test" line and returns (name, time, error)
-func gt_parseEnd(prefix, line string) (string, string, error) {
-	// "end of test" regexp for name and time, examples:
 	// --- PASS: TestSub (0.00 seconds)
 	// --- FAIL: TestSubFail (0.00 seconds)
-	var endRegexp *regexp.Regexp = regexp.MustCompile(`([^ ]+) \((\d+\.\d+)`)
+	gt_endRE = "^--- (PASS|FAIL): ([a-zA-Z_][[:word:]]*) \\((\\d+.\\d+)"
 
-	matches := endRegexp.FindStringSubmatch(line[len(prefix):])
+	// FAIL	_/home/miki/Projects/goroot/src/xunit	0.004s
+	// ok  	_/home/miki/Projects/goroot/src/anotherTest	0.000s
+	gt_suiteRE = "^(ok|FAIL)[ \t]+([^ \t]+)[ \t]+(\\d+.\\d+)"
+)
 
-	if len(matches) == 0 {
-		return "", "", fmt.Errorf("can't parse %s", line)
+func gt_Parse(rd io.Reader) ([]*Suite, error) {
+	find_start := regexp.MustCompile(gt_startRE).FindStringSubmatch
+	find_end := regexp.MustCompile(gt_endRE).FindStringSubmatch
+	find_suite := regexp.MustCompile(gt_suiteRE).FindStringSubmatch
+	is_exit := regexp.MustCompile("^exit status -?\\d+").MatchString
+
+	suites := []*Suite{}
+	var curTest *Test
+	var curSuite *Suite
+	var out []string
+
+	scanner := bufio.NewScanner(rd)
+	for lnum := 1; scanner.Scan(); lnum++ {
+		line := scanner.Text()
+
+		tokens := find_start(line)
+		if tokens != nil {
+			if curTest != nil {
+				return nil, fmt.Errorf("%d: test in middle of other", lnum)
+			}
+			curTest = &Test{
+				Name: tokens[1],
+			}
+			if len(out) > 0 {
+				message := strings.Join(out, "\n")
+				if (curSuite == nil) {
+					return nil, fmt.Errorf("orphan output: %s", message)
+				}
+				curSuite.Tests[len(curSuite.Tests)-1].Message = message
+			}
+			out = []string{}
+			continue
+		}
+
+		tokens = find_end(line)
+		if tokens != nil {
+			if curTest == nil {
+				return nil, fmt.Errorf("%d: orphan end test", lnum)
+			}
+			if tokens[2] != curTest.Name {
+				return nil, fmt.Errorf("%d: name mismatch", lnum)
+			}
+
+			curTest.Failed = (tokens[1] == "FAIL")
+			curTest.Time = tokens[3]
+			curTest.Message = strings.Join(out, "\n")
+			if curSuite == nil {
+				curSuite = &Suite{}
+			}
+			curSuite.Tests = append(curSuite.Tests, curTest)
+			curTest = nil
+			continue
+		}
+
+		tokens = find_suite(line)
+		if tokens != nil {
+			if curSuite == nil {
+				return nil, fmt.Errorf("%d: orphan end suite", lnum)
+			}
+			curSuite.Name = tokens[2]
+			curSuite.Time = tokens[3]
+			suites = append(suites, curSuite)
+			curSuite = nil
+
+			continue
+		}
+
+		if is_exit(line) || (line == "FAIL") {
+			continue
+		}
+
+		if curSuite == nil {
+			return nil, fmt.Errorf("%d: orphan line", lnum)
+		}
+
+		out = append(out, line)
 	}
 
-	return matches[1], matches[2], nil
-}
-
-
-// gt_parseEndTest parses "end of test file" line and returns (status, name, time, error)
-func gt_parseEndTest(line string) (string, string, string, error) {
-	// "end of tested file" regexp for parsing package & file name
-	// ok  	teky/cointreau/gs1/deliver	0.015s
-	// FAIL	teky/cointreau/gs1/deliver	0.010s
-	var endTestRegexp *regexp.Regexp = regexp.MustCompile(`^(ok  |FAIL)\t([^ ]+)\t(\d+\.\d+)s$`)
-
-	matches := endTestRegexp.FindStringSubmatch(line)
-
-	if len(matches) == 0 {
-		return "", "", "", fmt.Errorf("can't parse %s", line)
+	if err := scanner.Err(); err != nil {
+		return nil, err
 	}
 
-	return matches[1], matches[2], matches[3], nil
+	return suites, nil
 }
-
-// gt_Parse parses output of "go test -v", returns a list of tests
-// See data/gotest.out for an example
-func gt_Parse (rd io.Reader) ([]*Suite, error) {
-
-	startPrefix := "=== RUN "
-	passPrefix := "--- PASS: "
-	failPrefix := "--- FAIL: "
-
-	suites := []*Suite{}
-	var test *Test = nil
-	var suite *Suite = nil
-
-	nextTest := func() {
-		// We are switching to the next test, store the current one.
-		if suite == nil {
-			suite = &Suite{}
-			suite.Tests = make([]*Test, 0, 1)
-		}
-		if test == nil {
-			return
-		}
-		suite.Tests = append(suite.Tests, test)
-		test = nil
-	}
-	nextSuite := func() {
-		// We are switching to the next suite, store the current one.
-		if suite == nil {
-			return
-		}
-
-		suites = append(suites, suite)
-		suite = nil
-	}
-
-	reader := bufio.NewReader(rd)
-	for {
-		buf, _, err := reader.ReadLine()
-
-		switch err {
-		case io.EOF:
-			if suite != nil || test != nil {
-				// if suite or test in progress EOF is an unexpected EOF
-				return nil, fmt.Errorf("Unexpected EOF")
-			}
-			return suites, nil
-		case nil:
-			// nil is OK
-
-		default: // Error other than io.EOF
-			return nil, err
-		}
-
-		line := string(buf)
-		switch {
-		case strings.HasPrefix(line, startPrefix):
-		case strings.HasPrefix(line, failPrefix):
-			nextTest()
-
-			// Extract the test name and the duration:
-			name, time, err := gt_parseEnd(failPrefix, line)
-			if err != nil {
-				return nil, err
-			}
-
-			test = &Test{
-				Name:   name,
-				Time:   time,
-				Failed: true,
-			}
-
-		case strings.HasPrefix(line, passPrefix):
-			nextTest()
-			// Extract the test name and the duration:
-			name, time, err := gt_parseEnd(passPrefix, line)
-			if err != nil {
-				return nil, err
-			}
-			// Create the test structure and store it.
-			suite.Tests = append(suite.Tests, &Test{
-				Name:   name,
-				Time:   time,
-				Failed: false,
-			})
-			test = nil
-		case line == "FAIL":
-			nextTest()
-
-		case strings.HasPrefix(line, "ok  \t") || strings.HasPrefix(line, "FAIL\t"):
-			// End of suite, read data
-			status, name, time, err := gt_parseEndTest(line)
-			if err != nil {
-				return nil, err
-			}
-			suite.Name = name
-			suite.Time = time
-			suite.Status = status
-			nextSuite()
-		default:
-			if test != nil { // test != nil marks we're in the middle of a test
-				test.Message += line + "\n"
-			}
-		}
-	}
-
-	// If we're here, it's an error
-	return nil, fmt.Errorf("Error parsing")
-}