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