cmd/go,testing: re-implement testing.Coverage

This patch revives the testing.Coverage() function, which takes a
snapshot of the coverage counters within an executing "go test -cover"
test binary and returns a percentage approximating the percent of
statements covered so far.

Fixes #59590.

Change-Id: I541d47a42d71c8fb2edc473d86c8951fa80f4ab0
Reviewed-on: https://go-review.googlesource.com/c/go/+/495450
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Than McIntosh 2023-05-12 08:48:32 -04:00
parent c99d966c17
commit f81c885819
4 changed files with 79 additions and 5 deletions

View File

@ -932,11 +932,14 @@ func init() {
func runtime_coverage_processCoverTestDir(dir string, cfile string, cmode string, cpkgs string) error func runtime_coverage_processCoverTestDir(dir string, cfile string, cmode string, cpkgs string) error
//go:linkname testing_registerCover2 testing.registerCover2 //go:linkname testing_registerCover2 testing.registerCover2
func testing_registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error)) func testing_registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64)
//go:linkname runtime_coverage_markProfileEmitted runtime/coverage.markProfileEmitted //go:linkname runtime_coverage_markProfileEmitted runtime/coverage.markProfileEmitted
func runtime_coverage_markProfileEmitted(val bool) func runtime_coverage_markProfileEmitted(val bool)
//go:linkname runtime_coverage_snapshot runtime/coverage.snapshot
func runtime_coverage_snapshot() float64
func coverTearDown(coverprofile string, gocoverdir string) (string, error) { func coverTearDown(coverprofile string, gocoverdir string) (string, error) {
var err error var err error
if gocoverdir == "" { if gocoverdir == "" {
@ -957,7 +960,7 @@ func coverTearDown(coverprofile string, gocoverdir string) (string, error) {
func main() { func main() {
{{if .Cover}} {{if .Cover}}
testing_registerCover2({{printf "%q" .Cover.Mode}}, coverTearDown) testing_registerCover2({{printf "%q" .Cover.Mode}}, coverTearDown, runtime_coverage_snapshot)
{{end}} {{end}}
m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples)
{{with .TestMain}} {{with .TestMain}}

View File

@ -0,0 +1,57 @@
# Rudimentary test of testing.Coverage().
[short] skip
[!GOEXPERIMENT:coverageredesign] skip
# Simple test.
go test -v -cover -count=1
# Make sure test still passes when test executable is built and
# run outside the go command.
go test -c -o t.exe -cover
exec ./t.exe
-- go.mod --
module hello
go 1.20
-- hello.go --
package hello
func Hello() {
println("hello")
}
// contents not especially interesting, just need some code
func foo(n int) int {
t := 0
for i := 0; i < n; i++ {
for j := 0; j < i; j++ {
t += i ^ j
if t == 1010101 {
break
}
}
}
return t
}
-- hello_test.go --
package hello
import "testing"
func TestTestCoverage(t *testing.T) {
Hello()
C1 := testing.Coverage()
foo(29)
C2 := testing.Coverage()
if C1 == 0.0 || C2 == 0.0 {
t.Errorf("unexpected zero values C1=%f C2=%f", C1, C2)
}
if C1 >= C2 {
t.Errorf("testing.Coverage() not monotonically increasing C1=%f C2=%f", C1, C2)
}
}

View File

@ -47,6 +47,9 @@ type Cover struct {
// It is not a replacement for the reports generated by 'go test -cover' and // It is not a replacement for the reports generated by 'go test -cover' and
// 'go tool cover'. // 'go tool cover'.
func Coverage() float64 { func Coverage() float64 {
if goexperiment.CoverageRedesign {
return coverage2()
}
var n, d int64 var n, d int64
for _, counters := range cover.Counters { for _, counters := range cover.Counters {
for i := range counters { for i := range counters {

View File

@ -15,16 +15,18 @@ import (
// cover2 variable stores the current coverage mode and a // cover2 variable stores the current coverage mode and a
// tear-down function to be called at the end of the testing run. // tear-down function to be called at the end of the testing run.
var cover2 struct { var cover2 struct {
mode string mode string
tearDown func(coverprofile string, gocoverdir string) (string, error) tearDown func(coverprofile string, gocoverdir string) (string, error)
snapshotcov func() float64
} }
// registerCover2 is invoked during "go test -cover" runs by the test harness // registerCover2 is invoked during "go test -cover" runs by the test harness
// code in _testmain.go; it is used to record a 'tear down' function // code in _testmain.go; it is used to record a 'tear down' function
// (to be called when the test is complete) and the coverage mode. // (to be called when the test is complete) and the coverage mode.
func registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error)) { func registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64) {
cover2.mode = mode cover2.mode = mode
cover2.tearDown = tearDown cover2.tearDown = tearDown
cover2.snapshotcov = snapcov
} }
// coverReport2 invokes a callback in _testmain.go that will // coverReport2 invokes a callback in _testmain.go that will
@ -46,3 +48,12 @@ func coverReport2() {
func testGoCoverDir() string { func testGoCoverDir() string {
return *gocoverdir return *gocoverdir
} }
// coverage2 returns a rough "coverage percentage so far"
// number to support the testing.Coverage() function.
func coverage2() float64 {
if cover2.mode == "" {
return 0.0
}
return cover2.snapshotcov()
}