diff options
| author | Than McIntosh <thanm@google.com> | 2021-10-11 11:26:19 -0400 |
|---|---|---|
| committer | Than McIntosh <thanm@google.com> | 2022-09-28 11:50:58 +0000 |
| commit | 53773a5d0892be4489b4d5e91bbc8ae61000ada7 (patch) | |
| tree | 0d137bcb30ce27cf331d396ba82fc983a3f19c35 /src/testing | |
| parent | b32689f6c3156da19d469f35cc68fc155d401ef9 (diff) | |
| download | go-53773a5d0892be4489b4d5e91bbc8ae61000ada7.tar.xz | |
cmd/go: support new hybrid coverage instrumentation
If GOEXPERIMENT=coverageredesign is in effect, introduce a new
top-level '-cover' option to "go build" to turn on new-style hybrid
code coverage instrumentation. Similarly, use the new instrumentation
for "go test -cover".
The main effects of "-cover" under the hood are to instrument files at
the package level using cmd/cover and to pass additional options to
the compiler when building instrumented packages.
The previous workflow for "go tool -cover mypkg" would expand to a
series of "go tool cover" commands (one per file) followed by a single
package compilation command to build the rewritten sources.
With the new workflow, the Go command will pass all of the Go files in
a package to the cover tool as a chunk (along with a config file
containing other parameters), then the cover tool will write
instrumented versions of the sources along with another "output"
config with info on coverage variable names for the the compiler. The
Go command will then kick off the compiler on the modified source
files, also passing in the config file generated by cmd/cover.
Updates #51430.
Change-Id: Id65621ff6a8c70a30168c1412c2d6f805ff3b9e7
Reviewed-on: https://go-review.googlesource.com/c/go/+/355452
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Than McIntosh <thanm@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Diffstat (limited to 'src/testing')
| -rw-r--r-- | src/testing/cover.go | 5 | ||||
| -rw-r--r-- | src/testing/newcover.go | 41 | ||||
| -rw-r--r-- | src/testing/testing.go | 14 |
3 files changed, 58 insertions, 2 deletions
diff --git a/src/testing/cover.go b/src/testing/cover.go index 62ee5ac9c0..b52e53a926 100644 --- a/src/testing/cover.go +++ b/src/testing/cover.go @@ -8,6 +8,7 @@ package testing import ( "fmt" + "internal/goexperiment" "os" "sync/atomic" ) @@ -78,6 +79,10 @@ func mustBeNil(err error) { // coverReport reports the coverage percentage and writes a coverage profile if requested. func coverReport() { + if goexperiment.CoverageRedesign { + coverReport2() + return + } var f *os.File var err error if *coverProfile != "" { diff --git a/src/testing/newcover.go b/src/testing/newcover.go new file mode 100644 index 0000000000..e90b9c9805 --- /dev/null +++ b/src/testing/newcover.go @@ -0,0 +1,41 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Support for test coverage with redesigned coverage implementation. + +package testing + +import ( + "fmt" + "internal/goexperiment" + "os" +) + +// cover2 variable stores the current coverage mode and a +// tear-down function to be called at the end of the testing run. +var cover2 struct { + mode string + tearDown func(coverprofile string, gocoverdir string) (string, error) +} + +// 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 +// (to be called when the test is complete) and the coverage mode. +func registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error)) { + cover2.mode = mode + cover2.tearDown = tearDown +} + +// coverReport2 invokes a callback in _testmain.go that will +// emit coverage data at the point where test execution is complete, +// for "go test -cover" runs. +func coverReport2() { + if !goexperiment.CoverageRedesign { + panic("unexpected") + } + if errmsg, err := cover2.tearDown(*coverProfile, *gocoverdir); err != nil { + fmt.Fprintf(os.Stderr, "%s: %v\n", errmsg, err) + os.Exit(2) + } +} diff --git a/src/testing/testing.go b/src/testing/testing.go index 81268ec61f..7e86faf950 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -372,6 +372,7 @@ import ( "errors" "flag" "fmt" + "internal/goexperiment" "internal/race" "io" "math/rand" @@ -420,6 +421,7 @@ func Init() { chatty = flag.Bool("test.v", false, "verbose: print additional output") count = flag.Uint("test.count", 1, "run tests and benchmarks `n` times") coverProfile = flag.String("test.coverprofile", "", "write a coverage profile to `file`") + gocoverdir = flag.String("test.gocoverdir", "", "write coverage intermediate files to this directory") matchList = flag.String("test.list", "", "list tests, examples, and benchmarks matching `regexp` then exit") match = flag.String("test.run", "", "run only tests and examples matching `regexp`") skip = flag.String("test.skip", "", "do not list or run tests matching `regexp`") @@ -450,6 +452,7 @@ var ( chatty *bool count *uint coverProfile *string + gocoverdir *string matchList *string match *string skip *string @@ -578,6 +581,9 @@ func Short() bool { // values are "set", "count", or "atomic". The return value will be // empty if test coverage is not enabled. func CoverMode() string { + if goexperiment.CoverageRedesign { + return cover2.mode + } return cover.Mode } @@ -1942,10 +1948,14 @@ func (m *M) before() { if *mutexProfile != "" && *mutexProfileFraction >= 0 { runtime.SetMutexProfileFraction(*mutexProfileFraction) } - if *coverProfile != "" && cover.Mode == "" { + if *coverProfile != "" && CoverMode() == "" { fmt.Fprintf(os.Stderr, "testing: cannot use -test.coverprofile because test binary was not built with coverage enabled\n") os.Exit(2) } + if *gocoverdir != "" && CoverMode() == "" { + fmt.Fprintf(os.Stderr, "testing: cannot use -test.gocoverdir because test binary was not built with coverage enabled\n") + os.Exit(2) + } if *testlog != "" { // Note: Not using toOutputDir. // This file is for use by cmd/go, not users. @@ -2039,7 +2049,7 @@ func (m *M) writeProfiles() { } f.Close() } - if cover.Mode != "" { + if CoverMode() != "" { coverReport() } } |
