From 27ad87e370c4a44a41afb2eee08f09716b6c11ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Stemmer?= Date: Thu, 11 Aug 2022 23:29:28 +0100 Subject: [PATCH] parser/gotest: parseLine now returns the events it creates --- parser/gotest/gotest.go | 108 +++++++++++++++++----------------- parser/gotest/gotest_test.go | 110 ++++++++++++++++------------------- 2 files changed, 105 insertions(+), 113 deletions(-) diff --git a/parser/gotest/gotest.go b/parser/gotest/gotest.go index b79daeb..ad0f5e0 100644 --- a/parser/gotest/gotest.go +++ b/parser/gotest/gotest.go @@ -143,6 +143,8 @@ func (p *Parser) parse(r *reader.LimitedLineReader) (gtr.Report, error) { return gtr.Report{}, err } + var evs []Event + // Lines that exceed bufio.MaxScanTokenSize are not expected to contain // any relevant test infrastructure output, so instead of parsing them // we treat them as regular output to increase performance. @@ -152,9 +154,13 @@ func (p *Parser) parse(r *reader.LimitedLineReader) (gtr.Report, error) { // turned out to be fine in almost all cases, it seemed an appropriate // value to use to decide whether or not to attempt parsing this line. if len(line) > bufio.MaxScanTokenSize { - p.output(line) + evs = p.output(line) } else { - p.parseLine(line) + evs = p.parseLine(line) + } + + for _, ev := range evs { + p.events = append(p.events, ev) } } return p.report(p.events), nil @@ -181,76 +187,70 @@ func (p *Parser) Events() []Event { return events } -func (p *Parser) parseLine(line string) { +func (p *Parser) parseLine(line string) (events []Event) { if strings.HasPrefix(line, "=== RUN ") { - p.runTest(strings.TrimSpace(line[8:])) + return p.runTest(strings.TrimSpace(line[8:])) } else if strings.HasPrefix(line, "=== PAUSE ") { - p.pauseTest(strings.TrimSpace(line[10:])) + return p.pauseTest(strings.TrimSpace(line[10:])) } else if strings.HasPrefix(line, "=== CONT ") { - p.contTest(strings.TrimSpace(line[9:])) + return p.contTest(strings.TrimSpace(line[9:])) } else if matches := regexEndTest.FindStringSubmatch(line); len(matches) == 5 { - p.endTest(line, matches[1], matches[2], matches[3], matches[4]) + return p.endTest(line, matches[1], matches[2], matches[3], matches[4]) } else if matches := regexStatus.FindStringSubmatch(line); len(matches) == 2 { - p.status(matches[1]) + return p.status(matches[1]) } else if matches := regexSummary.FindStringSubmatch(line); len(matches) == 8 { - p.summary(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6], matches[7]) + return p.summary(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6], matches[7]) } else if matches := regexCoverage.FindStringSubmatch(line); len(matches) == 3 { - p.coverage(matches[1], matches[2]) + return p.coverage(matches[1], matches[2]) } else if matches := regexBenchmark.FindStringSubmatch(line); len(matches) == 2 { - p.runBench(matches[1]) + return p.runBench(matches[1]) } else if matches := regexBenchSummary.FindStringSubmatch(line); len(matches) == 7 { - p.benchSummary(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]) + return p.benchSummary(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]) } else if matches := regexEndBenchmark.FindStringSubmatch(line); len(matches) == 3 { - p.endBench(matches[1], matches[2]) + return p.endBench(matches[1], matches[2]) } else if strings.HasPrefix(line, "# ") { - // TODO(jstemmer): this should just be output; we should detect build output when building report fields := strings.Fields(strings.TrimPrefix(line, "# ")) if len(fields) == 1 || len(fields) == 2 { - p.buildOutput(fields[0]) - } else { - p.output(line) + return p.buildOutput(fields[0]) } - } else { - p.output(line) } + return p.output(line) } -func (p *Parser) add(event Event) { - p.events = append(p.events, event) +func (p *Parser) runTest(name string) []Event { + return []Event{{Type: "run_test", Name: name}} } -func (p *Parser) runTest(name string) { - p.add(Event{Type: "run_test", Name: name}) +func (p *Parser) pauseTest(name string) []Event { + return []Event{{Type: "pause_test", Name: name}} } -func (p *Parser) pauseTest(name string) { - p.add(Event{Type: "pause_test", Name: name}) +func (p *Parser) contTest(name string) []Event { + return []Event{{Type: "cont_test", Name: name}} } -func (p *Parser) contTest(name string) { - p.add(Event{Type: "cont_test", Name: name}) -} - -func (p *Parser) endTest(line, indent, result, name, duration string) { +func (p *Parser) endTest(line, indent, result, name, duration string) []Event { + var events []Event if idx := strings.Index(line, fmt.Sprintf("%s--- %s:", indent, result)); idx > 0 { - p.output(line[:idx]) + events = append(events, p.output(line[:idx])...) } _, n := stripIndent(indent) - p.add(Event{ + events = append(events, Event{ Type: "end_test", Name: name, Result: result, Indent: n, Duration: parseSeconds(duration), }) + return events } -func (p *Parser) status(result string) { - p.add(Event{Type: "status", Result: result}) +func (p *Parser) status(result string) []Event { + return []Event{{Type: "status", Result: result}} } -func (p *Parser) summary(result, name, duration, cached, status, covpct, packages string) { - p.add(Event{ +func (p *Parser) summary(result, name, duration, cached, status, covpct, packages string) []Event { + return []Event{{ Type: "summary", Result: result, Name: name, @@ -258,26 +258,26 @@ func (p *Parser) summary(result, name, duration, cached, status, covpct, package Data: strings.TrimSpace(cached + " " + status), CovPct: parseFloat(covpct), CovPackages: parsePackages(packages), - }) + }} } -func (p *Parser) coverage(percent, packages string) { - p.add(Event{ +func (p *Parser) coverage(percent, packages string) []Event { + return []Event{{ Type: "coverage", CovPct: parseFloat(percent), CovPackages: parsePackages(packages), - }) + }} } -func (p *Parser) runBench(name string) { - p.add(Event{ +func (p *Parser) runBench(name string) []Event { + return []Event{{ Type: "run_benchmark", Name: name, - }) + }} } -func (p *Parser) benchSummary(name, iterations, nsPerOp, mbPerSec, bytesPerOp, allocsPerOp string) { - p.add(Event{ +func (p *Parser) benchSummary(name, iterations, nsPerOp, mbPerSec, bytesPerOp, allocsPerOp string) []Event { + return []Event{{ Type: "benchmark", Name: name, Iterations: parseInt(iterations), @@ -285,26 +285,26 @@ func (p *Parser) benchSummary(name, iterations, nsPerOp, mbPerSec, bytesPerOp, a MBPerSec: parseFloat(mbPerSec), BytesPerOp: parseInt(bytesPerOp), AllocsPerOp: parseInt(allocsPerOp), - }) + }} } -func (p *Parser) endBench(result, name string) { - p.add(Event{ +func (p *Parser) endBench(result, name string) []Event { + return []Event{{ Type: "end_benchmark", Name: name, Result: result, - }) + }} } -func (p *Parser) buildOutput(packageName string) { - p.add(Event{ +func (p *Parser) buildOutput(packageName string) []Event { + return []Event{{ Type: "build_output", Name: packageName, - }) + }} } -func (p *Parser) output(line string) { - p.add(Event{Type: "output", Data: line}) +func (p *Parser) output(line string) []Event { + return []Event{{Type: "output", Data: line}} } func parseSeconds(s string) time.Duration { diff --git a/parser/gotest/gotest_test.go b/parser/gotest/gotest_test.go index 7e66f1a..ff3e1ba 100644 --- a/parser/gotest/gotest_test.go +++ b/parser/gotest/gotest_test.go @@ -16,37 +16,45 @@ var ( type parseLineTest struct { input string - events interface{} + events []Event +} + +func (t parseLineTest) Name() string { + var types []string + for _, e := range t.events { + types = append(types, e.Type) + } + return strings.Join(types, "-") } var parseLineTests = []parseLineTest{ { "=== RUN TestOne", - Event{Type: "run_test", Name: "TestOne"}, + []Event{{Type: "run_test", Name: "TestOne"}}, }, { "=== RUN TestTwo/Subtest", - Event{Type: "run_test", Name: "TestTwo/Subtest"}, + []Event{{Type: "run_test", Name: "TestTwo/Subtest"}}, }, { "=== PAUSE TestOne", - Event{Type: "pause_test", Name: "TestOne"}, + []Event{{Type: "pause_test", Name: "TestOne"}}, }, { "=== CONT TestOne", - Event{Type: "cont_test", Name: "TestOne"}, + []Event{{Type: "cont_test", Name: "TestOne"}}, }, { "--- PASS: TestOne (12.34 seconds)", - Event{Type: "end_test", Name: "TestOne", Result: "PASS", Duration: 12_340 * time.Millisecond}, + []Event{{Type: "end_test", Name: "TestOne", Result: "PASS", Duration: 12_340 * time.Millisecond}}, }, { " --- SKIP: TestOne/Subtest (0.00s)", - Event{Type: "end_test", Name: "TestOne/Subtest", Result: "SKIP", Indent: 1}, + []Event{{Type: "end_test", Name: "TestOne/Subtest", Result: "SKIP", Indent: 1}}, }, { " --- FAIL: TestOne/Subtest/#01 (0.35s)", - Event{Type: "end_test", Name: "TestOne/Subtest/#01", Result: "FAIL", Duration: 350 * time.Millisecond, Indent: 2}, + []Event{{Type: "end_test", Name: "TestOne/Subtest/#01", Result: "FAIL", Duration: 350 * time.Millisecond, Indent: 2}}, }, { "some text--- PASS: TestTwo (0.06 seconds)", @@ -57,157 +65,141 @@ var parseLineTests = []parseLineTest{ }, { "PASS", - Event{Type: "status", Result: "PASS"}, + []Event{{Type: "status", Result: "PASS"}}, }, { "FAIL", - Event{Type: "status", Result: "FAIL"}, + []Event{{Type: "status", Result: "FAIL"}}, }, { "SKIP", - Event{Type: "status", Result: "SKIP"}, + []Event{{Type: "status", Result: "SKIP"}}, }, { "ok package/name/ok 0.100s", - Event{Type: "summary", Name: "package/name/ok", Result: "ok", Duration: 100 * time.Millisecond}, + []Event{{Type: "summary", Name: "package/name/ok", Result: "ok", Duration: 100 * time.Millisecond}}, }, { "FAIL package/name/failing [build failed]", - Event{Type: "summary", Name: "package/name/failing", Result: "FAIL", Data: "[build failed]"}, + []Event{{Type: "summary", Name: "package/name/failing", Result: "FAIL", Data: "[build failed]"}}, }, { "FAIL package/other/failing [setup failed]", - Event{Type: "summary", Name: "package/other/failing", Result: "FAIL", Data: "[setup failed]"}, + []Event{{Type: "summary", Name: "package/other/failing", Result: "FAIL", Data: "[setup failed]"}}, }, { "ok package/other (cached)", - Event{Type: "summary", Name: "package/other", Result: "ok", Data: "(cached)"}, + []Event{{Type: "summary", Name: "package/other", Result: "ok", Data: "(cached)"}}, }, { "ok package/name 0.400s coverage: 10.0% of statements", - Event{Type: "summary", Name: "package/name", Result: "ok", Duration: 400 * time.Millisecond, CovPct: 10}, + []Event{{Type: "summary", Name: "package/name", Result: "ok", Duration: 400 * time.Millisecond, CovPct: 10}}, }, { "ok package/name 4.200s coverage: 99.8% of statements in fmt, encoding/xml", - Event{Type: "summary", Name: "package/name", Result: "ok", Duration: 4200 * time.Millisecond, CovPct: 99.8, CovPackages: []string{"fmt", "encoding/xml"}}, + []Event{{Type: "summary", Name: "package/name", Result: "ok", Duration: 4200 * time.Millisecond, CovPct: 99.8, CovPackages: []string{"fmt", "encoding/xml"}}}, }, { "? package/name [no test files]", - Event{Type: "summary", Name: "package/name", Result: "?", Data: "[no test files]"}, + []Event{{Type: "summary", Name: "package/name", Result: "?", Data: "[no test files]"}}, }, { "ok package/name 0.001s [no tests to run]", - Event{Type: "summary", Name: "package/name", Result: "ok", Duration: 1 * time.Millisecond, Data: "[no tests to run]"}, + []Event{{Type: "summary", Name: "package/name", Result: "ok", Duration: 1 * time.Millisecond, Data: "[no tests to run]"}}, }, { "ok package/name (cached) [no tests to run]", - Event{Type: "summary", Name: "package/name", Result: "ok", Data: "(cached) [no tests to run]"}, + []Event{{Type: "summary", Name: "package/name", Result: "ok", Data: "(cached) [no tests to run]"}}, }, { "coverage: 10% of statements", - Event{Type: "coverage", CovPct: 10}, + []Event{{Type: "coverage", CovPct: 10}}, }, { "coverage: 10% of statements in fmt, encoding/xml", - Event{Type: "coverage", CovPct: 10, CovPackages: []string{"fmt", "encoding/xml"}}, + []Event{{Type: "coverage", CovPct: 10, CovPackages: []string{"fmt", "encoding/xml"}}}, }, { "coverage: 13.37% of statements", - Event{Type: "coverage", CovPct: 13.37}, + []Event{{Type: "coverage", CovPct: 13.37}}, }, { "coverage: 99.8% of statements in fmt, encoding/xml", - Event{Type: "coverage", CovPct: 99.8, CovPackages: []string{"fmt", "encoding/xml"}}, + []Event{{Type: "coverage", CovPct: 99.8, CovPackages: []string{"fmt", "encoding/xml"}}}, }, { "BenchmarkOK", - Event{Type: "run_benchmark", Name: "BenchmarkOK"}, + []Event{{Type: "run_benchmark", Name: "BenchmarkOK"}}, }, { "BenchmarkOne-8 2000000 604 ns/op", - Event{Type: "benchmark", Name: "BenchmarkOne", Iterations: 2_000_000, NsPerOp: 604}, + []Event{{Type: "benchmark", Name: "BenchmarkOne", Iterations: 2_000_000, NsPerOp: 604}}, }, { "BenchmarkTwo-16 30000 52568 ns/op 24879 B/op 494 allocs/op", - Event{Type: "benchmark", Name: "BenchmarkTwo", Iterations: 30_000, NsPerOp: 52_568, BytesPerOp: 24_879, AllocsPerOp: 494}, + []Event{{Type: "benchmark", Name: "BenchmarkTwo", Iterations: 30_000, NsPerOp: 52_568, BytesPerOp: 24_879, AllocsPerOp: 494}}, }, { "BenchmarkThree 2000000000 0.26 ns/op", - Event{Type: "benchmark", Name: "BenchmarkThree", Iterations: 2_000_000_000, NsPerOp: 0.26}, + []Event{{Type: "benchmark", Name: "BenchmarkThree", Iterations: 2_000_000_000, NsPerOp: 0.26}}, }, { "BenchmarkFour-8 10000 104427 ns/op 95.76 MB/s 40629 B/op 5 allocs/op", - Event{Type: "benchmark", Name: "BenchmarkFour", Iterations: 10_000, NsPerOp: 104_427, MBPerSec: 95.76, BytesPerOp: 40_629, AllocsPerOp: 5}, + []Event{{Type: "benchmark", Name: "BenchmarkFour", Iterations: 10_000, NsPerOp: 104_427, MBPerSec: 95.76, BytesPerOp: 40_629, AllocsPerOp: 5}}, }, { "--- BENCH: BenchmarkOK-8", - Event{Type: "end_benchmark", Name: "BenchmarkOK", Result: "BENCH"}, + []Event{{Type: "end_benchmark", Name: "BenchmarkOK", Result: "BENCH"}}, }, { "--- FAIL: BenchmarkError", - Event{Type: "end_benchmark", Name: "BenchmarkError", Result: "FAIL"}, + []Event{{Type: "end_benchmark", Name: "BenchmarkError", Result: "FAIL"}}, }, { "--- SKIP: BenchmarkSkip", - Event{Type: "end_benchmark", Name: "BenchmarkSkip", Result: "SKIP"}, + []Event{{Type: "end_benchmark", Name: "BenchmarkSkip", Result: "SKIP"}}, }, { "# package/name/failing1", - Event{Type: "build_output", Name: "package/name/failing1"}, + []Event{{Type: "build_output", Name: "package/name/failing1"}}, }, { "# package/name/failing2 [package/name/failing2.test]", - Event{Type: "build_output", Name: "package/name/failing2"}, + []Event{{Type: "build_output", Name: "package/name/failing2"}}, }, { "single line stdout", - Event{Type: "output", Data: "single line stdout"}, + []Event{{Type: "output", Data: "single line stdout"}}, }, { "# some more output", - Event{Type: "output", Data: "# some more output"}, + []Event{{Type: "output", Data: "# some more output"}}, }, { "\tfile_test.go:11: Error message", - Event{Type: "output", Data: "\tfile_test.go:11: Error message"}, + []Event{{Type: "output", Data: "\tfile_test.go:11: Error message"}}, }, { "\tfile_test.go:12: Longer", - Event{Type: "output", Data: "\tfile_test.go:12: Longer"}, + []Event{{Type: "output", Data: "\tfile_test.go:12: Longer"}}, }, { "\t\terror", - Event{Type: "output", Data: "\t\terror"}, + []Event{{Type: "output", Data: "\t\terror"}}, }, { "\t\tmessage.", - Event{Type: "output", Data: "\t\tmessage."}, + []Event{{Type: "output", Data: "\t\tmessage."}}, }, } func TestParseLine(t *testing.T) { for i, test := range parseLineTests { - var want []Event - switch e := test.events.(type) { - case Event: - want = []Event{e} - case []Event: - want = e - default: - panic("invalid events type") - } - - var types []string - for _, e := range want { - types = append(types, e.Type) - } - - name := fmt.Sprintf("%d %s", i+1, strings.Join(types, ",")) + name := fmt.Sprintf("%d-%s", i, test.Name()) t.Run(name, func(t *testing.T) { parser := NewParser() - parser.parseLine(test.input) - got := parser.events - if diff := cmp.Diff(want, got); diff != "" { + events := parser.parseLine(test.input) + if diff := cmp.Diff(events, test.events); diff != "" { t.Errorf("parseLine(%q) returned unexpected events, diff (-want, +got):\n%v", test.input, diff) } })