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