Skip to content

cmd/cover: allow code coverage styling #65056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/cmd/cover/cover.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ var (
output = flag.String("o", "", "file for output")
outfilelist = flag.String("outfilelist", "", "file containing list of output files (one per line) if -pkgcfg is in use")
htmlOut = flag.String("html", "", "generate HTML representation of coverage profile")
css = flag.String("css", "", "CSS file for HTML representation of coverage profile")
funcOut = flag.String("func", "", "output coverage profile information for each function")
pkgcfg = flag.String("pkgcfg", "", "enable full-package instrumentation mode using params from specified config file")
pkgconfig covcmd.CoverPkgConfig
Expand Down Expand Up @@ -110,7 +111,7 @@ func main() {

// Output HTML or function coverage information.
if *htmlOut != "" {
err = htmlOutput(profile, *output)
err = htmlOutput(profile, *css, *output)
} else {
err = funcOutput(profile, *output)
}
Expand All @@ -131,6 +132,10 @@ func parseFlags() error {
profile = *funcOut
}

if *css != "" && *htmlOut == "" {
return fmt.Errorf("-css option requires -html")
}

// Must either display a profile or rewrite Go source.
if (profile == "") == (*mode == "") {
return fmt.Errorf("too many options")
Expand Down
60 changes: 58 additions & 2 deletions src/cmd/cover/cover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ func TestCoverWithToolExec(t *testing.T) {
t.Run("HtmlUnformatted", func(t *testing.T) {
testHtmlUnformatted(t, toolexecArg)
})

t.Run("HtmlCss", func(t *testing.T) {
testHtmlCss(t, toolexecArg)
})

t.Run("FuncWithDuplicateLines", func(t *testing.T) {
testFuncWithDuplicateLines(t, toolexecArg)
})
Expand Down Expand Up @@ -375,12 +380,18 @@ func testCoverHTML(t *testing.T, toolexecArg string) {
cmd = testenv.Command(t, testcover(t), "-html", htmlProfile, "-o", htmlHTML)
run(cmd, t)

// Extract the parts of the HTML with comment markers,
// and compare against a golden file.
entireHTML, err := os.ReadFile(htmlHTML)
if err != nil {
t.Fatal(err)
}

// Check default CSS.
if !bytes.Contains(entireHTML, []byte(`.cov0 { color: rgb(192, 0, 0) }`)) {
t.Error("Default CSS not found in HTML")
}

// Extract the parts of the HTML with comment markers,
// and compare against a golden file.
var out strings.Builder
scan := bufio.NewScanner(bytes.NewReader(entireHTML))
in := false
Expand Down Expand Up @@ -480,6 +491,51 @@ lab:
run(cmd, t)
}

// Check that cover produces HTML with specified CSS.
// Issue #64954.
func testHtmlCss(t *testing.T, toolexecArg string) {
testenv.MustHaveGoRun(t)
dir := tempDir(t)

t.Parallel()

const cssContent = "/* This is a test style sheet */\n"
if err := os.WriteFile(filepath.Join(dir, "html.css"), []byte(cssContent), 0666); err != nil {
t.Fatal(err)
}

// go test -coverprofile testdata/html/html.cov cmd/cover/testdata/html
htmlProfile := filepath.Join(dir, "html.cov")
cmd := testenv.Command(t, testenv.GoToolPath(t), "test", toolexecArg, "-coverprofile", htmlProfile, "cmd/cover/testdata/html")
cmd.Env = append(cmd.Environ(), "CMDCOVER_TOOLEXEC=true")
run(cmd, t)

// testcover -html testdata/html/html.cov -css testdata/html/html.css -o testdata/html/html.html
htmlCSS := filepath.Join(dir, "html.css")
htmlHTML := filepath.Join(dir, "html.html")
cmd = testenv.Command(t, testcover(t), "-html", htmlProfile, "-css", htmlCSS, "-o", htmlHTML)
run(cmd, t)

entireHTML, err := os.ReadFile(htmlHTML)
if err != nil {
t.Fatal(err)
}

defaultCssPos := bytes.Index(entireHTML, []byte(`.cov0 { color: rgb(192, 0, 0) }`))
if defaultCssPos == -1 {
t.Error("Default CSS not found in HTML")
}

cssPos := bytes.Index(entireHTML, []byte(cssContent))
if cssPos == -1 {
t.Error("Custom CSS not found in HTML")
}

if cssPos <= defaultCssPos {
t.Error("Custom CSS does not override default CSS")
}
}

// lineDupContents becomes linedup.go in testFuncWithDuplicateLines.
const lineDupContents = `
package linedup
Expand Down
25 changes: 21 additions & 4 deletions src/cmd/cover/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
// htmlOutput reads the profile data from profile and generates an HTML
// coverage report, writing it to outfile. If outfile is empty,
// it writes the report to a temporary file and opens it in a web browser.
func htmlOutput(profile, outfile string) error {
func htmlOutput(profile, cssfile, outfile string) error {
profiles, err := cover.ParseProfiles(profile)
if err != nil {
return err
Expand Down Expand Up @@ -59,6 +59,19 @@ func htmlOutput(profile, outfile string) error {
})
}

tmpl := htmlTemplate
if cssfile != "" {
css, err := os.ReadFile(cssfile)
if err != nil {
return err
}
tmpl.Funcs(template.FuncMap{
"css": func() template.CSS {
return defaultCss() + template.CSS(css)
},
})
}

var out *os.File
if outfile == "" {
var dir string
Expand All @@ -73,7 +86,7 @@ func htmlOutput(profile, outfile string) error {
if err != nil {
return err
}
err = htmlTemplate.Execute(out, d)
err = tmpl.Execute(out, d)
if err2 := out.Close(); err == nil {
err = err2
}
Expand Down Expand Up @@ -163,8 +176,12 @@ func colors() template.CSS {
return template.CSS(buf.String())
}

func defaultCss() template.CSS {
return colors()
}

var htmlTemplate = template.Must(template.New("html").Funcs(template.FuncMap{
"colors": colors,
"css": defaultCss,
}).Parse(tmplHTML))

type templateData struct {
Expand Down Expand Up @@ -237,7 +254,7 @@ const tmplHTML = `
#legend span {
margin: 0 5px;
}
{{colors}}
{{css}}
</style>
</head>
<body>
Expand Down