all: remove coverageredesign experiment

The coverageredesign experiment was turned on by default by
CL 436236 in September, 2022. We've documented it and people
are using it. This CL removes the ability to turn off the experiment.
This removes some old code that is no longer being executed.

For #51430

Change-Id: I88d4998c8b5ea98eef8145d7ca6ebd96f64fbc2b
Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest,gotip-darwin-amd64-longtest,gotip-linux-arm64-longtest,gotip-windows-amd64-longtest
Reviewed-on: https://go-review.googlesource.com/c/go/+/644997
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Than McIntosh <thanm@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Commit-Queue: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Ian Lance Taylor 2025-01-28 11:27:22 -08:00 committed by Gopher Robot
parent 26c59d3153
commit 41298239cf
33 changed files with 106 additions and 531 deletions

View file

@ -375,10 +375,6 @@ func TestIssue56044(t *testing.T) {
if testing.Short() {
t.Skipf("skipping test: too long for short mode")
}
if !goexperiment.CoverageRedesign {
t.Skipf("skipping new coverage tests (experiment not enabled)")
}
testenv.MustHaveGoBuild(t)
modes := []string{"-covermode=set", "-covermode=atomic"}

View file

@ -9,7 +9,6 @@ import (
"flag"
"fmt"
"internal/coverage/pods"
"internal/goexperiment"
"internal/testenv"
"log"
"os"
@ -150,9 +149,6 @@ const debugWorkDir = false
func TestCovTool(t *testing.T) {
testenv.MustHaveGoBuild(t)
if !goexperiment.CoverageRedesign {
t.Skipf("stubbed out due to goexperiment.CoverageRedesign=false")
}
dir := tempDir(t)
if testing.Short() {
t.Skip()

View file

@ -2484,7 +2484,6 @@
// GOCOVERDIR
// Directory into which to write code coverage data files
// generated by running a "go build -cover" binary.
// Requires that GOEXPERIMENT=coverageredesign is enabled.
//
// Special-purpose environment variables:
//

View file

@ -657,7 +657,6 @@ Environment variables for use with code coverage:
GOCOVERDIR
Directory into which to write code coverage data files
generated by running a "go build -cover" binary.
Requires that GOEXPERIMENT=coverageredesign is enabled.
Special-purpose environment variables:

View file

@ -347,9 +347,7 @@ func init() {
CmdList.Run = runList // break init cycle
// Omit build -json because list has its own -json
work.AddBuildFlags(CmdList, work.OmitJSONFlag)
if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign {
work.AddCoverFlags(CmdList, nil)
}
work.AddCoverFlags(CmdList, nil)
CmdList.Flag.Var(&listJsonFields, "json", "")
}
@ -728,7 +726,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
b.IsCmdList = true
b.NeedExport = *listExport
b.NeedCompiledGoFiles = *listCompiled
if cfg.Experiment.CoverageRedesign && cfg.BuildCover {
if cfg.BuildCover {
load.PrepareForCoverageBuild(pkgs)
}
a := &work.Action{}

View file

@ -8,7 +8,6 @@ package load
import (
"bytes"
"context"
"crypto/sha256"
"encoding/json"
"errors"
"fmt"
@ -218,27 +217,26 @@ func (p *Package) IsTestOnly() bool {
type PackageInternal struct {
// Unexported fields are not part of the public API.
Build *build.Package
Imports []*Package // this package's direct imports
CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library); 1:1 with the end of PackagePublic.Imports
RawImports []string // this package's original imports as they appear in the text of the program; 1:1 with the end of PackagePublic.Imports
ForceLibrary bool // this package is a library (even if named "main")
CmdlineFiles bool // package built from files listed on command line
CmdlinePkg bool // package listed on command line
CmdlinePkgLiteral bool // package listed as literal on command line (not via wildcard)
Local bool // imported via local path (./ or ../)
LocalPrefix string // interpret ./ and ../ imports relative to this prefix
ExeName string // desired name for temporary executable
FuzzInstrument bool // package should be instrumented for fuzzing
Cover CoverSetup // coverage mode and other setup info of -cover is being applied to this package
CoverVars map[string]*CoverVar // variables created by coverage analysis
OmitDebug bool // tell linker not to write debug information
GobinSubdir bool // install target would be subdir of GOBIN
BuildInfo *debug.BuildInfo // add this info to package main
TestmainGo *[]byte // content for _testmain.go
Embed map[string][]string // //go:embed comment mapping
OrigImportPath string // original import path before adding '_test' suffix
PGOProfile string // path to PGO profile
ForMain string // the main package if this package is built specifically for it
Imports []*Package // this package's direct imports
CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library); 1:1 with the end of PackagePublic.Imports
RawImports []string // this package's original imports as they appear in the text of the program; 1:1 with the end of PackagePublic.Imports
ForceLibrary bool // this package is a library (even if named "main")
CmdlineFiles bool // package built from files listed on command line
CmdlinePkg bool // package listed on command line
CmdlinePkgLiteral bool // package listed as literal on command line (not via wildcard)
Local bool // imported via local path (./ or ../)
LocalPrefix string // interpret ./ and ../ imports relative to this prefix
ExeName string // desired name for temporary executable
FuzzInstrument bool // package should be instrumented for fuzzing
Cover CoverSetup // coverage mode and other setup info of -cover is being applied to this package
OmitDebug bool // tell linker not to write debug information
GobinSubdir bool // install target would be subdir of GOBIN
BuildInfo *debug.BuildInfo // add this info to package main
TestmainGo *[]byte // content for _testmain.go
Embed map[string][]string // //go:embed comment mapping
OrigImportPath string // original import path before adding '_test' suffix
PGOProfile string // path to PGO profile
ForMain string // the main package if this package is built specifically for it
Asmflags []string // -asmflags for this package
Gcflags []string // -gcflags for this package
@ -372,12 +370,6 @@ func (p *Package) Resolve(imports []string) []string {
return all
}
// CoverVar holds the name of the generated coverage variables targeting the named file.
type CoverVar struct {
File string // local file name
Var string // name of count struct
}
// CoverSetup holds parameters related to coverage setup for a given package (covermode, etc).
type CoverSetup struct {
Mode string // coverage mode for this package
@ -2627,7 +2619,7 @@ func LinkerDeps(p *Package) ([]string, error) {
deps = append(deps, "runtime/asan")
}
// Building for coverage forces an import of runtime/coverage.
if cfg.BuildCover && cfg.Experiment.CoverageRedesign {
if cfg.BuildCover {
deps = append(deps, "runtime/coverage")
}
@ -3568,14 +3560,6 @@ func SelectCoverPackages(roots []*Package, match []func(*Package) bool, op strin
if cfg.BuildCoverMode == "atomic" {
EnsureImport(p, "sync/atomic")
}
// Generate covervars if using legacy coverage design.
if !cfg.Experiment.CoverageRedesign {
var coverFiles []string
coverFiles = append(coverFiles, p.GoFiles...)
coverFiles = append(coverFiles, p.CgoFiles...)
p.Internal.CoverVars = DeclareCoverVars(p, coverFiles...)
}
}
// Warn about -coverpkg arguments that are not actually used.
@ -3587,42 +3571,3 @@ func SelectCoverPackages(roots []*Package, match []func(*Package) bool, op strin
return covered
}
// DeclareCoverVars attaches the required cover variables names
// to the files, to be used when annotating the files. This
// function only called when using legacy coverage test/build
// (e.g. GOEXPERIMENT=coverageredesign is off).
func DeclareCoverVars(p *Package, files ...string) map[string]*CoverVar {
coverVars := make(map[string]*CoverVar)
coverIndex := 0
// We create the cover counters as new top-level variables in the package.
// We need to avoid collisions with user variables (GoCover_0 is unlikely but still)
// and more importantly with dot imports of other covered packages,
// so we append 12 hex digits from the SHA-256 of the import path.
// The point is only to avoid accidents, not to defeat users determined to
// break things.
sum := sha256.Sum256([]byte(p.ImportPath))
h := fmt.Sprintf("%x", sum[:6])
for _, file := range files {
if base.IsTestFile(file) {
continue
}
// For a package that is "local" (imported via ./ import or command line, outside GOPATH),
// we record the full path to the file name.
// Otherwise we record the import path, then a forward slash, then the file name.
// This makes profiles within GOPATH file system-independent.
// These names appear in the cmd/cover HTML interface.
var longFile string
if p.Internal.Local {
longFile = filepath.Join(p.Dir, file)
} else {
longFile = pathpkg.Join(p.ImportPath, file)
}
coverVars[file] = &CoverVar{
File: longFile,
Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h),
}
coverIndex++
}
return coverVars
}

View file

@ -23,7 +23,6 @@ import (
"unicode"
"unicode/utf8"
"cmd/go/internal/cfg"
"cmd/go/internal/fsys"
"cmd/go/internal/str"
"cmd/go/internal/trace"
@ -42,7 +41,6 @@ type TestCover struct {
Local bool
Pkgs []*Package
Paths []string
Vars []coverInfo
}
// TestPackagesFor is like TestPackagesAndErrors but it returns
@ -309,7 +307,7 @@ func TestPackagesAndErrors(ctx context.Context, done func(), opts PackageOpts, p
// Also the linker introduces implicit dependencies reported by LinkerDeps.
stk.Push(ImportInfo{Pkg: "testmain"})
deps := TestMainDeps // cap==len, so safe for append
if cover != nil && cfg.Experiment.CoverageRedesign {
if cover != nil {
deps = append(deps, "internal/coverage/cfile")
}
ldDeps, err := LinkerDeps(p)
@ -334,22 +332,6 @@ func TestPackagesAndErrors(ctx context.Context, done func(), opts PackageOpts, p
stk.Pop()
parallelizablePart := func() {
if cover != nil && cover.Pkgs != nil && !cfg.Experiment.CoverageRedesign {
// Add imports, but avoid duplicates.
seen := map[*Package]bool{p: true, ptest: true}
for _, p1 := range pmain.Internal.Imports {
seen[p1] = true
}
for _, p1 := range cover.Pkgs {
if seen[p1] {
// Don't add duplicate imports.
continue
}
seen[p1] = true
pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
}
}
allTestImports := make([]*Package, 0, len(pmain.Internal.Imports)+len(imports)+len(ximports))
allTestImports = append(allTestImports, pmain.Internal.Imports...)
allTestImports = append(allTestImports, imports...)
@ -397,35 +379,19 @@ func TestPackagesAndErrors(ctx context.Context, done func(), opts PackageOpts, p
}
if cover != nil {
if cfg.Experiment.CoverageRedesign {
// Here ptest needs to inherit the proper coverage mode (since
// it contains p's Go files), whereas pmain contains only
// test harness code (don't want to instrument it, and
// we don't want coverage hooks in the pkg init).
ptest.Internal.Cover.Mode = p.Internal.Cover.Mode
pmain.Internal.Cover.Mode = "testmain"
}
// Here ptest needs to inherit the proper coverage mode (since
// it contains p's Go files), whereas pmain contains only
// test harness code (don't want to instrument it, and
// we don't want coverage hooks in the pkg init).
ptest.Internal.Cover.Mode = p.Internal.Cover.Mode
pmain.Internal.Cover.Mode = "testmain"
// Should we apply coverage analysis locally, only for this
// package and only for this test? Yes, if -cover is on but
// -coverpkg has not specified a list of packages for global
// coverage.
if cover.Local {
ptest.Internal.Cover.Mode = cover.Mode
if !cfg.Experiment.CoverageRedesign {
var coverFiles []string
coverFiles = append(coverFiles, ptest.GoFiles...)
coverFiles = append(coverFiles, ptest.CgoFiles...)
ptest.Internal.CoverVars = DeclareCoverVars(ptest, coverFiles...)
}
}
if !cfg.Experiment.CoverageRedesign {
for _, cp := range pmain.Internal.Imports {
if len(cp.Internal.CoverVars) > 0 {
t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars})
}
}
}
}
@ -625,11 +591,6 @@ func isTest(name, prefix string) bool {
return !unicode.IsLower(rune)
}
type coverInfo struct {
Package *Package
Vars map[string]*CoverVar
}
// loadTestFuncs returns the testFuncs describing the tests that will be run.
// The returned testFuncs is always non-nil, even if an error occurred while
// processing test files.
@ -655,9 +616,6 @@ func loadTestFuncs(ptest *Package) (*testFuncs, error) {
func formatTestmain(t *testFuncs) ([]byte, error) {
var buf bytes.Buffer
tmpl := testmainTmpl
if cfg.Experiment.CoverageRedesign {
tmpl = testmainTmplNewCoverage
}
if err := tmpl.Execute(&buf, t); err != nil {
return nil, err
}
@ -825,119 +783,6 @@ var testmainTmpl = lazytemplate.New("main", `
package main
import (
"os"
{{if .TestMain}}
"reflect"
{{end}}
"testing"
"testing/internal/testdeps"
{{if .ImportTest}}
{{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}}
{{end}}
{{if .ImportXtest}}
{{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
{{end}}
{{if .Cover}}
{{range $i, $p := .Cover.Vars}}
_cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
{{end}}
{{end}}
)
var tests = []testing.InternalTest{
{{range .Tests}}
{"{{.Name}}", {{.Package}}.{{.Name}}},
{{end}}
}
var benchmarks = []testing.InternalBenchmark{
{{range .Benchmarks}}
{"{{.Name}}", {{.Package}}.{{.Name}}},
{{end}}
}
var fuzzTargets = []testing.InternalFuzzTarget{
{{range .FuzzTargets}}
{"{{.Name}}", {{.Package}}.{{.Name}}},
{{end}}
}
var examples = []testing.InternalExample{
{{range .Examples}}
{"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
{{end}}
}
func init() {
testdeps.ImportPath = {{.ImportPath | printf "%q"}}
}
{{if .Cover}}
// Only updated by init functions, so no need for atomicity.
var (
coverCounters = make(map[string][]uint32)
coverBlocks = make(map[string][]testing.CoverBlock)
)
func init() {
{{range $i, $p := .Cover.Vars}}
{{range $file, $cover := $p.Vars}}
coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:])
{{end}}
{{end}}
}
func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
panic("coverage: mismatched sizes")
}
if coverCounters[fileName] != nil {
// Already registered.
return
}
coverCounters[fileName] = counter
block := make([]testing.CoverBlock, len(counter))
for i := range counter {
block[i] = testing.CoverBlock{
Line0: pos[3*i+0],
Col0: uint16(pos[3*i+2]),
Line1: pos[3*i+1],
Col1: uint16(pos[3*i+2]>>16),
Stmts: numStmts[i],
}
}
coverBlocks[fileName] = block
}
{{end}}
func main() {
{{if .Cover}}
testing.RegisterCover(testing.Cover{
Mode: {{printf "%q" .Cover.Mode}},
Counters: coverCounters,
Blocks: coverBlocks,
CoveredPackages: {{printf "%q" .Covered}},
})
{{end}}
m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples)
{{with .TestMain}}
{{.Package}}.{{.Name}}(m)
os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int()))
{{else}}
os.Exit(m.Run())
{{end}}
}
`)
var testmainTmplNewCoverage = lazytemplate.New("main", `
// Code generated by 'go test'. DO NOT EDIT.
package main
import (
"os"
{{if .TestMain}}

View file

@ -66,9 +66,7 @@ func init() {
CmdRun.Run = runRun // break init loop
work.AddBuildFlags(CmdRun, work.DefaultBuildFlags)
if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign {
work.AddCoverFlags(CmdRun, nil)
}
work.AddCoverFlags(CmdRun, nil)
CmdRun.Flag.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "")
}
@ -141,7 +139,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
cmdArgs := args[i:]
load.CheckPackageErrors([]*load.Package{p})
if cfg.Experiment.CoverageRedesign && cfg.BuildCover {
if cfg.BuildCover {
load.PrepareForCoverageBuild([]*load.Package{p})
}

View file

@ -867,7 +867,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
// patterns.
plist := load.TestPackageList(ctx, pkgOpts, pkgs)
testCoverPkgs = load.SelectCoverPackages(plist, match, "test")
if cfg.Experiment.CoverageRedesign && len(testCoverPkgs) > 0 {
if len(testCoverPkgs) > 0 {
// create a new singleton action that will collect up the
// meta-data files from all of the packages mentioned in
// "-coverpkg" and write them to a summary file. This new
@ -984,9 +984,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
// later package. Note that if -coverpkg is in effect
// p.Internal.Cover.GenMeta will wind up being set for
// all matching packages.
if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 &&
cfg.BuildCoverPkg == nil &&
cfg.Experiment.CoverageRedesign {
if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 && cfg.BuildCoverPkg == nil {
p.Internal.Cover.GenMeta = true
}
}
@ -1092,7 +1090,7 @@ var windowsBadWords = []string{
func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, p *load.Package, imported bool, writeCoverMetaAct *work.Action) (buildAction, runAction, printAction *work.Action, perr *load.Package, err error) {
if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
if cfg.BuildCover && cfg.Experiment.CoverageRedesign {
if cfg.BuildCover {
if p.Internal.Cover.GenMeta {
p.Internal.Cover.Mode = cfg.BuildCoverMode
}
@ -1487,7 +1485,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
if p := a.Package; len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
reportNoTestFiles := true
if cfg.BuildCover && cfg.Experiment.CoverageRedesign && p.Internal.Cover.GenMeta {
if cfg.BuildCover && p.Internal.Cover.GenMeta {
if err := sh.Mkdir(a.Objdir); err != nil {
return err
}

View file

@ -247,10 +247,8 @@ func init() {
AddBuildFlags(CmdBuild, DefaultBuildFlags)
AddBuildFlags(CmdInstall, DefaultBuildFlags)
if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign {
AddCoverFlags(CmdBuild, nil)
AddCoverFlags(CmdInstall, nil)
}
AddCoverFlags(CmdBuild, nil)
AddCoverFlags(CmdInstall, nil)
}
// Note that flags consulted by other parts of the code
@ -361,26 +359,13 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
cmd.Flag.StringVar(&cfg.DebugTrace, "debug-trace", "", "")
}
// AddCoverFlags adds coverage-related flags to "cmd". If the
// CoverageRedesign experiment is enabled, we add -cover{mode,pkg} to
// the build command and only -coverprofile to the test command. If
// the CoverageRedesign experiment is disabled, -cover* flags are
// added only to the test command.
// AddCoverFlags adds coverage-related flags to "cmd".
// We add -cover{mode,pkg} to the build command and only
// -coverprofile to the test command.
func AddCoverFlags(cmd *base.Command, coverProfileFlag *string) {
addCover := false
if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign {
// New coverage enabled: both build and test commands get
// coverage flags.
addCover = true
} else {
// New coverage disabled: only test command gets cover flags.
addCover = coverProfileFlag != nil
}
if addCover {
cmd.Flag.BoolVar(&cfg.BuildCover, "cover", false, "")
cmd.Flag.Var(coverFlag{(*coverModeFlag)(&cfg.BuildCoverMode)}, "covermode", "")
cmd.Flag.Var(coverFlag{commaListFlag{&cfg.BuildCoverPkg}}, "coverpkg", "")
}
cmd.Flag.BoolVar(&cfg.BuildCover, "cover", false, "")
cmd.Flag.Var(coverFlag{(*coverModeFlag)(&cfg.BuildCoverMode)}, "covermode", "")
cmd.Flag.Var(coverFlag{commaListFlag{&cfg.BuildCoverPkg}}, "coverpkg", "")
if coverProfileFlag != nil {
cmd.Flag.Var(coverFlag{V: stringFlag{coverProfileFlag}}, "coverprofile", "")
}
@ -515,7 +500,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) {
cfg.BuildO = ""
}
if cfg.Experiment.CoverageRedesign && cfg.BuildCover {
if cfg.BuildCover {
load.PrepareForCoverageBuild(pkgs)
}
@ -732,7 +717,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) {
}
load.CheckPackageErrors(pkgs)
if cfg.Experiment.CoverageRedesign && cfg.BuildCover {
if cfg.BuildCover {
load.PrepareForCoverageBuild(pkgs)
}

View file

@ -647,31 +647,18 @@ OverlayLoop:
var sourceFile string
var coverFile string
var key string
if base, found := strings.CutSuffix(file, ".cgo1.go"); found {
// cgo files have absolute paths
base = filepath.Base(base)
sourceFile = file
coverFile = objdir + base + ".cgo1.go"
key = base + ".go"
} else {
sourceFile = filepath.Join(p.Dir, file)
coverFile = objdir + file
key = file
}
coverFile = strings.TrimSuffix(coverFile, ".go") + ".cover.go"
if cfg.Experiment.CoverageRedesign {
infiles = append(infiles, sourceFile)
outfiles = append(outfiles, coverFile)
} else {
cover := p.Internal.CoverVars[key]
if cover == nil {
continue // Not covering this file.
}
if err := b.cover(a, coverFile, sourceFile, cover.Var); err != nil {
return err
}
}
infiles = append(infiles, sourceFile)
outfiles = append(outfiles, coverFile)
if i < len(gofiles) {
gofiles[i] = coverFile
} else {
@ -679,36 +666,27 @@ OverlayLoop:
}
}
if cfg.Experiment.CoverageRedesign {
if len(infiles) != 0 {
// Coverage instrumentation creates new top level
// variables in the target package for things like
// meta-data containers, counter vars, etc. To avoid
// collisions with user variables, suffix the var name
// with 12 hex digits from the SHA-256 hash of the
// import path. Choice of 12 digits is historical/arbitrary,
// we just need enough of the hash to avoid accidents,
// as opposed to precluding determined attempts by
// users to break things.
sum := sha256.Sum256([]byte(a.Package.ImportPath))
coverVar := fmt.Sprintf("goCover_%x_", sum[:6])
mode := a.Package.Internal.Cover.Mode
if mode == "" {
panic("covermode should be set at this point")
}
if newoutfiles, err := b.cover2(a, infiles, outfiles, coverVar, mode); err != nil {
return err
} else {
outfiles = newoutfiles
gofiles = append([]string{newoutfiles[0]}, gofiles...)
}
if len(infiles) != 0 {
// Coverage instrumentation creates new top level
// variables in the target package for things like
// meta-data containers, counter vars, etc. To avoid
// collisions with user variables, suffix the var name
// with 12 hex digits from the SHA-256 hash of the
// import path. Choice of 12 digits is historical/arbitrary,
// we just need enough of the hash to avoid accidents,
// as opposed to precluding determined attempts by
// users to break things.
sum := sha256.Sum256([]byte(a.Package.ImportPath))
coverVar := fmt.Sprintf("goCover_%x_", sum[:6])
mode := a.Package.Internal.Cover.Mode
if mode == "" {
panic("covermode should be set at this point")
}
if newoutfiles, err := b.cover(a, infiles, outfiles, coverVar, mode); err != nil {
return err
} else {
// If there are no input files passed to cmd/cover,
// then we don't want to pass -covercfg when building
// the package with the compiler, so set covermode to
// the empty string so as to signal that we need to do
// that.
p.Internal.Cover.Mode = ""
outfiles = newoutfiles
gofiles = append([]string{newoutfiles[0]}, gofiles...)
}
if ba, ok := a.Actor.(*buildActor); ok && ba.covMetaFileName != "" {
b.cacheObjdirFile(a, cache.Default(), ba.covMetaFileName)
@ -1909,26 +1887,13 @@ func (b *Builder) installHeader(ctx context.Context, a *Action) error {
// cover runs, in effect,
//
// go tool cover -mode=b.coverMode -var="varName" -o dst.go src.go
func (b *Builder) cover(a *Action, dst, src string, varName string) error {
return b.Shell(a).run(a.Objdir, "", nil,
cfg.BuildToolexec,
base.Tool("cover"),
"-mode", a.Package.Internal.Cover.Mode,
"-var", varName,
"-o", dst,
src)
}
// cover2 runs, in effect,
//
// go tool cover -pkgcfg=<config file> -mode=b.coverMode -var="varName" -o <outfiles> <infiles>
//
// Return value is an updated output files list; in addition to the
// regular outputs (instrumented source files) the cover tool also
// writes a separate file (appearing first in the list of outputs)
// that will contain coverage counters and meta-data.
func (b *Builder) cover2(a *Action, infiles, outfiles []string, varName string, mode string) ([]string, error) {
func (b *Builder) cover(a *Action, infiles, outfiles []string, varName string, mode string) ([]string, error) {
pkgcfg := a.Objdir + "pkgcfg.txt"
covoutputs := a.Objdir + "coveroutfiles.txt"
odir := filepath.Dir(outfiles[0])

View file

@ -5,7 +5,6 @@
# inside and outside the standard library.
[short] skip
[!GOEXPERIMENT:coverageredesign] skip
# Compile an object.
go tool compile -p tiny tiny/tiny.go tiny/tiny2.go

View file

@ -3,9 +3,6 @@
[short] skip
# Skip if new coverage is not enabled.
[!GOEXPERIMENT:coverageredesign] skip
#-------------------------------------------
# Build for coverage.

View file

@ -2,9 +2,6 @@
[short] skip
# Hard-wire new coverage for this test.
env GOEXPERIMENT=coverageredesign
# Build for coverage.
go build -gcflags=-m -o example.exe -cover example/main &
[race] go build -o examplewithrace.exe -race -cover example/main &

View file

@ -16,7 +16,6 @@
#
[short] skip
[!GOEXPERIMENT:coverageredesign] skip
# Test all packages with -coverpkg=./...
go test -coverprofile=cov.p -coverpkg=./... ./...

View file

@ -5,7 +5,6 @@
# do not, some have tests and some do not.
[short] skip
[!GOEXPERIMENT:coverageredesign] skip
# Verify correct statements percentages. We have a total of 10
# statements in the packages matched by "./..."; package "a" (for

View file

@ -3,7 +3,6 @@
# build arguments (such as -cover, -covermode). See issue #57785.
[short] skip
[!GOEXPERIMENT:coverageredesign] skip
env GOBIN=$WORK/bin

View file

@ -4,7 +4,6 @@
# the "main" package is handled. See issue 57169 for details.
[short] skip
[!GOEXPERIMENT:coverageredesign] skip
# Build this program with -cover and run to collect a profile.

View file

@ -10,35 +10,32 @@ env GOCACHE=$WORK/cache
# Initial run with simple coverage.
go test -cover ./pkg1 ./pkg2 ./pkg3 ./pkg4
[!GOEXPERIMENT:coverageredesign] stdout 'pkg1 \[no test files\]'
[GOEXPERIMENT:coverageredesign] stdout 'pkg1 coverage: 0.0% of statements'
stdout 'pkg1 coverage: 0.0% of statements'
stdout 'pkg2 \S+ coverage: 0.0% of statements \[no tests to run\]'
stdout 'pkg3 \S+ coverage: 100.0% of statements'
stdout 'pkg4 \S+ coverage: \[no statements\]'
# Second run to make sure that caching works properly.
go test -x -cover ./pkg1 ./pkg2 ./pkg3 ./pkg4
[!GOEXPERIMENT:coverageredesign] stdout 'pkg1 \[no test files\]'
[GOEXPERIMENT:coverageredesign] stdout 'pkg1 coverage: 0.0% of statements'
stdout 'pkg1 coverage: 0.0% of statements'
stdout 'pkg2 \S+ coverage: 0.0% of statements \[no tests to run\]'
stdout 'pkg3 \S+ coverage: 100.0% of statements'
stdout 'pkg4 \S+ coverage: \[no statements\]'
[GOEXPERIMENT:coverageredesign] ! stderr 'link(\.exe"?)? -'
! stderr 'link(\.exe"?)? -'
! stderr 'compile(\.exe"?)? -'
! stderr 'cover(\.exe"?)? -'
[GOEXPERIMENT:coverageredesign] stderr 'covdata(\.exe"?)? percent'
stderr 'covdata(\.exe"?)? percent'
# Now add in -coverprofile.
go test -cover -coverprofile=cov.dat ./pkg1 ./pkg2 ./pkg3 ./pkg4
[!GOEXPERIMENT:coverageredesign] stdout 'pkg1 \[no test files\]'
[GOEXPERIMENT:coverageredesign] stdout 'pkg1 coverage: 0.0% of statements'
stdout 'pkg1 coverage: 0.0% of statements'
stdout 'pkg2 \S+ coverage: 0.0% of statements \[no tests to run\]'
stdout 'pkg3 \S+ coverage: 100.0% of statements'
stdout 'pkg4 \S+ coverage: \[no statements\]'
# Validate
go tool cover -func=cov.dat
[GOEXPERIMENT:coverageredesign] stdout 'pkg1/a.go:5:\s+F\s+0.0%'
stdout 'pkg1/a.go:5:\s+F\s+0.0%'
-- go.mod --
module m

View file

@ -1,6 +1,5 @@
[short] skip
[compiler:gccgo] skip # gccgo has no cover tool
[!GOEXPERIMENT:coverageredesign] skip
go test -short -cover -covermode=atomic -coverpkg=coverdep/p1 coverdep

View file

@ -1,9 +1,6 @@
[short] skip
# Hard-wire new coverage for this test.
env GOEXPERIMENT=coverageredesign
# Baseline run.
go test -cover example/foo
stdout 'coverage: 50.0% of statements$'

View file

@ -4,9 +4,6 @@
[short] skip
# Skip if new coverage is turned off.
[!GOEXPERIMENT:coverageredesign] skip
go test -cover example
-- go.mod --

View file

@ -2,7 +2,6 @@
# Rudimentary test of testing.Coverage().
[short] skip
[!GOEXPERIMENT:coverageredesign] skip
# Simple test.
go test -v -cover -count=1

View file

@ -11,7 +11,6 @@ import (
"internal/coverage/decodecounter"
"internal/coverage/decodemeta"
"internal/coverage/pods"
"internal/goexperiment"
"internal/testenv"
"os"
"path/filepath"
@ -45,9 +44,6 @@ func (v *visitor) Finish()
func TestIssue58411(t *testing.T) {
testenv.MustHaveGoBuild(t)
if !goexperiment.CoverageRedesign {
t.Skipf("skipping since this test requires 'go build -cover'")
}
// Build a tiny test program with -cover. Smallness is important;
// it is one of the factors that triggers issue 58411.

View file

@ -74,13 +74,12 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
}
baseline := goexperiment.Flags{
RegabiWrappers: regabiSupported,
RegabiArgs: regabiSupported,
CoverageRedesign: true,
AliasTypeParams: true,
SwissMap: true,
SpinbitMutex: haveXchg8,
SyncHashTrieMap: true,
RegabiWrappers: regabiSupported,
RegabiArgs: regabiSupported,
AliasTypeParams: true,
SwissMap: true,
SpinbitMutex: haveXchg8,
SyncHashTrieMap: true,
}
// Start with the statically enabled set of experiments.

View file

@ -7,7 +7,6 @@ package cfile
import (
"fmt"
"internal/coverage"
"internal/goexperiment"
"internal/platform"
"internal/testenv"
"os"
@ -25,9 +24,6 @@ func TestCoverageApis(t *testing.T) {
if testing.Short() {
t.Skipf("skipping test: too long for short mode")
}
if !goexperiment.CoverageRedesign {
t.Skipf("skipping new coverage tests (experiment not enabled)")
}
testenv.MustHaveGoBuild(t)
dir := t.TempDir()
if fixedTestDir {
@ -465,9 +461,6 @@ func TestIssue56006EmitDataRaceCoverRunningGoroutine(t *testing.T) {
if testing.Short() {
t.Skipf("skipping test: too long for short mode")
}
if !goexperiment.CoverageRedesign {
t.Skipf("skipping new coverage tests (experiment not enabled)")
}
// This test requires "go test -race -cover", meaning that we need
// go build, go run, and "-race" support.

View file

@ -8,7 +8,6 @@ import (
"encoding/json"
"flag"
"internal/coverage"
"internal/goexperiment"
"internal/testenv"
"os"
"os/exec"
@ -32,9 +31,6 @@ func testGoCoverDir(t *testing.T) string {
// relying on other test paths will provide a better signal when
// running "go test -cover" for this package).
func TestTestSupport(t *testing.T) {
if !goexperiment.CoverageRedesign {
return
}
if testing.CoverMode() == "" {
return
}
@ -128,9 +124,6 @@ func genAuxMeta(t *testing.T, dstdir string) (string, string) {
}
func TestAuxMetaDataFiles(t *testing.T) {
if !goexperiment.CoverageRedesign {
return
}
if testing.CoverMode() == "" {
return
}

View file

@ -1,8 +0,0 @@
// Code generated by mkconsts.go. DO NOT EDIT.
//go:build !goexperiment.coverageredesign
package goexperiment
const CoverageRedesign = false
const CoverageRedesignInt = 0

View file

@ -1,8 +0,0 @@
// Code generated by mkconsts.go. DO NOT EDIT.
//go:build goexperiment.coverageredesign
package goexperiment
const CoverageRedesign = true
const CoverageRedesignInt = 1

View file

@ -83,10 +83,6 @@ type Flags struct {
// by default.
HeapMinimum512KiB bool
// CoverageRedesign enables the new compiler-based code coverage
// tooling.
CoverageRedesign bool
// Arenas causes the "arena" standard library package to be visible
// to the outside world.
Arenas bool

View file

@ -6,13 +6,6 @@
package testing
import (
"fmt"
"internal/goexperiment"
"os"
"sync/atomic"
)
// CoverBlock records the coverage data for a single basic block.
// The fields are 1-indexed, as in an editor: The opening line of
// the file is number 1, for example. Columns are measured
@ -27,8 +20,6 @@ type CoverBlock struct {
Stmts uint16 // Number of statements included in this block.
}
var cover Cover
// Cover records information about test coverage checking.
// NOTE: This struct is internal to the testing infrastructure and may change.
// It is not covered (yet) by the Go 1 compatibility guidelines.
@ -39,86 +30,8 @@ type Cover struct {
CoveredPackages string
}
// Coverage reports the current code coverage as a fraction in the range [0, 1].
// If coverage is not enabled, Coverage returns 0.
//
// When running a large set of sequential test cases, checking Coverage after each one
// can be useful for identifying which test cases exercise new code paths.
// It is not a replacement for the reports generated by 'go test -cover' and
// 'go tool cover'.
func Coverage() float64 {
if goexperiment.CoverageRedesign {
return coverage2()
}
var n, d int64
for _, counters := range cover.Counters {
for i := range counters {
if atomic.LoadUint32(&counters[i]) > 0 {
n++
}
d++
}
}
if d == 0 {
return 0
}
return float64(n) / float64(d)
}
// RegisterCover records the coverage data accumulators for the tests.
// NOTE: This function is internal to the testing infrastructure and may change.
// It is not covered (yet) by the Go 1 compatibility guidelines.
func RegisterCover(c Cover) {
cover = c
}
// mustBeNil checks the error and, if present, reports it and exits.
func mustBeNil(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "testing: %s\n", err)
os.Exit(2)
}
}
// 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 != "" {
f, err = os.Create(toOutputDir(*coverProfile))
mustBeNil(err)
fmt.Fprintf(f, "mode: %s\n", cover.Mode)
defer func() { mustBeNil(f.Close()) }()
}
var active, total int64
var count uint32
for name, counts := range cover.Counters {
blocks := cover.Blocks[name]
for i := range counts {
stmts := int64(blocks[i].Stmts)
total += stmts
count = atomic.LoadUint32(&counts[i]) // For -mode=atomic.
if count > 0 {
active += stmts
}
if f != nil {
_, err := fmt.Fprintf(f, "%s:%d.%d,%d.%d %d %d\n", name,
blocks[i].Line0, blocks[i].Col0,
blocks[i].Line1, blocks[i].Col1,
stmts,
count)
mustBeNil(err)
}
}
}
if total == 0 {
fmt.Println("coverage: [no statements]")
return
}
fmt.Printf("coverage: %.1f%% of statements%s\n", 100*float64(active)/float64(total), cover.CoveredPackages)
}

View file

@ -8,49 +8,52 @@ package testing
import (
"fmt"
"internal/goexperiment"
"os"
_ "unsafe" // for linkname
)
// cover2 variable stores the current coverage mode and a
// cover variable stores the current coverage mode and a
// tear-down function to be called at the end of the testing run.
var cover2 struct {
var cover struct {
mode string
tearDown func(coverprofile string, gocoverdir string) (string, error)
snapshotcov func() float64
}
// registerCover2 is invoked during "go test -cover" runs.
// registerCover is invoked during "go test -cover" runs.
// 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), snapcov func() float64) {
func registerCover(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64) {
if mode == "" {
return
}
cover2.mode = mode
cover2.tearDown = tearDown
cover2.snapshotcov = snapcov
cover.mode = mode
cover.tearDown = tearDown
cover.snapshotcov = snapcov
}
// coverReport2 invokes a callback in _testmain.go that will
// coverReport reports the coverage percentage and
// writes a coverage profile if requested.
// This 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 {
func coverReport() {
if errmsg, err := cover.tearDown(*coverProfile, *gocoverdir); err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", errmsg, err)
os.Exit(2)
}
}
// coverage2 returns a rough "coverage percentage so far"
// number to support the testing.Coverage() function.
func coverage2() float64 {
if cover2.mode == "" {
// Coverage reports the current code coverage as a fraction in the range [0, 1].
// If coverage is not enabled, Coverage returns 0.
//
// When running a large set of sequential test cases, checking Coverage after each one
// can be useful for identifying which test cases exercise new code paths.
// It is not a replacement for the reports generated by 'go test -cover' and
// 'go tool cover'.
func Coverage() float64 {
if cover.mode == "" {
return 0.0
}
return cover2.snapshotcov()
return cover.snapshotcov()
}

View file

@ -403,7 +403,6 @@ import (
"errors"
"flag"
"fmt"
"internal/goexperiment"
"internal/race"
"io"
"math/rand"
@ -700,10 +699,7 @@ func Testing() 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
return cover.mode
}
// Verbose reports whether the -test.v flag is set.
@ -2021,7 +2017,7 @@ type testDeps interface {
// It is not meant to be called directly and is not subject to the Go 1 compatibility document.
// It may change signature from release to release.
func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
registerCover2(deps.InitRuntimeCoverage())
registerCover(deps.InitRuntimeCoverage())
Init()
return &M{
deps: deps,