| | |
| | |
| | |
| |
|
| | package cformat |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import ( |
| | "cmp" |
| | "fmt" |
| | "internal/coverage" |
| | "internal/coverage/cmerge" |
| | "io" |
| | "maps" |
| | "slices" |
| | "sort" |
| | "strings" |
| | "text/tabwriter" |
| | ) |
| |
|
| | type Formatter struct { |
| | |
| | pm map[string]*pstate |
| | |
| | pkg string |
| | |
| | p *pstate |
| | |
| | cm coverage.CounterMode |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | type pstate struct { |
| | |
| | funcs []fnfile |
| | |
| | funcTable map[fnfile]uint32 |
| |
|
| | |
| | unitTable map[extcu]uint32 |
| | } |
| |
|
| | |
| | type extcu struct { |
| | fnfid uint32 |
| | coverage.CoverableUnit |
| | } |
| |
|
| | |
| | type fnfile struct { |
| | file string |
| | fname string |
| | lit bool |
| | } |
| |
|
| | func NewFormatter(cm coverage.CounterMode) *Formatter { |
| | return &Formatter{ |
| | pm: make(map[string]*pstate), |
| | cm: cm, |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func (fm *Formatter) SetPackage(importpath string) { |
| | if importpath == fm.pkg { |
| | return |
| | } |
| | fm.pkg = importpath |
| | ps, ok := fm.pm[importpath] |
| | if !ok { |
| | ps = new(pstate) |
| | fm.pm[importpath] = ps |
| | ps.unitTable = make(map[extcu]uint32) |
| | ps.funcTable = make(map[fnfile]uint32) |
| | } |
| | fm.p = ps |
| | } |
| |
|
| | |
| | |
| | |
| | func (fm *Formatter) AddUnit(file string, fname string, isfnlit bool, unit coverage.CoverableUnit, count uint32) { |
| | if fm.p == nil { |
| | panic("AddUnit invoked before SetPackage") |
| | } |
| | fkey := fnfile{file: file, fname: fname, lit: isfnlit} |
| | idx, ok := fm.p.funcTable[fkey] |
| | if !ok { |
| | idx = uint32(len(fm.p.funcs)) |
| | fm.p.funcs = append(fm.p.funcs, fkey) |
| | fm.p.funcTable[fkey] = idx |
| | } |
| | ukey := extcu{fnfid: idx, CoverableUnit: unit} |
| | pcount := fm.p.unitTable[ukey] |
| | var result uint32 |
| | if fm.cm == coverage.CtrModeSet { |
| | if count != 0 || pcount != 0 { |
| | result = 1 |
| | } |
| | } else { |
| | |
| | result, _ = cmerge.SaturatingAdd(pcount, count) |
| | } |
| | fm.p.unitTable[ukey] = result |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func (p *pstate) sortUnits(units []extcu) { |
| | slices.SortFunc(units, func(ui, uj extcu) int { |
| | ifile := p.funcs[ui.fnfid].file |
| | jfile := p.funcs[uj.fnfid].file |
| | if r := strings.Compare(ifile, jfile); r != 0 { |
| | return r |
| | } |
| | |
| | |
| | if r := cmp.Compare(ui.StLine, uj.StLine); r != 0 { |
| | return r |
| | } |
| | if r := cmp.Compare(ui.EnLine, uj.EnLine); r != 0 { |
| | return r |
| | } |
| | if r := cmp.Compare(ui.StCol, uj.StCol); r != 0 { |
| | return r |
| | } |
| | if r := cmp.Compare(ui.EnCol, uj.EnCol); r != 0 { |
| | return r |
| | } |
| | return cmp.Compare(ui.NxStmts, uj.NxStmts) |
| | }) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func (fm *Formatter) EmitTextual(pkgs []string, w io.Writer) error { |
| | if fm.cm == coverage.CtrModeInvalid { |
| | panic("internal error, counter mode unset") |
| | } |
| | if len(pkgs) == 0 { |
| | pkgs = make([]string, 0, len(fm.pm)) |
| | for importpath := range fm.pm { |
| | pkgs = append(pkgs, importpath) |
| | } |
| | } |
| | if _, err := fmt.Fprintf(w, "mode: %s\n", fm.cm.String()); err != nil { |
| | return err |
| | } |
| | sort.Strings(pkgs) |
| | for _, importpath := range pkgs { |
| | p := fm.pm[importpath] |
| | if p == nil { |
| | continue |
| | } |
| | units := make([]extcu, 0, len(p.unitTable)) |
| | for u := range p.unitTable { |
| | units = append(units, u) |
| | } |
| | p.sortUnits(units) |
| | for _, u := range units { |
| | count := p.unitTable[u] |
| | file := p.funcs[u.fnfid].file |
| | if _, err := fmt.Fprintf(w, "%s:%d.%d,%d.%d %d %d\n", |
| | file, u.StLine, u.StCol, |
| | u.EnLine, u.EnCol, u.NxStmts, count); err != nil { |
| | return err |
| | } |
| | } |
| | } |
| | return nil |
| | } |
| |
|
| | |
| | |
| | |
| | func (fm *Formatter) EmitPercent(w io.Writer, pkgs []string, inpkgs string, noteEmpty bool, aggregate bool) error { |
| | if len(pkgs) == 0 { |
| | pkgs = make([]string, 0, len(fm.pm)) |
| | for importpath := range fm.pm { |
| | pkgs = append(pkgs, importpath) |
| | } |
| | } |
| |
|
| | rep := func(cov, tot uint64) error { |
| | if tot != 0 { |
| | if _, err := fmt.Fprintf(w, "coverage: %.1f%% of statements%s\n", |
| | 100.0*float64(cov)/float64(tot), inpkgs); err != nil { |
| | return err |
| | } |
| | } else if noteEmpty { |
| | if _, err := fmt.Fprintf(w, "coverage: [no statements]\n"); err != nil { |
| | return err |
| | } |
| | } |
| | return nil |
| | } |
| |
|
| | slices.Sort(pkgs) |
| | var totalStmts, coveredStmts uint64 |
| | for _, importpath := range pkgs { |
| | p := fm.pm[importpath] |
| | if p == nil { |
| | continue |
| | } |
| | if !aggregate { |
| | totalStmts, coveredStmts = 0, 0 |
| | } |
| | for unit, count := range p.unitTable { |
| | nx := uint64(unit.NxStmts) |
| | totalStmts += nx |
| | if count != 0 { |
| | coveredStmts += nx |
| | } |
| | } |
| | if !aggregate { |
| | if _, err := fmt.Fprintf(w, "\t%s\t\t", importpath); err != nil { |
| | return err |
| | } |
| | if err := rep(coveredStmts, totalStmts); err != nil { |
| | return err |
| | } |
| | } |
| | } |
| | if aggregate { |
| | if err := rep(coveredStmts, totalStmts); err != nil { |
| | return err |
| | } |
| | } |
| |
|
| | return nil |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func (fm *Formatter) EmitFuncs(w io.Writer) error { |
| | if fm.cm == coverage.CtrModeInvalid { |
| | panic("internal error, counter mode unset") |
| | } |
| | perc := func(covered, total uint64) float64 { |
| | if total == 0 { |
| | total = 1 |
| | } |
| | return 100.0 * float64(covered) / float64(total) |
| | } |
| | tabber := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) |
| | defer tabber.Flush() |
| | allStmts := uint64(0) |
| | covStmts := uint64(0) |
| |
|
| | |
| | for _, importpath := range slices.Sorted(maps.Keys(fm.pm)) { |
| | p := fm.pm[importpath] |
| | if len(p.unitTable) == 0 { |
| | continue |
| | } |
| | units := make([]extcu, 0, len(p.unitTable)) |
| | for u := range p.unitTable { |
| | units = append(units, u) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | p.sortUnits(units) |
| | fname := "" |
| | ffile := "" |
| | flit := false |
| | var fline uint32 |
| | var cstmts, tstmts uint64 |
| | captureFuncStart := func(u extcu) { |
| | fname = p.funcs[u.fnfid].fname |
| | ffile = p.funcs[u.fnfid].file |
| | flit = p.funcs[u.fnfid].lit |
| | fline = u.StLine |
| | } |
| | emitFunc := func(u extcu) error { |
| | |
| | |
| | if !flit { |
| | if _, err := fmt.Fprintf(tabber, "%s:%d:\t%s\t%.1f%%\n", |
| | ffile, fline, fname, perc(cstmts, tstmts)); err != nil { |
| | return err |
| | } |
| | } |
| | captureFuncStart(u) |
| | allStmts += tstmts |
| | covStmts += cstmts |
| | tstmts = 0 |
| | cstmts = 0 |
| | return nil |
| | } |
| | for k, u := range units { |
| | if k == 0 { |
| | captureFuncStart(u) |
| | } else { |
| | if fname != p.funcs[u.fnfid].fname { |
| | |
| | if err := emitFunc(u); err != nil { |
| | return err |
| | } |
| | } |
| | } |
| | tstmts += uint64(u.NxStmts) |
| | count := p.unitTable[u] |
| | if count != 0 { |
| | cstmts += uint64(u.NxStmts) |
| | } |
| | } |
| | if err := emitFunc(extcu{}); err != nil { |
| | return err |
| | } |
| | } |
| | if _, err := fmt.Fprintf(tabber, "%s\t%s\t%.1f%%\n", |
| | "total", "(statements)", perc(covStmts, allStmts)); err != nil { |
| | return err |
| | } |
| | return nil |
| | } |
| |
|