aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/cover/html.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/cover/html.go')
-rw-r--r--pkg/cover/html.go219
1 files changed, 219 insertions, 0 deletions
diff --git a/pkg/cover/html.go b/pkg/cover/html.go
index b353029a3..1757964b5 100644
--- a/pkg/cover/html.go
+++ b/pkg/cover/html.go
@@ -103,6 +103,165 @@ func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog) error {
return coverTemplate.Execute(w, d)
}
+type fileStats struct {
+ Name string
+ CoveredLines int
+ TotalLines int
+ CoveredPCs int
+ TotalPCs int
+ TotalFunctions int
+ CoveredPCsInFunctions int
+ TotalPCsInFunctions int
+}
+
+var csvFilesHeader = []string{
+ "Filename",
+ "CoveredLines",
+ "TotalLines",
+ "CoveredPCs",
+ "TotalPCs",
+ "TotalFunctions",
+ "CoveredPCsInFunctions",
+ "TotalPCsInFunctions",
+}
+
+func (rg *ReportGenerator) convertToStats(progs []Prog) ([]fileStats, error) {
+ files, err := rg.prepareFileMap(progs)
+ if err != nil {
+ return nil, err
+ }
+
+ var data []fileStats
+ for fname, file := range files {
+ lines, err := parseFile(file.filename)
+ if err != nil {
+ fmt.Printf("failed to open/locate %s\n", file.filename)
+ continue
+ }
+ totalFuncs := len(file.functions)
+ var coveredInFunc int
+ var pcsInFunc int
+ for _, function := range file.functions {
+ coveredInFunc += function.covered
+ pcsInFunc += function.pcs
+ }
+ totalLines := len(lines)
+ var coveredLines int
+ for _, line := range file.lines {
+ if len(line.progCount) != 0 {
+ coveredLines++
+ }
+ }
+ data = append(data, fileStats{
+ Name: fname,
+ CoveredLines: coveredLines,
+ TotalLines: totalLines,
+ CoveredPCs: file.coveredPCs,
+ TotalPCs: file.totalPCs,
+ TotalFunctions: totalFuncs,
+ CoveredPCsInFunctions: coveredInFunc,
+ TotalPCsInFunctions: pcsInFunc,
+ })
+ }
+
+ return data, nil
+}
+
+func (rg *ReportGenerator) DoCSVFiles(w io.Writer, progs []Prog) error {
+ data, err := rg.convertToStats(progs)
+ if err != nil {
+ return err
+ }
+
+ sort.SliceStable(data, func(i, j int) bool {
+ return data[i].Name < data[j].Name
+ })
+
+ writer := csv.NewWriter(w)
+ defer writer.Flush()
+ if err := writer.Write(csvFilesHeader); err != nil {
+ return err
+ }
+
+ var d [][]string
+ for _, dt := range data {
+ d = append(d, []string{
+ dt.Name,
+ strconv.Itoa(dt.CoveredLines),
+ strconv.Itoa(dt.TotalLines),
+ strconv.Itoa(dt.CoveredPCs),
+ strconv.Itoa(dt.TotalPCs),
+ strconv.Itoa(dt.TotalFunctions),
+ strconv.Itoa(dt.CoveredPCsInFunctions),
+ strconv.Itoa(dt.TotalPCsInFunctions),
+ })
+ }
+ return writer.WriteAll(d)
+}
+
+func groupCoverByFilePrefixes(datas []fileStats, subsystems []Subsystem) map[string]map[string]string {
+ d := make(map[string]map[string]string)
+
+ for _, subsystem := range subsystems {
+ var coveredLines int
+ var totalLines int
+ var coveredPCsInFile int
+ var totalPCsInFile int
+ var totalFuncs int
+ var coveredPCsInFuncs int
+ var pcsInFuncs int
+ var percentLines float64
+ var percentPCsInFile float64
+ var percentPCsInFunc float64
+
+ for _, path := range subsystem.Paths {
+ for _, data := range datas {
+ if !strings.HasPrefix(data.Name, path) {
+ continue
+ }
+ coveredLines += data.CoveredLines
+ totalLines += data.TotalLines
+ coveredPCsInFile += data.CoveredPCs
+ totalPCsInFile += data.TotalPCs
+ totalFuncs += data.TotalFunctions
+ coveredPCsInFuncs += data.CoveredPCsInFunctions
+ pcsInFuncs += data.TotalPCsInFunctions
+ }
+ }
+
+ if totalLines != 0 {
+ percentLines = 100.0 * float64(coveredLines) / float64(totalLines)
+ }
+ if totalPCsInFile != 0 {
+ percentPCsInFile = 100.0 * float64(coveredPCsInFile) / float64(totalPCsInFile)
+ }
+ if pcsInFuncs != 0 {
+ percentPCsInFunc = 100.0 * float64(coveredPCsInFuncs) / float64(pcsInFuncs)
+ }
+
+ d[subsystem.Name] = map[string]string{
+ "subsystem": subsystem.Name,
+ "lines": fmt.Sprintf("%v / %v / %.2f%%", coveredLines, totalLines, percentLines),
+ "PCsInFiles": fmt.Sprintf("%v / %v / %.2f%%", coveredPCsInFile, totalPCsInFile, percentPCsInFile),
+ "totalFuncs": strconv.Itoa(totalFuncs),
+ "PCsInFuncs": fmt.Sprintf("%v / %v / %.2f%%", coveredPCsInFuncs, pcsInFuncs, percentPCsInFunc),
+ }
+ }
+
+ return d
+}
+
+func (rg *ReportGenerator) DoHTMLTable(w io.Writer, progs []Prog) error {
+ data, err := rg.convertToStats(progs)
+ if err != nil {
+ return err
+ }
+
+ d := groupCoverByFilePrefixes(data, rg.subsystem)
+
+ return coverTableTemplate.Execute(w, d)
+}
+
var csvHeader = []string{
"Filename",
"Function",
@@ -557,3 +716,63 @@ var coverTemplate = template.Must(template.New("").Parse(`
{{end}}
{{end}}
`))
+
+var coverTableTemplate = template.Must(template.New("coverTable").Parse(`
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <style>
+ body {
+ background: white;
+ }
+ #content {
+ color: rgb(70, 70, 70);
+ margin-top: 50px;
+ }
+ th, td {
+ text-align: left;
+ border: 1px solid black;
+ }
+ th {
+ background: gray;
+ }
+ tr:nth-child(2n+1) {
+ background: #CCC
+ }
+ table {
+ border-collapse: collapse;
+ border: 1px solid black;
+ margin-bottom: 20px;
+ }
+ </style>
+ </head>
+ <body>
+ <div>
+ <table>
+ <thead>
+ <tr>
+ <th>Subsystem</th>
+ <th>Covered / Total Lines / %</th>
+ <th>Covered / Total PCs in File / %</th>
+ <th>Covered / Total PCs in Function / %</th>
+ <th>Covered Functions</th>
+ </tr>
+ </thead>
+ <tbody id="content">
+ {{range $i, $p := .}}
+ <tr>
+ <td>{{$p.subsystem}}</td>
+ <td>{{$p.lines}}</td>
+ <td>{{$p.PCsInFiles}}</td>
+ <td>{{$p.PCsInFuncs}}</td>
+ <td>{{$p.totalFuncs}}</td>
+ </tr>
+ {{end}}
+ </tbody>
+ </table>
+ </div>
+ </body>
+</html>
+
+`))