Skip to content

Commit 85f2715

Browse files
committed
parser/gotest: Create JSONEventReader in internal reader package
The JSONEventReader implements reading lines with metadata and replaces the gotest.jsonReader struct.
1 parent bd21d54 commit 85f2715

File tree

4 files changed

+87
-115
lines changed

4 files changed

+87
-115
lines changed

parser/gotest/internal/reader/reader.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package reader
33
import (
44
"bufio"
55
"bytes"
6+
"encoding/json"
67
"io"
8+
"strings"
9+
"time"
710
)
811

912
// LineReader is an interface to read lines with optional Metadata.
@@ -68,3 +71,52 @@ func (r *LimitedLineReader) ReadLine() (string, *Metadata, error) {
6871
}
6972
return buf.String(), nil, nil
7073
}
74+
75+
// Event represents a JSON event emitted by `go test -json`.
76+
type Event struct {
77+
Time time.Time
78+
Action string
79+
Package string
80+
Test string
81+
Elapsed float64 // seconds
82+
Output string
83+
}
84+
85+
// JSONEventReader reads JSON events from an io.Reader object.
86+
type JSONEventReader struct {
87+
r *LimitedLineReader
88+
}
89+
90+
var _ LineReader = &JSONEventReader{}
91+
92+
// jsonLineLimit is the maximum size of a single JSON line emitted by `go test
93+
// -json`.
94+
const jsonLineLimit = 64 * 1024
95+
96+
// NewJSONEventReader returns a JSONEventReader to read the data in JSON
97+
// events from r.
98+
func NewJSONEventReader(r io.Reader) *JSONEventReader {
99+
return &JSONEventReader{NewLimitedLineReader(r, jsonLineLimit)}
100+
}
101+
102+
// ReadLine returns the next line from the underlying reader.
103+
func (r *JSONEventReader) ReadLine() (string, *Metadata, error) {
104+
for {
105+
line, _, err := r.r.ReadLine()
106+
if err != nil {
107+
return "", nil, err
108+
}
109+
if len(line) == 0 || line[0] != '{' {
110+
return line, nil, nil
111+
}
112+
event := &Event{}
113+
if err := json.Unmarshal([]byte(line), event); err != nil {
114+
return "", nil, err
115+
}
116+
if event.Output == "" {
117+
// Skip events without output
118+
continue
119+
}
120+
return strings.TrimSuffix(event.Output, "\n"), &Metadata{Package: event.Package}, nil
121+
}
122+
}

parser/gotest/internal/reader/reader_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"io"
66
"strings"
77
"testing"
8+
9+
"github.com/google/go-cmp/cmp"
810
)
911

1012
const testingLimit = 4 * 1024 * 1024
@@ -66,3 +68,34 @@ func TestLimitedLineReader(t *testing.T) {
6668
})
6769
}
6870
}
71+
72+
func TestJSONEventReader(t *testing.T) {
73+
input := `some other output
74+
{"Time":"2019-10-09T00:00:00.708139047+00:00","Action":"output","Package":"package/name/ok","Test":"TestOK"}
75+
{"Time":"2019-10-09T00:00:00.708139047+00:00","Action":"output","Package":"package/name/ok","Test":"TestOK","Output":"=== RUN TestOK\n"}
76+
`
77+
want := []struct {
78+
line string
79+
metadata *Metadata
80+
}{
81+
{"some other output", nil},
82+
{"=== RUN TestOK", &Metadata{Package: "package/name/ok"}},
83+
}
84+
85+
r := NewJSONEventReader(strings.NewReader(input))
86+
for i := 0; i < len(want); i++ {
87+
line, metadata, err := r.ReadLine()
88+
if err == io.EOF {
89+
return
90+
} else if err != nil {
91+
t.Fatalf("ReadLine() returned error %v", err)
92+
}
93+
94+
if diff := cmp.Diff(want[i].line, line); diff != "" {
95+
t.Errorf("ReadLine() returned incorrect line, diff (-want, +got):\n%s\n", diff)
96+
}
97+
if diff := cmp.Diff(want[i].metadata, metadata); diff != "" {
98+
t.Errorf("ReadLine() Returned incorrect metadata, diff (-want, +got):\n%s\n", diff)
99+
}
100+
}
101+
}

parser/gotest/json.go

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package gotest
22

33
import (
4-
"bufio"
5-
"encoding/json"
64
"io"
7-
"time"
85

96
"github.com/jstemmer/go-junit-report/v2/gtr"
7+
"github.com/jstemmer/go-junit-report/v2/parser/gotest/internal/reader"
108
)
119

1210
// NewJSONParser returns a new Go test json output parser.
@@ -22,56 +20,10 @@ type JSONParser struct {
2220
// Parse parses Go test json output from the given io.Reader r and returns
2321
// gtr.Report.
2422
func (p *JSONParser) Parse(r io.Reader) (gtr.Report, error) {
25-
return p.gp.Parse(newJSONReader(r))
23+
return p.gp.parse(reader.NewJSONEventReader(r))
2624
}
2725

2826
// Events returns the events created by the parser.
2927
func (p *JSONParser) Events() []Event {
3028
return p.gp.Events()
3129
}
32-
33-
type jsonEvent struct {
34-
Time time.Time
35-
Action string
36-
Package string
37-
Test string
38-
Elapsed float64 // seconds
39-
Output string
40-
}
41-
42-
type jsonReader struct {
43-
r *bufio.Reader
44-
buf []byte
45-
}
46-
47-
func newJSONReader(reader io.Reader) *jsonReader {
48-
return &jsonReader{r: bufio.NewReader(reader)}
49-
}
50-
51-
func (j *jsonReader) Read(p []byte) (int, error) {
52-
var err error
53-
for len(j.buf) == 0 {
54-
j.buf, err = j.readNextLine()
55-
if err != nil {
56-
return 0, err
57-
}
58-
}
59-
n := copy(p, j.buf)
60-
j.buf = j.buf[n:]
61-
return n, nil
62-
}
63-
64-
func (j jsonReader) readNextLine() ([]byte, error) {
65-
line, err := j.r.ReadBytes('\n')
66-
if err != nil {
67-
return nil, err
68-
}
69-
if len(line) == 0 || line[0] != '{' {
70-
return line, nil
71-
}
72-
var event jsonEvent
73-
if err := json.Unmarshal(line, &event); err != nil {
74-
return nil, err
75-
}
76-
return []byte(event.Output), nil
77-
}

parser/gotest/json_test.go

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)