1
2
3
4
5 package cformat
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 import (
34 "fmt"
35 "internal/coverage"
36 "internal/coverage/cmerge"
37 "io"
38 "sort"
39 "text/tabwriter"
40 )
41
42 type Formatter struct {
43
44 pm map[string]*pstate
45
46 pkg string
47
48 p *pstate
49
50 cm coverage.CounterMode
51 }
52
53
54
55
56
57 type pstate struct {
58
59 funcs []fnfile
60
61 funcTable map[fnfile]uint32
62
63
64 unitTable map[extcu]uint32
65 }
66
67
68 type extcu struct {
69 fnfid uint32
70 coverage.CoverableUnit
71 }
72
73
74 type fnfile struct {
75 file string
76 fname string
77 lit bool
78 }
79
80 func NewFormatter(cm coverage.CounterMode) *Formatter {
81 return &Formatter{
82 pm: make(map[string]*pstate),
83 cm: cm,
84 }
85 }
86
87
88
89
90
91 func (fm *Formatter) SetPackage(importpath string) {
92 if importpath == fm.pkg {
93 return
94 }
95 fm.pkg = importpath
96 ps, ok := fm.pm[importpath]
97 if !ok {
98 ps = new(pstate)
99 fm.pm[importpath] = ps
100 ps.unitTable = make(map[extcu]uint32)
101 ps.funcTable = make(map[fnfile]uint32)
102 }
103 fm.p = ps
104 }
105
106
107
108
109 func (fm *Formatter) AddUnit(file string, fname string, isfnlit bool, unit coverage.CoverableUnit, count uint32) {
110 if fm.p == nil {
111 panic("AddUnit invoked before SetPackage")
112 }
113 fkey := fnfile{file: file, fname: fname, lit: isfnlit}
114 idx, ok := fm.p.funcTable[fkey]
115 if !ok {
116 idx = uint32(len(fm.p.funcs))
117 fm.p.funcs = append(fm.p.funcs, fkey)
118 fm.p.funcTable[fkey] = idx
119 }
120 ukey := extcu{fnfid: idx, CoverableUnit: unit}
121 pcount := fm.p.unitTable[ukey]
122 var result uint32
123 if fm.cm == coverage.CtrModeSet {
124 if count != 0 || pcount != 0 {
125 result = 1
126 }
127 } else {
128
129 result, _ = cmerge.SaturatingAdd(pcount, count)
130 }
131 fm.p.unitTable[ukey] = result
132 }
133
134
135
136
137
138 func (p *pstate) sortUnits(units []extcu) {
139 sort.Slice(units, func(i, j int) bool {
140 ui := units[i]
141 uj := units[j]
142 ifile := p.funcs[ui.fnfid].file
143 jfile := p.funcs[uj.fnfid].file
144 if ifile != jfile {
145 return ifile < jfile
146 }
147
148
149 if units[i].StLine != units[j].StLine {
150 return units[i].StLine < units[j].StLine
151 }
152 if units[i].EnLine != units[j].EnLine {
153 return units[i].EnLine < units[j].EnLine
154 }
155 if units[i].StCol != units[j].StCol {
156 return units[i].StCol < units[j].StCol
157 }
158 if units[i].EnCol != units[j].EnCol {
159 return units[i].EnCol < units[j].EnCol
160 }
161 return units[i].NxStmts < units[j].NxStmts
162 })
163 }
164
165
166
167
168
169
170 func (fm *Formatter) EmitTextual(w io.Writer) error {
171 if fm.cm == coverage.CtrModeInvalid {
172 panic("internal error, counter mode unset")
173 }
174 if _, err := fmt.Fprintf(w, "mode: %s\n", fm.cm.String()); err != nil {
175 return err
176 }
177 pkgs := make([]string, 0, len(fm.pm))
178 for importpath := range fm.pm {
179 pkgs = append(pkgs, importpath)
180 }
181 sort.Strings(pkgs)
182 for _, importpath := range pkgs {
183 p := fm.pm[importpath]
184 units := make([]extcu, 0, len(p.unitTable))
185 for u := range p.unitTable {
186 units = append(units, u)
187 }
188 p.sortUnits(units)
189 for _, u := range units {
190 count := p.unitTable[u]
191 file := p.funcs[u.fnfid].file
192 if _, err := fmt.Fprintf(w, "%s:%d.%d,%d.%d %d %d\n",
193 file, u.StLine, u.StCol,
194 u.EnLine, u.EnCol, u.NxStmts, count); err != nil {
195 return err
196 }
197 }
198 }
199 return nil
200 }
201
202
203 func (fm *Formatter) EmitPercent(w io.Writer, covpkgs string, noteEmpty bool, aggregate bool) error {
204 pkgs := make([]string, 0, len(fm.pm))
205 for importpath := range fm.pm {
206 pkgs = append(pkgs, importpath)
207 }
208
209 rep := func(cov, tot uint64) error {
210 if tot != 0 {
211 if _, err := fmt.Fprintf(w, "coverage: %.1f%% of statements%s\n",
212 100.0*float64(cov)/float64(tot), covpkgs); err != nil {
213 return err
214 }
215 } else if noteEmpty {
216 if _, err := fmt.Fprintf(w, "coverage: [no statements]\n"); err != nil {
217 return err
218 }
219 }
220 return nil
221 }
222
223 sort.Strings(pkgs)
224 var totalStmts, coveredStmts uint64
225 for _, importpath := range pkgs {
226 p := fm.pm[importpath]
227 if !aggregate {
228 totalStmts, coveredStmts = 0, 0
229 }
230 for unit, count := range p.unitTable {
231 nx := uint64(unit.NxStmts)
232 totalStmts += nx
233 if count != 0 {
234 coveredStmts += nx
235 }
236 }
237 if !aggregate {
238 if _, err := fmt.Fprintf(w, "\t%s\t\t", importpath); err != nil {
239 return err
240 }
241 if err := rep(coveredStmts, totalStmts); err != nil {
242 return err
243 }
244 }
245 }
246 if aggregate {
247 if err := rep(coveredStmts, totalStmts); err != nil {
248 return err
249 }
250 }
251
252 return nil
253 }
254
255
256
257
258
259
260
261
262 func (fm *Formatter) EmitFuncs(w io.Writer) error {
263 if fm.cm == coverage.CtrModeInvalid {
264 panic("internal error, counter mode unset")
265 }
266 perc := func(covered, total uint64) float64 {
267 if total == 0 {
268 total = 1
269 }
270 return 100.0 * float64(covered) / float64(total)
271 }
272 tabber := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
273 defer tabber.Flush()
274 allStmts := uint64(0)
275 covStmts := uint64(0)
276
277 pkgs := make([]string, 0, len(fm.pm))
278 for importpath := range fm.pm {
279 pkgs = append(pkgs, importpath)
280 }
281 sort.Strings(pkgs)
282
283
284 for _, importpath := range pkgs {
285 p := fm.pm[importpath]
286 if len(p.unitTable) == 0 {
287 continue
288 }
289 units := make([]extcu, 0, len(p.unitTable))
290 for u := range p.unitTable {
291 units = append(units, u)
292 }
293
294
295
296
297
298 p.sortUnits(units)
299 fname := ""
300 ffile := ""
301 flit := false
302 var fline uint32
303 var cstmts, tstmts uint64
304 captureFuncStart := func(u extcu) {
305 fname = p.funcs[u.fnfid].fname
306 ffile = p.funcs[u.fnfid].file
307 flit = p.funcs[u.fnfid].lit
308 fline = u.StLine
309 }
310 emitFunc := func(u extcu) error {
311
312
313 if !flit {
314 if _, err := fmt.Fprintf(tabber, "%s:%d:\t%s\t%.1f%%\n",
315 ffile, fline, fname, perc(cstmts, tstmts)); err != nil {
316 return err
317 }
318 }
319 captureFuncStart(u)
320 allStmts += tstmts
321 covStmts += cstmts
322 tstmts = 0
323 cstmts = 0
324 return nil
325 }
326 for k, u := range units {
327 if k == 0 {
328 captureFuncStart(u)
329 } else {
330 if fname != p.funcs[u.fnfid].fname {
331
332 if err := emitFunc(u); err != nil {
333 return err
334 }
335 }
336 }
337 tstmts += uint64(u.NxStmts)
338 count := p.unitTable[u]
339 if count != 0 {
340 cstmts += uint64(u.NxStmts)
341 }
342 }
343 if err := emitFunc(extcu{}); err != nil {
344 return err
345 }
346 }
347 if _, err := fmt.Fprintf(tabber, "%s\t%s\t%.1f%%\n",
348 "total", "(statements)", perc(covStmts, allStmts)); err != nil {
349 return err
350 }
351 return nil
352 }
353
View as plain text