/
html_report.go
134 lines (109 loc) · 3.43 KB
/
html_report.go
1
2
3
4
5
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package report
import (
"fmt"
"html/template"
"os"
"strings"
filemanagement "github.com/vieolo/file-management"
"github.com/vieolo/shiraz/utils"
terminalutils "github.com/vieolo/terminal-utils"
"golang.org/x/exp/slices"
"golang.org/x/tools/cover"
)
// Entry point of report generation
//
// It takes the path of the `.out` file, analyze it, and generate the HTML reports
func GenHTMLReport(outPath string, conf utils.ShirazConfig) error {
// Parsing the `.out` file. Each profile is the representation of the analysis of a single file
// This function is the default golang function
profiles, pErr := cover.ParseProfiles(outPath)
if pErr != nil {
return pErr
}
// Preparing the folders
//
// These folders represent the structure of the project
// The generated HTML files will be placed in a similar structure to the
// actual project. This placement makes it easier for the developer to understand the
// degree of the coverage
//
// Each project has its own coverage percentage which is the average coverage of its files
folders := make([]ReportFolder, 0)
// Getting the directories that will be used in coverage
dirs, err := findPkgs(profiles)
if err != nil {
return err
}
for _, profile := range profiles {
fn := profile.FileName
if slices.Contains(conf.IgnoreFiles, fn) {
continue
}
sp := strings.Split(fn, "/")
folN := strings.Join(sp[:len(sp)-1], "/")
if slices.Contains(conf.IgnoreFolders, folN) {
continue
}
// Finding the file in the folders
file, err := findFile(dirs, fn)
if err != nil {
return err
}
// Getting the relative path of the folder of the file
relativePath, absolutePath, _ := getFolderPath(file)
// Reading the contents of the file and generating an HTML detail
src, err := os.ReadFile(file)
if err != nil {
return fmt.Errorf("can't read %q: %v", fn, err)
}
var buf strings.Builder
err = htmlGen(&buf, src, profile.Boundaries(src))
if err != nil {
return err
}
thisFolder, created, index := findOrCreateFolder(folders, relativePath, absolutePath)
coverage, coveredBlock, totalBlock := percentCovered(profile)
thisFolder.AddFile(ReportFile{
Name: fn,
Path: file,
Body: template.HTML(buf.String()),
Coverage: coverage,
BlockCovered: coveredBlock,
BlockTotal: totalBlock,
})
if created {
folders = append(folders, thisFolder)
} else {
folders[index] = thisFolder
}
}
var baseFolder ReportFolder
for i := 0; i < len(folders); i++ {
folder := folders[i]
folder.Subfolders = append(folder.Subfolders, getSubfolders(folder, folders)...)
folders[i] = folder
if folder.Name == "" {
baseFolder = folder
}
}
// Writing the generated HTML files
outFolder := strings.Replace(outPath, "/coverage.out", "", 1)
for _, fol := range folders {
prePath := outFolder + "/" + fol.RelativePath
filemanagement.CreateDirIfNotExists(prePath, 0777)
newFileName := fmt.Sprintf("%v/index.html", prePath)
iwe := os.WriteFile(newFileName, []byte(generateIndexHTMLFile(fol)), 0777)
if iwe != nil {
terminalutils.PrintError(iwe.Error())
}
for _, file := range fol.Files {
sp := strings.Split(file.Name, "/")
newFileName := fmt.Sprintf("%v/%v.html", prePath, strings.Replace(sp[len(sp)-1], ".go", "", 1))
we := os.WriteFile(newFileName, []byte(generateContentHTMLFile(baseFolder.AbsolutePath, file)), 0777)
if we != nil {
terminalutils.PrintError(we.Error())
}
}
}
return nil
}