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