parser/gotest: refactor parser so Parse is no longer a top level func

Making Parse a method on a Parser struct makes it possible to later
define an common parser interface.
This commit is contained in:
Joël Stemmer 2022-03-14 23:12:20 +00:00
parent 0e7d095a28
commit 832cc97037
4 changed files with 50 additions and 30 deletions

View File

@ -76,7 +76,8 @@ func main() {
in = io.TeeReader(in, os.Stdout) in = io.TeeReader(in, os.Stdout)
} }
events, err := gotest.Parse(in) parser := gotest.New()
events, err := parser.Parse(in)
if err != nil { if err != nil {
exitf("error reading input: %s\n", err) exitf("error reading input: %s\n", err)
} }

View File

@ -192,7 +192,8 @@ func testReport(input, reportFile, packageName string, t *testing.T) {
} }
defer file.Close() defer file.Close()
events, err := gotest.Parse(file) parser := gotest.New()
events, err := parser.Parse(file)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -35,10 +35,35 @@ var (
`(?:\s+coverage:\s+(\d+\.\d+)%\sof\sstatements(?:\sin\s(.+))?)?$`) `(?:\s+coverage:\s+(\d+\.\d+)%\sof\sstatements(?:\sin\s(.+))?)?$`)
) )
// Parse parses Go test output from the given io.Reader r. // Option defines options that can be passed to gotest.New.
func Parse(r io.Reader) ([]gtr.Event, error) { type Option func(*Parser)
p := &parser{}
// PackageName sets the default package name to use when it cannot be
// determined from the test output.
func PackageName(name string) Option {
return func(p *Parser) {
p.packageName = name
}
}
// New returns a new Go test output parser.
func New(options ...Option) *Parser {
p := &Parser{}
for _, option := range options {
option(p)
}
return p
}
// Parser is a Go test output Parser.
type Parser struct {
packageName string
events []gtr.Event
}
// Parse parses Go test output from the given io.Reader r.
func (p *Parser) Parse(r io.Reader) ([]gtr.Event, error) {
s := bufio.NewScanner(r) s := bufio.NewScanner(r)
for s.Scan() { for s.Scan() {
p.parseLine(s.Text()) p.parseLine(s.Text())
@ -46,11 +71,7 @@ func Parse(r io.Reader) ([]gtr.Event, error) {
return p.events, s.Err() return p.events, s.Err()
} }
type parser struct { func (p *Parser) parseLine(line string) {
events []gtr.Event
}
func (p *parser) parseLine(line string) {
if strings.HasPrefix(line, "=== RUN ") { if strings.HasPrefix(line, "=== RUN ") {
p.runTest(strings.TrimSpace(line[8:])) p.runTest(strings.TrimSpace(line[8:]))
} else if strings.HasPrefix(line, "=== PAUSE ") { } else if strings.HasPrefix(line, "=== PAUSE ") {
@ -80,23 +101,23 @@ func (p *parser) parseLine(line string) {
} }
} }
func (p *parser) add(event gtr.Event) { func (p *Parser) add(event gtr.Event) {
p.events = append(p.events, event) p.events = append(p.events, event)
} }
func (p *parser) runTest(name string) { func (p *Parser) runTest(name string) {
p.add(gtr.Event{Type: "run_test", Name: name}) p.add(gtr.Event{Type: "run_test", Name: name})
} }
func (p *parser) pauseTest(name string) { func (p *Parser) pauseTest(name string) {
p.add(gtr.Event{Type: "pause_test", Name: name}) p.add(gtr.Event{Type: "pause_test", Name: name})
} }
func (p *parser) contTest(name string) { func (p *Parser) contTest(name string) {
p.add(gtr.Event{Type: "cont_test", Name: name}) p.add(gtr.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) {
if idx := strings.Index(line, fmt.Sprintf("%s--- %s:", indent, result)); idx > 0 { if idx := strings.Index(line, fmt.Sprintf("%s--- %s:", indent, result)); idx > 0 {
p.output(line[:idx]) p.output(line[:idx])
} }
@ -110,11 +131,11 @@ func (p *parser) endTest(line, indent, result, name, duration string) {
}) })
} }
func (p *parser) status(result string) { func (p *Parser) status(result string) {
p.add(gtr.Event{Type: "status", Result: result}) p.add(gtr.Event{Type: "status", Result: result})
} }
func (p *parser) summary(result, name, duration, cached, status, covpct, packages string) { func (p *Parser) summary(result, name, duration, cached, status, covpct, packages string) {
p.add(gtr.Event{ p.add(gtr.Event{
Type: "summary", Type: "summary",
Result: result, Result: result,
@ -126,7 +147,7 @@ func (p *parser) summary(result, name, duration, cached, status, covpct, package
}) })
} }
func (p *parser) coverage(percent, packages string) { func (p *Parser) coverage(percent, packages string) {
p.add(gtr.Event{ p.add(gtr.Event{
Type: "coverage", Type: "coverage",
CovPct: parseFloat(percent), CovPct: parseFloat(percent),
@ -134,7 +155,7 @@ func (p *parser) coverage(percent, packages string) {
}) })
} }
func (p *parser) benchmark(name, iterations, nsPerOp, mbPerSec, bytesPerOp, allocsPerOp string) { func (p *Parser) benchmark(name, iterations, nsPerOp, mbPerSec, bytesPerOp, allocsPerOp string) {
p.add(gtr.Event{ p.add(gtr.Event{
Type: "benchmark", Type: "benchmark",
Name: name, Name: name,
@ -146,14 +167,14 @@ func (p *parser) benchmark(name, iterations, nsPerOp, mbPerSec, bytesPerOp, allo
}) })
} }
func (p *parser) buildOutput(packageName string) { func (p *Parser) buildOutput(packageName string) {
p.add(gtr.Event{ p.add(gtr.Event{
Type: "build_output", Type: "build_output",
Name: packageName, Name: packageName,
}) })
} }
func (p *parser) output(line string) { func (p *Parser) output(line string) {
p.add(gtr.Event{Type: "output", Data: line}) p.add(gtr.Event{Type: "output", Data: line})
} }

View File

@ -185,15 +185,12 @@ func TestParseLine(t *testing.T) {
name := fmt.Sprintf("%d %s", i+1, strings.Join(types, ",")) name := fmt.Sprintf("%d %s", i+1, strings.Join(types, ","))
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
testParseLine(t, &parser{}, test.input, want) parser := New()
parser.parseLine(test.input)
got := parser.events
if diff := cmp.Diff(got, want); diff != "" {
t.Errorf("parseLine(%q) returned unexpected events, diff (-got, +want):\n%v", test.input, diff)
}
}) })
} }
} }
func testParseLine(t *testing.T, parser *parser, input string, want []gtr.Event) {
parser.parseLine(input)
got := parser.events
if diff := cmp.Diff(got, want); diff != "" {
t.Errorf("parseLine(%q) returned unexpected events, diff (-got, +want):\n%v", input, diff)
}
}