xref: /aosp_15_r20/external/swiftshader/tests/regres/cov/serialization.go (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1*03ce13f7SAndroid Build Coastguard Worker// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
2*03ce13f7SAndroid Build Coastguard Worker//
3*03ce13f7SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*03ce13f7SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*03ce13f7SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*03ce13f7SAndroid Build Coastguard Worker//
7*03ce13f7SAndroid Build Coastguard Worker//    http://www.apache.org/licenses/LICENSE-2.0
8*03ce13f7SAndroid Build Coastguard Worker//
9*03ce13f7SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*03ce13f7SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*03ce13f7SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*03ce13f7SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*03ce13f7SAndroid Build Coastguard Worker// limitations under the License.
14*03ce13f7SAndroid Build Coastguard Worker
15*03ce13f7SAndroid Build Coastguard Workerpackage cov
16*03ce13f7SAndroid Build Coastguard Worker
17*03ce13f7SAndroid Build Coastguard Workerimport (
18*03ce13f7SAndroid Build Coastguard Worker	"bufio"
19*03ce13f7SAndroid Build Coastguard Worker	"compress/zlib"
20*03ce13f7SAndroid Build Coastguard Worker	"fmt"
21*03ce13f7SAndroid Build Coastguard Worker	"io"
22*03ce13f7SAndroid Build Coastguard Worker	"runtime/debug"
23*03ce13f7SAndroid Build Coastguard Worker	"sort"
24*03ce13f7SAndroid Build Coastguard Worker	"strconv"
25*03ce13f7SAndroid Build Coastguard Worker	"strings"
26*03ce13f7SAndroid Build Coastguard Worker)
27*03ce13f7SAndroid Build Coastguard Worker
28*03ce13f7SAndroid Build Coastguard Worker// ReadJSON parses the JSON Tree from r.
29*03ce13f7SAndroid Build Coastguard Workerfunc ReadJSON(r io.Reader) (*Tree, string, error) {
30*03ce13f7SAndroid Build Coastguard Worker	p := parser{r: bufio.NewReader(r)}
31*03ce13f7SAndroid Build Coastguard Worker	return p.parse()
32*03ce13f7SAndroid Build Coastguard Worker}
33*03ce13f7SAndroid Build Coastguard Worker
34*03ce13f7SAndroid Build Coastguard Worker// Encode zlib encodes the JSON coverage tree to w.
35*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) Encode(revision string, w io.Writer) error {
36*03ce13f7SAndroid Build Coastguard Worker	t.Optimize()
37*03ce13f7SAndroid Build Coastguard Worker
38*03ce13f7SAndroid Build Coastguard Worker	zw := zlib.NewWriter(w)
39*03ce13f7SAndroid Build Coastguard Worker
40*03ce13f7SAndroid Build Coastguard Worker	_, err := zw.Write([]byte(t.JSON(revision)))
41*03ce13f7SAndroid Build Coastguard Worker	if err != nil {
42*03ce13f7SAndroid Build Coastguard Worker		return err
43*03ce13f7SAndroid Build Coastguard Worker	}
44*03ce13f7SAndroid Build Coastguard Worker
45*03ce13f7SAndroid Build Coastguard Worker	return zw.Close()
46*03ce13f7SAndroid Build Coastguard Worker}
47*03ce13f7SAndroid Build Coastguard Worker
48*03ce13f7SAndroid Build Coastguard Worker// JSON returns the full test tree serialized to JSON.
49*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) JSON(revision string) string {
50*03ce13f7SAndroid Build Coastguard Worker	sb := &strings.Builder{}
51*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`{`)
52*03ce13f7SAndroid Build Coastguard Worker
53*03ce13f7SAndroid Build Coastguard Worker	spansByID := map[SpanID]Span{}
54*03ce13f7SAndroid Build Coastguard Worker	for span, id := range t.spans {
55*03ce13f7SAndroid Build Coastguard Worker		spansByID[id] = span
56*03ce13f7SAndroid Build Coastguard Worker	}
57*03ce13f7SAndroid Build Coastguard Worker
58*03ce13f7SAndroid Build Coastguard Worker	// write the revision
59*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`"r":"` + revision + `"`)
60*03ce13f7SAndroid Build Coastguard Worker
61*03ce13f7SAndroid Build Coastguard Worker	// write the strings
62*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`,"n":[`)
63*03ce13f7SAndroid Build Coastguard Worker	for i, s := range t.strings.s {
64*03ce13f7SAndroid Build Coastguard Worker		if i > 0 {
65*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`,`)
66*03ce13f7SAndroid Build Coastguard Worker		}
67*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`"`)
68*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(s)
69*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`"`)
70*03ce13f7SAndroid Build Coastguard Worker	}
71*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`]`)
72*03ce13f7SAndroid Build Coastguard Worker
73*03ce13f7SAndroid Build Coastguard Worker	// write the tests
74*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`,"t":`)
75*03ce13f7SAndroid Build Coastguard Worker	t.writeTestJSON(&t.testRoot, sb)
76*03ce13f7SAndroid Build Coastguard Worker
77*03ce13f7SAndroid Build Coastguard Worker	// write the spans
78*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`,"s":`)
79*03ce13f7SAndroid Build Coastguard Worker	t.writeSpansJSON(sb)
80*03ce13f7SAndroid Build Coastguard Worker
81*03ce13f7SAndroid Build Coastguard Worker	// write the files
82*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`,"f":`)
83*03ce13f7SAndroid Build Coastguard Worker	t.writeFilesJSON(spansByID, sb)
84*03ce13f7SAndroid Build Coastguard Worker
85*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`}`)
86*03ce13f7SAndroid Build Coastguard Worker	return sb.String()
87*03ce13f7SAndroid Build Coastguard Worker}
88*03ce13f7SAndroid Build Coastguard Worker
89*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) writeTestJSON(test *Test, sb *strings.Builder) {
90*03ce13f7SAndroid Build Coastguard Worker	names := map[int]StringID{}
91*03ce13f7SAndroid Build Coastguard Worker	for name, idx := range test.indices {
92*03ce13f7SAndroid Build Coastguard Worker		names[int(idx)] = name
93*03ce13f7SAndroid Build Coastguard Worker	}
94*03ce13f7SAndroid Build Coastguard Worker
95*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`[`)
96*03ce13f7SAndroid Build Coastguard Worker	for i, child := range test.children {
97*03ce13f7SAndroid Build Coastguard Worker		if i > 0 {
98*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`,`)
99*03ce13f7SAndroid Build Coastguard Worker		}
100*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`[`)
101*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(fmt.Sprintf("%v,", names[i]))
102*03ce13f7SAndroid Build Coastguard Worker		t.writeTestJSON(&child, sb)
103*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`]`)
104*03ce13f7SAndroid Build Coastguard Worker	}
105*03ce13f7SAndroid Build Coastguard Worker
106*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`]`)
107*03ce13f7SAndroid Build Coastguard Worker}
108*03ce13f7SAndroid Build Coastguard Worker
109*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) writeSpansJSON(sb *strings.Builder) {
110*03ce13f7SAndroid Build Coastguard Worker	type spanAndID struct {
111*03ce13f7SAndroid Build Coastguard Worker		span Span
112*03ce13f7SAndroid Build Coastguard Worker		id   SpanID
113*03ce13f7SAndroid Build Coastguard Worker	}
114*03ce13f7SAndroid Build Coastguard Worker	spans := make([]spanAndID, 0, len(t.spans))
115*03ce13f7SAndroid Build Coastguard Worker	for span, id := range t.spans {
116*03ce13f7SAndroid Build Coastguard Worker		spans = append(spans, spanAndID{span, id})
117*03ce13f7SAndroid Build Coastguard Worker	}
118*03ce13f7SAndroid Build Coastguard Worker	sort.Slice(spans, func(i, j int) bool { return spans[i].id < spans[j].id })
119*03ce13f7SAndroid Build Coastguard Worker
120*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`[`)
121*03ce13f7SAndroid Build Coastguard Worker	for i, s := range spans {
122*03ce13f7SAndroid Build Coastguard Worker		if i > 0 {
123*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`,`)
124*03ce13f7SAndroid Build Coastguard Worker		}
125*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(fmt.Sprintf("[%v,%v,%v,%v]",
126*03ce13f7SAndroid Build Coastguard Worker			s.span.Start.Line, s.span.Start.Column,
127*03ce13f7SAndroid Build Coastguard Worker			s.span.End.Line, s.span.End.Column))
128*03ce13f7SAndroid Build Coastguard Worker	}
129*03ce13f7SAndroid Build Coastguard Worker
130*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`]`)
131*03ce13f7SAndroid Build Coastguard Worker}
132*03ce13f7SAndroid Build Coastguard Worker
133*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) writeSpanJSON(span Span, sb *strings.Builder) {
134*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(fmt.Sprintf("[%v,%v,%v,%v]",
135*03ce13f7SAndroid Build Coastguard Worker		span.Start.Line, span.Start.Column,
136*03ce13f7SAndroid Build Coastguard Worker		span.End.Line, span.End.Column))
137*03ce13f7SAndroid Build Coastguard Worker}
138*03ce13f7SAndroid Build Coastguard Worker
139*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) writeFilesJSON(spansByID map[SpanID]Span, sb *strings.Builder) {
140*03ce13f7SAndroid Build Coastguard Worker	paths := make([]string, 0, len(t.files))
141*03ce13f7SAndroid Build Coastguard Worker	for path := range t.files {
142*03ce13f7SAndroid Build Coastguard Worker		paths = append(paths, path)
143*03ce13f7SAndroid Build Coastguard Worker	}
144*03ce13f7SAndroid Build Coastguard Worker	sort.Strings(paths)
145*03ce13f7SAndroid Build Coastguard Worker
146*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`{`)
147*03ce13f7SAndroid Build Coastguard Worker	for i, path := range paths {
148*03ce13f7SAndroid Build Coastguard Worker		file := t.files[path]
149*03ce13f7SAndroid Build Coastguard Worker
150*03ce13f7SAndroid Build Coastguard Worker		uncovered := append(SpanList{}, file.allSpans...)
151*03ce13f7SAndroid Build Coastguard Worker		for id := range t.allSpans(file, file.tcm) {
152*03ce13f7SAndroid Build Coastguard Worker			uncovered.Remove(spansByID[id])
153*03ce13f7SAndroid Build Coastguard Worker		}
154*03ce13f7SAndroid Build Coastguard Worker
155*03ce13f7SAndroid Build Coastguard Worker		if i > 0 {
156*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`,`)
157*03ce13f7SAndroid Build Coastguard Worker		}
158*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`"`)
159*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(path)
160*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`":`)
161*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`{`)
162*03ce13f7SAndroid Build Coastguard Worker		if totalLines := file.allSpans.NumLines(); totalLines > 0 {
163*03ce13f7SAndroid Build Coastguard Worker			uncoveredLines := uncovered.NumLines()
164*03ce13f7SAndroid Build Coastguard Worker			percentage := 1.0 - (float64(uncoveredLines) / float64(totalLines))
165*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`"p":`)
166*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(fmt.Sprintf("%v", percentage))
167*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`,`)
168*03ce13f7SAndroid Build Coastguard Worker		}
169*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`"g":`)
170*03ce13f7SAndroid Build Coastguard Worker		t.writeSpanGroupsJSON(file.spangroups, sb)
171*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`,"u":`)
172*03ce13f7SAndroid Build Coastguard Worker		t.writeUncoveredJSON(file, uncovered, sb)
173*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`,"c":`)
174*03ce13f7SAndroid Build Coastguard Worker		t.writeCoverageMapJSON(file.tcm, sb)
175*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`}`)
176*03ce13f7SAndroid Build Coastguard Worker	}
177*03ce13f7SAndroid Build Coastguard Worker
178*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`}`)
179*03ce13f7SAndroid Build Coastguard Worker}
180*03ce13f7SAndroid Build Coastguard Worker
181*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) writeSpanGroupsJSON(spangroups map[SpanGroupID]SpanGroup, sb *strings.Builder) {
182*03ce13f7SAndroid Build Coastguard Worker	type groupAndID struct {
183*03ce13f7SAndroid Build Coastguard Worker		group SpanGroup
184*03ce13f7SAndroid Build Coastguard Worker		id    SpanGroupID
185*03ce13f7SAndroid Build Coastguard Worker	}
186*03ce13f7SAndroid Build Coastguard Worker	groups := make([]groupAndID, 0, len(spangroups))
187*03ce13f7SAndroid Build Coastguard Worker	for id, group := range spangroups {
188*03ce13f7SAndroid Build Coastguard Worker		groups = append(groups, groupAndID{group, id})
189*03ce13f7SAndroid Build Coastguard Worker	}
190*03ce13f7SAndroid Build Coastguard Worker	sort.Slice(groups, func(i, j int) bool { return groups[i].id < groups[j].id })
191*03ce13f7SAndroid Build Coastguard Worker
192*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`[`)
193*03ce13f7SAndroid Build Coastguard Worker	for i, g := range groups {
194*03ce13f7SAndroid Build Coastguard Worker		if i > 0 {
195*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`,`)
196*03ce13f7SAndroid Build Coastguard Worker		}
197*03ce13f7SAndroid Build Coastguard Worker		t.writeSpanGroupJSON(g.group, sb)
198*03ce13f7SAndroid Build Coastguard Worker	}
199*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`]`)
200*03ce13f7SAndroid Build Coastguard Worker}
201*03ce13f7SAndroid Build Coastguard Worker
202*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) writeSpanGroupJSON(group SpanGroup, sb *strings.Builder) {
203*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`{`)
204*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`"s":[`)
205*03ce13f7SAndroid Build Coastguard Worker	for i, spanID := range group.Spans.List() {
206*03ce13f7SAndroid Build Coastguard Worker		if i > 0 {
207*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`,`)
208*03ce13f7SAndroid Build Coastguard Worker		}
209*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(fmt.Sprintf("%v", spanID))
210*03ce13f7SAndroid Build Coastguard Worker	}
211*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`]`)
212*03ce13f7SAndroid Build Coastguard Worker	if group.Extend != nil {
213*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`,"e":`)
214*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(fmt.Sprintf("%v", *group.Extend))
215*03ce13f7SAndroid Build Coastguard Worker	}
216*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`}`)
217*03ce13f7SAndroid Build Coastguard Worker}
218*03ce13f7SAndroid Build Coastguard Worker
219*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) writeUncoveredJSON(tf *treeFile, uncovered SpanList, sb *strings.Builder) {
220*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`[`)
221*03ce13f7SAndroid Build Coastguard Worker	for i, span := range uncovered {
222*03ce13f7SAndroid Build Coastguard Worker		if i > 0 {
223*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`,`)
224*03ce13f7SAndroid Build Coastguard Worker		}
225*03ce13f7SAndroid Build Coastguard Worker		t.writeSpanJSON(span, sb)
226*03ce13f7SAndroid Build Coastguard Worker	}
227*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`]`)
228*03ce13f7SAndroid Build Coastguard Worker}
229*03ce13f7SAndroid Build Coastguard Worker
230*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) writeCoverageMapJSON(c TestCoverageMap, sb *strings.Builder) {
231*03ce13f7SAndroid Build Coastguard Worker	ids := make([]TestIndex, 0, len(c))
232*03ce13f7SAndroid Build Coastguard Worker	for id := range c {
233*03ce13f7SAndroid Build Coastguard Worker		ids = append(ids, id)
234*03ce13f7SAndroid Build Coastguard Worker	}
235*03ce13f7SAndroid Build Coastguard Worker	sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
236*03ce13f7SAndroid Build Coastguard Worker
237*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`[`)
238*03ce13f7SAndroid Build Coastguard Worker	for i, id := range ids {
239*03ce13f7SAndroid Build Coastguard Worker		if i > 0 {
240*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`,`)
241*03ce13f7SAndroid Build Coastguard Worker		}
242*03ce13f7SAndroid Build Coastguard Worker
243*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`[`)
244*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(fmt.Sprintf("%v", id))
245*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`,`)
246*03ce13f7SAndroid Build Coastguard Worker		t.writeCoverageJSON(c[id], sb)
247*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`]`)
248*03ce13f7SAndroid Build Coastguard Worker	}
249*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`]`)
250*03ce13f7SAndroid Build Coastguard Worker}
251*03ce13f7SAndroid Build Coastguard Worker
252*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) writeCoverageJSON(c *TestCoverage, sb *strings.Builder) {
253*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`{`)
254*03ce13f7SAndroid Build Coastguard Worker	comma := false
255*03ce13f7SAndroid Build Coastguard Worker	if len(c.Spans) > 0 {
256*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`"s":[`)
257*03ce13f7SAndroid Build Coastguard Worker		for i, spanID := range c.Spans.List() {
258*03ce13f7SAndroid Build Coastguard Worker			if i > 0 {
259*03ce13f7SAndroid Build Coastguard Worker				sb.WriteString(`,`)
260*03ce13f7SAndroid Build Coastguard Worker			}
261*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(fmt.Sprintf("%v", spanID))
262*03ce13f7SAndroid Build Coastguard Worker		}
263*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`]`)
264*03ce13f7SAndroid Build Coastguard Worker		comma = true
265*03ce13f7SAndroid Build Coastguard Worker	}
266*03ce13f7SAndroid Build Coastguard Worker	if c.Group != nil {
267*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`"g":`)
268*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(fmt.Sprintf("%v", *c.Group))
269*03ce13f7SAndroid Build Coastguard Worker		comma = true
270*03ce13f7SAndroid Build Coastguard Worker	}
271*03ce13f7SAndroid Build Coastguard Worker	if len(c.Children) > 0 {
272*03ce13f7SAndroid Build Coastguard Worker		if comma {
273*03ce13f7SAndroid Build Coastguard Worker			sb.WriteString(`,`)
274*03ce13f7SAndroid Build Coastguard Worker		}
275*03ce13f7SAndroid Build Coastguard Worker		sb.WriteString(`"c":`)
276*03ce13f7SAndroid Build Coastguard Worker		t.writeCoverageMapJSON(c.Children, sb)
277*03ce13f7SAndroid Build Coastguard Worker	}
278*03ce13f7SAndroid Build Coastguard Worker	sb.WriteString(`}`)
279*03ce13f7SAndroid Build Coastguard Worker}
280*03ce13f7SAndroid Build Coastguard Worker
281*03ce13f7SAndroid Build Coastguard Workertype parser struct {
282*03ce13f7SAndroid Build Coastguard Worker	r   *bufio.Reader
283*03ce13f7SAndroid Build Coastguard Worker	err error
284*03ce13f7SAndroid Build Coastguard Worker
285*03ce13f7SAndroid Build Coastguard Worker	revision string
286*03ce13f7SAndroid Build Coastguard Worker	tree     Tree
287*03ce13f7SAndroid Build Coastguard Worker}
288*03ce13f7SAndroid Build Coastguard Worker
289*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parse() (*Tree, string, error) {
290*03ce13f7SAndroid Build Coastguard Worker	p.tree.init()
291*03ce13f7SAndroid Build Coastguard Worker	p.dict(func(key string) {
292*03ce13f7SAndroid Build Coastguard Worker		switch key {
293*03ce13f7SAndroid Build Coastguard Worker		case "r":
294*03ce13f7SAndroid Build Coastguard Worker			p.revision = p.str()
295*03ce13f7SAndroid Build Coastguard Worker		case "n":
296*03ce13f7SAndroid Build Coastguard Worker			p.parseStrings()
297*03ce13f7SAndroid Build Coastguard Worker		case "t":
298*03ce13f7SAndroid Build Coastguard Worker			p.parseTests(&p.tree.testRoot)
299*03ce13f7SAndroid Build Coastguard Worker		case "s":
300*03ce13f7SAndroid Build Coastguard Worker			p.parseSpans()
301*03ce13f7SAndroid Build Coastguard Worker		case "g":
302*03ce13f7SAndroid Build Coastguard Worker			p.parseSpanGroups()
303*03ce13f7SAndroid Build Coastguard Worker		case "f":
304*03ce13f7SAndroid Build Coastguard Worker			p.parseFiles()
305*03ce13f7SAndroid Build Coastguard Worker		default:
306*03ce13f7SAndroid Build Coastguard Worker			p.fail("Unknown root key '%v'", key)
307*03ce13f7SAndroid Build Coastguard Worker		}
308*03ce13f7SAndroid Build Coastguard Worker	})
309*03ce13f7SAndroid Build Coastguard Worker	if p.err != nil {
310*03ce13f7SAndroid Build Coastguard Worker		return nil, "", p.err
311*03ce13f7SAndroid Build Coastguard Worker	}
312*03ce13f7SAndroid Build Coastguard Worker
313*03ce13f7SAndroid Build Coastguard Worker	p.populateAllSpans(&p.tree)
314*03ce13f7SAndroid Build Coastguard Worker
315*03ce13f7SAndroid Build Coastguard Worker	return &p.tree, p.revision, nil
316*03ce13f7SAndroid Build Coastguard Worker}
317*03ce13f7SAndroid Build Coastguard Worker
318*03ce13f7SAndroid Build Coastguard Worker// populateAllSpans() adds all the coverage spans to each treeFile.allSpans.
319*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) populateAllSpans(tree *Tree) {
320*03ce13f7SAndroid Build Coastguard Worker	spansByID := map[SpanID]Span{}
321*03ce13f7SAndroid Build Coastguard Worker	for span, id := range tree.spans {
322*03ce13f7SAndroid Build Coastguard Worker		spansByID[id] = span
323*03ce13f7SAndroid Build Coastguard Worker	}
324*03ce13f7SAndroid Build Coastguard Worker	for _, file := range tree.files {
325*03ce13f7SAndroid Build Coastguard Worker		for spanID := range tree.allSpans(file, file.tcm) {
326*03ce13f7SAndroid Build Coastguard Worker			span := spansByID[spanID]
327*03ce13f7SAndroid Build Coastguard Worker			file.allSpans.Add(span)
328*03ce13f7SAndroid Build Coastguard Worker		}
329*03ce13f7SAndroid Build Coastguard Worker	}
330*03ce13f7SAndroid Build Coastguard Worker}
331*03ce13f7SAndroid Build Coastguard Worker
332*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parseStrings() {
333*03ce13f7SAndroid Build Coastguard Worker	p.array(func(idx int) {
334*03ce13f7SAndroid Build Coastguard Worker		id := StringID(idx)
335*03ce13f7SAndroid Build Coastguard Worker		s := p.str()
336*03ce13f7SAndroid Build Coastguard Worker		p.tree.strings.m[s] = id
337*03ce13f7SAndroid Build Coastguard Worker		p.tree.strings.s = append(p.tree.strings.s, s)
338*03ce13f7SAndroid Build Coastguard Worker	})
339*03ce13f7SAndroid Build Coastguard Worker}
340*03ce13f7SAndroid Build Coastguard Worker
341*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parseTests(t *Test) {
342*03ce13f7SAndroid Build Coastguard Worker	p.array(func(idx int) {
343*03ce13f7SAndroid Build Coastguard Worker		p.expect("[")
344*03ce13f7SAndroid Build Coastguard Worker		name := StringID(p.integer())
345*03ce13f7SAndroid Build Coastguard Worker		child, _ := t.index(name)
346*03ce13f7SAndroid Build Coastguard Worker		p.expect(",")
347*03ce13f7SAndroid Build Coastguard Worker		p.parseTests(child)
348*03ce13f7SAndroid Build Coastguard Worker		p.expect("]")
349*03ce13f7SAndroid Build Coastguard Worker	})
350*03ce13f7SAndroid Build Coastguard Worker}
351*03ce13f7SAndroid Build Coastguard Worker
352*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parseSpans() {
353*03ce13f7SAndroid Build Coastguard Worker	p.array(func(idx int) {
354*03ce13f7SAndroid Build Coastguard Worker		p.tree.spans[p.parseSpan()] = SpanID(idx)
355*03ce13f7SAndroid Build Coastguard Worker	})
356*03ce13f7SAndroid Build Coastguard Worker}
357*03ce13f7SAndroid Build Coastguard Worker
358*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parseSpan() Span {
359*03ce13f7SAndroid Build Coastguard Worker	p.expect("[")
360*03ce13f7SAndroid Build Coastguard Worker	s := Span{}
361*03ce13f7SAndroid Build Coastguard Worker	s.Start.Line = p.integer()
362*03ce13f7SAndroid Build Coastguard Worker	p.expect(",")
363*03ce13f7SAndroid Build Coastguard Worker	s.Start.Column = p.integer()
364*03ce13f7SAndroid Build Coastguard Worker	p.expect(",")
365*03ce13f7SAndroid Build Coastguard Worker	s.End.Line = p.integer()
366*03ce13f7SAndroid Build Coastguard Worker	p.expect(",")
367*03ce13f7SAndroid Build Coastguard Worker	s.End.Column = p.integer()
368*03ce13f7SAndroid Build Coastguard Worker	p.expect("]")
369*03ce13f7SAndroid Build Coastguard Worker	return s
370*03ce13f7SAndroid Build Coastguard Worker}
371*03ce13f7SAndroid Build Coastguard Worker
372*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parseFiles() {
373*03ce13f7SAndroid Build Coastguard Worker	p.dict(func(path string) {
374*03ce13f7SAndroid Build Coastguard Worker		p.tree.files[path] = p.parseFile()
375*03ce13f7SAndroid Build Coastguard Worker	})
376*03ce13f7SAndroid Build Coastguard Worker}
377*03ce13f7SAndroid Build Coastguard Worker
378*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parseFile() *treeFile {
379*03ce13f7SAndroid Build Coastguard Worker	file := newTreeFile()
380*03ce13f7SAndroid Build Coastguard Worker	if p.peek() == '{' {
381*03ce13f7SAndroid Build Coastguard Worker		p.dict(func(key string) {
382*03ce13f7SAndroid Build Coastguard Worker			switch key {
383*03ce13f7SAndroid Build Coastguard Worker			case "p":
384*03ce13f7SAndroid Build Coastguard Worker				p.double()
385*03ce13f7SAndroid Build Coastguard Worker			case "g":
386*03ce13f7SAndroid Build Coastguard Worker				file.spangroups = p.parseSpanGroups()
387*03ce13f7SAndroid Build Coastguard Worker			case "c":
388*03ce13f7SAndroid Build Coastguard Worker				p.parseCoverageMap(file.tcm)
389*03ce13f7SAndroid Build Coastguard Worker			case "u":
390*03ce13f7SAndroid Build Coastguard Worker				p.parseUncovered(file)
391*03ce13f7SAndroid Build Coastguard Worker			default:
392*03ce13f7SAndroid Build Coastguard Worker				p.fail("Unknown file key: '%s'", key)
393*03ce13f7SAndroid Build Coastguard Worker			}
394*03ce13f7SAndroid Build Coastguard Worker		})
395*03ce13f7SAndroid Build Coastguard Worker	} else { // backwards compatibility
396*03ce13f7SAndroid Build Coastguard Worker		p.parseCoverageMap(file.tcm)
397*03ce13f7SAndroid Build Coastguard Worker	}
398*03ce13f7SAndroid Build Coastguard Worker	return file
399*03ce13f7SAndroid Build Coastguard Worker}
400*03ce13f7SAndroid Build Coastguard Worker
401*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parseSpanGroups() map[SpanGroupID]SpanGroup {
402*03ce13f7SAndroid Build Coastguard Worker	spangroups := map[SpanGroupID]SpanGroup{}
403*03ce13f7SAndroid Build Coastguard Worker	p.array(func(groupIdx int) {
404*03ce13f7SAndroid Build Coastguard Worker		g := newSpanGroup()
405*03ce13f7SAndroid Build Coastguard Worker		p.dict(func(key string) {
406*03ce13f7SAndroid Build Coastguard Worker			switch key {
407*03ce13f7SAndroid Build Coastguard Worker			case "s":
408*03ce13f7SAndroid Build Coastguard Worker				p.array(func(spanIdx int) {
409*03ce13f7SAndroid Build Coastguard Worker					id := SpanID(p.integer())
410*03ce13f7SAndroid Build Coastguard Worker					g.Spans[id] = struct{}{}
411*03ce13f7SAndroid Build Coastguard Worker				})
412*03ce13f7SAndroid Build Coastguard Worker			case "e":
413*03ce13f7SAndroid Build Coastguard Worker				extend := SpanGroupID(p.integer())
414*03ce13f7SAndroid Build Coastguard Worker				g.Extend = &extend
415*03ce13f7SAndroid Build Coastguard Worker			}
416*03ce13f7SAndroid Build Coastguard Worker		})
417*03ce13f7SAndroid Build Coastguard Worker		spangroups[SpanGroupID(groupIdx)] = g
418*03ce13f7SAndroid Build Coastguard Worker	})
419*03ce13f7SAndroid Build Coastguard Worker	return spangroups
420*03ce13f7SAndroid Build Coastguard Worker}
421*03ce13f7SAndroid Build Coastguard Worker
422*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parseCoverageMap(tcm TestCoverageMap) {
423*03ce13f7SAndroid Build Coastguard Worker	p.array(func(int) {
424*03ce13f7SAndroid Build Coastguard Worker		p.expect("[")
425*03ce13f7SAndroid Build Coastguard Worker		idx := TestIndex(p.integer())
426*03ce13f7SAndroid Build Coastguard Worker		p.expect(",")
427*03ce13f7SAndroid Build Coastguard Worker		p.parseCoverage(tcm.index(idx))
428*03ce13f7SAndroid Build Coastguard Worker		p.expect("]")
429*03ce13f7SAndroid Build Coastguard Worker	})
430*03ce13f7SAndroid Build Coastguard Worker}
431*03ce13f7SAndroid Build Coastguard Worker
432*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parseUncovered(tf *treeFile) {
433*03ce13f7SAndroid Build Coastguard Worker	p.array(func(int) {
434*03ce13f7SAndroid Build Coastguard Worker		tf.allSpans.Add(p.parseSpan())
435*03ce13f7SAndroid Build Coastguard Worker	})
436*03ce13f7SAndroid Build Coastguard Worker}
437*03ce13f7SAndroid Build Coastguard Worker
438*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) parseCoverage(tc *TestCoverage) {
439*03ce13f7SAndroid Build Coastguard Worker	p.dict(func(key string) {
440*03ce13f7SAndroid Build Coastguard Worker		switch key {
441*03ce13f7SAndroid Build Coastguard Worker		case "s":
442*03ce13f7SAndroid Build Coastguard Worker			p.array(func(int) {
443*03ce13f7SAndroid Build Coastguard Worker				id := SpanID(p.integer())
444*03ce13f7SAndroid Build Coastguard Worker				tc.Spans[id] = struct{}{}
445*03ce13f7SAndroid Build Coastguard Worker			})
446*03ce13f7SAndroid Build Coastguard Worker		case "g":
447*03ce13f7SAndroid Build Coastguard Worker			groupID := SpanGroupID(p.integer())
448*03ce13f7SAndroid Build Coastguard Worker			tc.Group = &groupID
449*03ce13f7SAndroid Build Coastguard Worker		case "c":
450*03ce13f7SAndroid Build Coastguard Worker			p.parseCoverageMap(tc.Children)
451*03ce13f7SAndroid Build Coastguard Worker		default:
452*03ce13f7SAndroid Build Coastguard Worker			p.fail("Unknown test key: '%s'", key)
453*03ce13f7SAndroid Build Coastguard Worker		}
454*03ce13f7SAndroid Build Coastguard Worker	})
455*03ce13f7SAndroid Build Coastguard Worker}
456*03ce13f7SAndroid Build Coastguard Worker
457*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) array(f func(idx int)) {
458*03ce13f7SAndroid Build Coastguard Worker	p.expect("[")
459*03ce13f7SAndroid Build Coastguard Worker	if p.match("]") {
460*03ce13f7SAndroid Build Coastguard Worker		return
461*03ce13f7SAndroid Build Coastguard Worker	}
462*03ce13f7SAndroid Build Coastguard Worker	idx := 0
463*03ce13f7SAndroid Build Coastguard Worker	for p.err == nil {
464*03ce13f7SAndroid Build Coastguard Worker		f(idx)
465*03ce13f7SAndroid Build Coastguard Worker		if !p.match(",") {
466*03ce13f7SAndroid Build Coastguard Worker			p.expect("]")
467*03ce13f7SAndroid Build Coastguard Worker			return
468*03ce13f7SAndroid Build Coastguard Worker		}
469*03ce13f7SAndroid Build Coastguard Worker		idx++
470*03ce13f7SAndroid Build Coastguard Worker	}
471*03ce13f7SAndroid Build Coastguard Worker	p.expect("]")
472*03ce13f7SAndroid Build Coastguard Worker}
473*03ce13f7SAndroid Build Coastguard Worker
474*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) dict(f func(key string)) {
475*03ce13f7SAndroid Build Coastguard Worker	p.expect("{")
476*03ce13f7SAndroid Build Coastguard Worker	if p.match("}") {
477*03ce13f7SAndroid Build Coastguard Worker		return
478*03ce13f7SAndroid Build Coastguard Worker	}
479*03ce13f7SAndroid Build Coastguard Worker	for p.err == nil {
480*03ce13f7SAndroid Build Coastguard Worker		key := p.str()
481*03ce13f7SAndroid Build Coastguard Worker		p.expect(`:`)
482*03ce13f7SAndroid Build Coastguard Worker		f(key)
483*03ce13f7SAndroid Build Coastguard Worker		if !p.match(",") {
484*03ce13f7SAndroid Build Coastguard Worker			p.expect("}")
485*03ce13f7SAndroid Build Coastguard Worker			return
486*03ce13f7SAndroid Build Coastguard Worker		}
487*03ce13f7SAndroid Build Coastguard Worker	}
488*03ce13f7SAndroid Build Coastguard Worker	p.expect("}")
489*03ce13f7SAndroid Build Coastguard Worker}
490*03ce13f7SAndroid Build Coastguard Worker
491*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) next() byte {
492*03ce13f7SAndroid Build Coastguard Worker	d := make([]byte, 1)
493*03ce13f7SAndroid Build Coastguard Worker	n, err := p.r.Read(d)
494*03ce13f7SAndroid Build Coastguard Worker	if err != nil || n != 1 {
495*03ce13f7SAndroid Build Coastguard Worker		p.err = err
496*03ce13f7SAndroid Build Coastguard Worker		return 0
497*03ce13f7SAndroid Build Coastguard Worker	}
498*03ce13f7SAndroid Build Coastguard Worker	return d[0]
499*03ce13f7SAndroid Build Coastguard Worker}
500*03ce13f7SAndroid Build Coastguard Worker
501*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) peek() byte {
502*03ce13f7SAndroid Build Coastguard Worker	d, err := p.r.Peek(1)
503*03ce13f7SAndroid Build Coastguard Worker	if err != nil {
504*03ce13f7SAndroid Build Coastguard Worker		p.err = err
505*03ce13f7SAndroid Build Coastguard Worker		return 0
506*03ce13f7SAndroid Build Coastguard Worker	}
507*03ce13f7SAndroid Build Coastguard Worker	return d[0]
508*03ce13f7SAndroid Build Coastguard Worker}
509*03ce13f7SAndroid Build Coastguard Worker
510*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) expect(s string) {
511*03ce13f7SAndroid Build Coastguard Worker	if p.err != nil {
512*03ce13f7SAndroid Build Coastguard Worker		return
513*03ce13f7SAndroid Build Coastguard Worker	}
514*03ce13f7SAndroid Build Coastguard Worker	d := make([]byte, len(s))
515*03ce13f7SAndroid Build Coastguard Worker	n, err := p.r.Read(d)
516*03ce13f7SAndroid Build Coastguard Worker	if err != nil {
517*03ce13f7SAndroid Build Coastguard Worker		p.err = err
518*03ce13f7SAndroid Build Coastguard Worker		return
519*03ce13f7SAndroid Build Coastguard Worker	}
520*03ce13f7SAndroid Build Coastguard Worker	got := string(d[:n])
521*03ce13f7SAndroid Build Coastguard Worker	if got != s {
522*03ce13f7SAndroid Build Coastguard Worker		p.fail("Expected '%v', got '%v'", s, got)
523*03ce13f7SAndroid Build Coastguard Worker		return
524*03ce13f7SAndroid Build Coastguard Worker	}
525*03ce13f7SAndroid Build Coastguard Worker}
526*03ce13f7SAndroid Build Coastguard Worker
527*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) match(s string) bool {
528*03ce13f7SAndroid Build Coastguard Worker	got, err := p.r.Peek(len(s))
529*03ce13f7SAndroid Build Coastguard Worker	if err != nil {
530*03ce13f7SAndroid Build Coastguard Worker		return false
531*03ce13f7SAndroid Build Coastguard Worker	}
532*03ce13f7SAndroid Build Coastguard Worker	if string(got) != s {
533*03ce13f7SAndroid Build Coastguard Worker		return false
534*03ce13f7SAndroid Build Coastguard Worker	}
535*03ce13f7SAndroid Build Coastguard Worker	p.r.Discard(len(s))
536*03ce13f7SAndroid Build Coastguard Worker	return true
537*03ce13f7SAndroid Build Coastguard Worker}
538*03ce13f7SAndroid Build Coastguard Worker
539*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) str() string {
540*03ce13f7SAndroid Build Coastguard Worker	p.expect(`"`)
541*03ce13f7SAndroid Build Coastguard Worker	sb := strings.Builder{}
542*03ce13f7SAndroid Build Coastguard Worker	for p.err == nil {
543*03ce13f7SAndroid Build Coastguard Worker		c := p.next()
544*03ce13f7SAndroid Build Coastguard Worker		if c == '"' {
545*03ce13f7SAndroid Build Coastguard Worker			return sb.String()
546*03ce13f7SAndroid Build Coastguard Worker		}
547*03ce13f7SAndroid Build Coastguard Worker		sb.WriteByte(c)
548*03ce13f7SAndroid Build Coastguard Worker	}
549*03ce13f7SAndroid Build Coastguard Worker	return ""
550*03ce13f7SAndroid Build Coastguard Worker}
551*03ce13f7SAndroid Build Coastguard Worker
552*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) integer() int {
553*03ce13f7SAndroid Build Coastguard Worker	sb := strings.Builder{}
554*03ce13f7SAndroid Build Coastguard Worker	for {
555*03ce13f7SAndroid Build Coastguard Worker		if c := p.peek(); c < '0' || c > '9' {
556*03ce13f7SAndroid Build Coastguard Worker			break
557*03ce13f7SAndroid Build Coastguard Worker		}
558*03ce13f7SAndroid Build Coastguard Worker		sb.WriteByte(p.next())
559*03ce13f7SAndroid Build Coastguard Worker	}
560*03ce13f7SAndroid Build Coastguard Worker	if sb.Len() == 0 {
561*03ce13f7SAndroid Build Coastguard Worker		p.fail("Expected integer, got '%c'", p.peek())
562*03ce13f7SAndroid Build Coastguard Worker		return 0
563*03ce13f7SAndroid Build Coastguard Worker	}
564*03ce13f7SAndroid Build Coastguard Worker	i, err := strconv.Atoi(sb.String())
565*03ce13f7SAndroid Build Coastguard Worker	if err != nil {
566*03ce13f7SAndroid Build Coastguard Worker		p.fail("Failed to parse integer: %v", err)
567*03ce13f7SAndroid Build Coastguard Worker		return 0
568*03ce13f7SAndroid Build Coastguard Worker	}
569*03ce13f7SAndroid Build Coastguard Worker	return i
570*03ce13f7SAndroid Build Coastguard Worker}
571*03ce13f7SAndroid Build Coastguard Worker
572*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) double() float64 {
573*03ce13f7SAndroid Build Coastguard Worker	sb := strings.Builder{}
574*03ce13f7SAndroid Build Coastguard Worker	for {
575*03ce13f7SAndroid Build Coastguard Worker		if c := p.peek(); c != '.' && (c < '0' || c > '9') {
576*03ce13f7SAndroid Build Coastguard Worker			break
577*03ce13f7SAndroid Build Coastguard Worker		}
578*03ce13f7SAndroid Build Coastguard Worker		sb.WriteByte(p.next())
579*03ce13f7SAndroid Build Coastguard Worker	}
580*03ce13f7SAndroid Build Coastguard Worker	if sb.Len() == 0 {
581*03ce13f7SAndroid Build Coastguard Worker		p.fail("Expected double, got '%c'", p.peek())
582*03ce13f7SAndroid Build Coastguard Worker		return 0
583*03ce13f7SAndroid Build Coastguard Worker	}
584*03ce13f7SAndroid Build Coastguard Worker	f, err := strconv.ParseFloat(sb.String(), 64)
585*03ce13f7SAndroid Build Coastguard Worker	if err != nil {
586*03ce13f7SAndroid Build Coastguard Worker		p.fail("Failed to parse double: %v", err)
587*03ce13f7SAndroid Build Coastguard Worker		return 0
588*03ce13f7SAndroid Build Coastguard Worker	}
589*03ce13f7SAndroid Build Coastguard Worker	return f
590*03ce13f7SAndroid Build Coastguard Worker}
591*03ce13f7SAndroid Build Coastguard Worker
592*03ce13f7SAndroid Build Coastguard Workerfunc (p *parser) fail(msg string, args ...interface{}) {
593*03ce13f7SAndroid Build Coastguard Worker	if p.err == nil {
594*03ce13f7SAndroid Build Coastguard Worker		msg = fmt.Sprintf(msg, args...)
595*03ce13f7SAndroid Build Coastguard Worker		stack := string(debug.Stack())
596*03ce13f7SAndroid Build Coastguard Worker		p.err = fmt.Errorf("%v\nCallstack:\n%v", msg, stack)
597*03ce13f7SAndroid Build Coastguard Worker	}
598*03ce13f7SAndroid Build Coastguard Worker}
599