import (
"os"
-{{if .Cover}}
- _ "unsafe"
-{{end}}
{{if .TestMain}}
"reflect"
{{end}}
}
func init() {
- testdeps.ImportPath = {{.ImportPath | printf "%q"}}
-}
-
{{if .Cover}}
-
-//go:linkname runtime_coverage_processCoverTestDir runtime/coverage.processCoverTestDir
-func runtime_coverage_processCoverTestDir(dir string, cfile string, cmode string, cpkgs string) error
-
-//go:linkname testing_registerCover2 testing.registerCover2
-func testing_registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64)
-
-//go:linkname runtime_coverage_markProfileEmitted runtime/coverage.markProfileEmitted
-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) {
- var err error
- if gocoverdir == "" {
- gocoverdir, err = os.MkdirTemp("", "gocoverdir")
- if err != nil {
- return "error setting GOCOVERDIR: bad os.MkdirTemp return", err
- }
- defer os.RemoveAll(gocoverdir)
- }
- runtime_coverage_markProfileEmitted(true)
- cmode := {{printf "%q" .Cover.Mode}}
- if err := runtime_coverage_processCoverTestDir(gocoverdir, coverprofile, cmode, {{printf "%q" .Covered}}); err != nil {
- return "error generating coverage report", err
- }
- return "", nil
-}
+ testdeps.CoverMode = {{printf "%q" .Cover.Mode}}
+ testdeps.Covered = {{printf "%q" .Covered}}
{{end}}
+ testdeps.ImportPath = {{.ImportPath | printf "%q"}}
+}
func main() {
-{{if .Cover}}
- testing_registerCover2({{printf "%q" .Cover.Mode}}, coverTearDown, runtime_coverage_snapshot)
-{{end}}
m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples)
{{with .TestMain}}
{{.Package}}.{{.Name}}(m)
internal/godebug, math/rand, encoding/hex, crypto/sha256
< internal/fuzz;
- internal/fuzz, internal/testlog, runtime/pprof, regexp
- < testing/internal/testdeps;
-
OS, flag, testing, internal/cfg, internal/platform, internal/goroot
< internal/testenv;
internal/coverage/decodecounter, internal/coverage/decodemeta,
internal/coverage/encodecounter, internal/coverage/encodemeta,
internal/coverage/pods
+ < internal/coverage/cfile
< runtime/coverage;
+ internal/coverage/cfile, internal/fuzz, internal/testlog, runtime/pprof, regexp
+ < testing/internal/testdeps;
+
# Test-only packages can have anything they want
CGO, internal/syscall/unix < net/internal/cgotest;
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package coverage
+package cfile
import (
"fmt"
"unsafe"
)
-// WriteMetaDir writes a coverage meta-data file for the currently
-// running program to the directory specified in 'dir'. An error will
-// be returned if the operation can't be completed successfully (for
-// example, if the currently running program was not built with
-// "-cover", or if the directory does not exist).
+// WriteMetaDir implements [runtime/coverage.WriteMetaDir].
func WriteMetaDir(dir string) error {
if !finalHashComputed {
return fmt.Errorf("error: no meta-data available (binary not built with -cover?)")
return emitMetaDataToDirectory(dir, getCovMetaList())
}
-// WriteMeta writes the meta-data content (the payload that would
-// normally be emitted to a meta-data file) for the currently running
-// program to the writer 'w'. An error will be returned if the
-// operation can't be completed successfully (for example, if the
-// currently running program was not built with "-cover", or if a
-// write fails).
+// WriteMeta implements [runtime/coverage.WriteMeta].
func WriteMeta(w io.Writer) error {
if w == nil {
return fmt.Errorf("error: nil writer in WriteMeta")
return writeMetaData(w, ml, cmode, cgran, finalHash)
}
-// WriteCountersDir writes a coverage counter-data file for the
-// currently running program to the directory specified in 'dir'. An
-// error will be returned if the operation can't be completed
-// successfully (for example, if the currently running program was not
-// built with "-cover", or if the directory does not exist). The
-// counter data written will be a snapshot taken at the point of the
-// call.
+// WriteCountersDir implements [runtime/coverage.WriteCountersDir].
func WriteCountersDir(dir string) error {
if cmode != coverage.CtrModeAtomic {
return fmt.Errorf("WriteCountersDir invoked for program built with -covermode=%s (please use -covermode=atomic)", cmode.String())
return emitCounterDataToDirectory(dir)
}
-// WriteCounters writes coverage counter-data content for the
-// currently running program to the writer 'w'. An error will be
-// returned if the operation can't be completed successfully (for
-// example, if the currently running program was not built with
-// "-cover", or if a write fails). The counter data written will be a
-// snapshot taken at the point of the invocation.
+// WriteCounters implements [runtime/coverage.WriteCounters].
func WriteCounters(w io.Writer) error {
if w == nil {
return fmt.Errorf("error: nil writer in WriteCounters")
return s.emitCounterDataToWriter(w)
}
-// ClearCounters clears/resets all coverage counter variables in the
-// currently running program. It returns an error if the program in
-// question was not built with the "-cover" flag. Clearing of coverage
-// counters is also not supported for programs not using atomic
-// counter mode (see more detailed comments below for the rationale
-// here).
+// ClearCounters implements [runtime/coverage.ClearCounters].
func ClearCounters() error {
cl := getCovCounterList()
if len(cl) == 0 {
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package coverage
+// Package cfile implements management of coverage files.
+// It provides functionality exported in runtime/coverage as well as
+// additional functionality used directly by package testing
+// through testing/internal/testdeps.
+package cfile
import (
"crypto/md5"
// getCovMetaList returns a list of meta-data blobs registered
// for the currently executing instrumented program. It is defined in the
// runtime.
+//go:linkname getCovMetaList
func getCovMetaList() []rtcov.CovMetaBlob
// getCovCounterList returns a list of counter-data blobs registered
// for the currently executing instrumented program. It is defined in the
// runtime.
+//go:linkname getCovCounterList
func getCovCounterList() []rtcov.CovCounterBlob
// getCovPkgMap returns a map storing the remapped package IDs for
// hard-coded runtime packages (see internal/coverage/pkgid.go for
// more on why hard-coded package IDs are needed). This function
// is defined in the runtime.
+//go:linkname getCovPkgMap
func getCovPkgMap() map[int]int
// emitState holds useful state information during the emit process.
return nil
}
-// markProfileEmitted is injected to testmain via linkname.
-//go:linkname markProfileEmitted
-
-// markProfileEmitted signals the runtime/coverage machinery that
+// MarkProfileEmitted signals the coverage machinery that
// coverage data output files have already been written out, and there
// is no need to take any additional action at exit time. This
-// function is called (via linknamed reference) from the
-// coverage-related boilerplate code in _testmain.go emitted for go
-// unit tests.
-func markProfileEmitted(val bool) {
+// function is called from the coverage-related boilerplate code in _testmain.go
+// emitted for go unit tests.
+func MarkProfileEmitted(val bool) {
covProfileAlreadyEmitted = val
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package coverage
+package cfile
import (
"fmt"
cmd.Dir = filepath.Join("testdata", "issue56006")
b, err := cmd.CombinedOutput()
if err != nil {
- t.Fatalf("go test -cover -race failed: %v", err)
+ t.Fatalf("go test -cover -race failed: %v\n%s", err, b)
}
// Don't want to see any data races in output.
cmd.Dir = filepath.Join("testdata", "issue59563")
b, err := cmd.CombinedOutput()
if err != nil {
- t.Fatalf("go test -cover failed: %v", err)
+ t.Fatalf("go test -cover failed: %v\n%s", err, b)
}
cmd = exec.Command(testenv.GoToolPath(t), "tool", "cover", "-func="+ppath)
// We're only interested in the specific function "large" for
// the testcase being built. See the #59563 for details on why
// size matters.
- if !(strings.HasPrefix(f[0], "runtime/coverage/testdata/issue59563/repro.go") && strings.Contains(line, "large")) {
+ if !(strings.HasPrefix(f[0], "internal/coverage/cfile/testdata/issue59563/repro.go") && strings.Contains(line, "large")) {
continue
}
nfound++
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package coverage
+package cfile
import _ "unsafe"
-// initHook is invoked from the main package "init" routine in
+// InitHook is invoked from the main package "init" routine in
// programs built with "-cover". This function is intended to be
-// called only by the compiler.
+// called only by the compiler (via runtime/coverage.initHook).
//
// If 'istest' is false, it indicates we're building a regular program
// ("go build -cover ..."), in which case we immediately try to write
// emitCounterData as exit hooks. In the normal case (e.g. regular "go
// test -cover" run) the testmain.go boilerplate will run at the end
// of the test, write out the coverage percentage, and then invoke
-// markProfileEmitted() to indicate that no more work needs to be
+// MarkProfileEmitted to indicate that no more work needs to be
// done. If however that call is never made, this is a sign that the
// test binary is being used as a replacement binary for the tool
// being tested, hence we do want to run exit hooks when the program
// terminates.
-func initHook(istest bool) {
+func InitHook(istest bool) {
// Note: hooks are run in reverse registration order, so
// register the counter data hook before the meta-data hook
// (in the case where two hooks are needed).
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package coverage
+package cfile
import (
"encoding/json"
"unsafe"
)
-// processCoverTestDir is injected in testmain.
-//go:linkname processCoverTestDir
-
-// processCoverTestDir is called (via a linknamed reference) from
+// ProcessCoverTestDir is called from
// testmain code when "go test -cover" is in effect. It is not
// intended to be used other than internally by the Go command's
// generated code.
-func processCoverTestDir(dir string, cfile string, cm string, cpkg string) error {
- return processCoverTestDirInternal(dir, cfile, cm, cpkg, os.Stdout)
-}
-
-// processCoverTestDirInternal is an io.Writer version of processCoverTestDir,
-// exposed for unit testing.
-func processCoverTestDirInternal(dir string, cfile string, cm string, cpkg string, w io.Writer) error {
+func ProcessCoverTestDir(dir string, cfile string, cm string, cpkg string, w io.Writer) error {
cmode := coverage.ParseCounterMode(cm)
if cmode == coverage.CtrModeInvalid {
return fmt.Errorf("invalid counter mode %q", cm)
return nil
}
-// snapshot is injected in testmain.
-//go:linkname snapshot
-
-// snapshot returns a snapshot of coverage percentage at a moment of
+// Snapshot returns a snapshot of coverage percentage at a moment of
// time within a running test, so as to support the testing.Coverage()
// function. This version doesn't examine coverage meta-data, so the
// result it returns will be less accurate (more "slop") due to the
// fact that we don't look at the meta data to see how many statements
// are associated with each counter.
-func snapshot() float64 {
+func Snapshot() float64 {
cl := getCovCounterList()
if len(cl) == 0 {
// no work to do here.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package coverage
+package cfile
import (
"encoding/json"
}
// TestTestSupport does a basic verification of the functionality in
-// runtime/coverage.processCoverTestDir (doing this here as opposed to
+// ProcessCoverTestDir (doing this here as opposed to
// relying on other test paths will provide a better signal when
// running "go test -cover" for this package).
func TestTestSupport(t *testing.T) {
textfile := filepath.Join(t.TempDir(), "file.txt")
var sb strings.Builder
- err := processCoverTestDirInternal(tgcd, textfile,
+ err := ProcessCoverTestDir(tgcd, textfile,
testing.CoverMode(), "", &sb)
if err != nil {
t.Fatalf("bad: %v", err)
// coverage is not enabled, the hook is designed to just return
// zero.
func TestCoverageSnapshot(t *testing.T) {
- C1 := snapshot()
+ C1 := Snapshot()
thisFunctionOnlyCalledFromSnapshotTest(15)
- C2 := snapshot()
+ C2 := Snapshot()
cond := "C1 > C2"
val := C1 > C2
if testing.CoverMode() != "" {
// Kick off guts of test.
var sb strings.Builder
textfile := filepath.Join(td, "file2.txt")
- err = processCoverTestDirInternal(tgcd, textfile,
+ err = ProcessCoverTestDir(tgcd, textfile,
testing.CoverMode(), "", &sb)
if err != nil {
t.Fatalf("bad: %v", err)
--- /dev/null
+// 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.
+
+package coverage
+
+import (
+ "internal/coverage/cfile"
+ "io"
+)
+
+// initHook is invoked from main.init in programs built with -cover.
+// The call is emitted by the compiler.
+func initHook(istest bool) {
+ cfile.InitHook(istest)
+}
+
+// WriteMetaDir writes a coverage meta-data file for the currently
+// running program to the directory specified in 'dir'. An error will
+// be returned if the operation can't be completed successfully (for
+// example, if the currently running program was not built with
+// "-cover", or if the directory does not exist).
+func WriteMetaDir(dir string) error {
+ return cfile.WriteMetaDir(dir)
+}
+
+// WriteMeta writes the meta-data content (the payload that would
+// normally be emitted to a meta-data file) for the currently running
+// program to the writer 'w'. An error will be returned if the
+// operation can't be completed successfully (for example, if the
+// currently running program was not built with "-cover", or if a
+// write fails).
+func WriteMeta(w io.Writer) error {
+ return cfile.WriteMeta(w)
+}
+
+// WriteCountersDir writes a coverage counter-data file for the
+// currently running program to the directory specified in 'dir'. An
+// error will be returned if the operation can't be completed
+// successfully (for example, if the currently running program was not
+// built with "-cover", or if the directory does not exist). The
+// counter data written will be a snapshot taken at the point of the
+// call.
+func WriteCountersDir(dir string) error {
+ return cfile.WriteCountersDir(dir)
+}
+
+// WriteCounters writes coverage counter-data content for the
+// currently running program to the writer 'w'. An error will be
+// returned if the operation can't be completed successfully (for
+// example, if the currently running program was not built with
+// "-cover", or if a write fails). The counter data written will be a
+// snapshot taken at the point of the invocation.
+func WriteCounters(w io.Writer) error {
+ return cfile.WriteCounters(w)
+}
+
+// ClearCounters clears/resets all coverage counter variables in the
+// currently running program. It returns an error if the program in
+// question was not built with the "-cover" flag. Clearing of coverage
+// counters is also not supported for programs not using atomic
+// counter mode (see more detailed comments below for the rationale
+// here).
+func ClearCounters() error {
+ return cfile.ClearCounters()
+}
+++ /dev/null
-// 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.
-
-// The runtime package uses //go:linkname to push a few functions into this
-// package but we still need a .s file so the Go tool does not pass -complete
-// to 'go tool compile' so the latter does not complain about Go functions
-// with no bodies.
"unsafe"
)
-//go:linkname runtime_coverage_getCovCounterList runtime/coverage.getCovCounterList
-func runtime_coverage_getCovCounterList() []rtcov.CovCounterBlob {
+//go:linkname coverage_getCovCounterList internal/coverage/cfile.getCovCounterList
+func coverage_getCovCounterList() []rtcov.CovCounterBlob {
res := []rtcov.CovCounterBlob{}
u32sz := unsafe.Sizeof(uint32(0))
for datap := &firstmoduledata; datap != nil; datap = datap.next {
return uint32(slot + 1)
}
-//go:linkname runtime_coverage_getCovMetaList runtime/coverage.getCovMetaList
-func runtime_coverage_getCovMetaList() []rtcov.CovMetaBlob {
+//go:linkname coverage_getCovMetaList internal/coverage/cfile.getCovMetaList
+func coverage_getCovMetaList() []rtcov.CovMetaBlob {
return covMeta.metaList
}
-//go:linkname runtime_coverage_getCovPkgMap runtime/coverage.getCovPkgMap
-func runtime_coverage_getCovPkgMap() map[int]int {
+//go:linkname coverage_getCovPkgMap internal/coverage/cfile.getCovPkgMap
+func coverage_getCovPkgMap() map[int]int {
return covMeta.pkgMap
}
import (
"bufio"
"context"
+ "internal/coverage/cfile"
"internal/fuzz"
"internal/testlog"
"io"
"time"
)
+// Cover indicates whether coverage is enabled.
+var Cover bool
+
// TestDeps is an implementation of the testing.testDeps interface,
// suitable for passing to [testing.MainStart].
type TestDeps struct{}
func (TestDeps) SnapshotCoverage() {
fuzz.SnapshotCoverage()
}
+
+var CoverMode string
+var Covered string
+
+func (TestDeps) InitRuntimeCoverage() (mode string, tearDown func(string, string) (string, error), snapcov func() float64) {
+ if CoverMode == "" {
+ return
+ }
+ return CoverMode, coverTearDown, cfile.Snapshot
+}
+
+func coverTearDown(coverprofile string, gocoverdir string) (string, error) {
+ var err error
+ if gocoverdir == "" {
+ gocoverdir, err = os.MkdirTemp("", "gocoverdir")
+ if err != nil {
+ return "error setting GOCOVERDIR: bad os.MkdirTemp return", err
+ }
+ defer os.RemoveAll(gocoverdir)
+ }
+ cfile.MarkProfileEmitted(true)
+ cmode := CoverMode
+ if err := cfile.ProcessCoverTestDir(gocoverdir, coverprofile, cmode, Covered, os.Stdout); err != nil {
+ return "error generating coverage report", err
+ }
+ return "", nil
+}
snapshotcov func() float64
}
-// registerCover2 is injected in testmain.
-//go:linkname registerCover2
-
-// 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
+// registerCover2 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) {
+ if mode == "" {
+ return
+ }
cover2.mode = mode
cover2.tearDown = tearDown
cover2.snapshotcov = snapcov
func (f matchStringOnly) ResetCoverage() {}
func (f matchStringOnly) SnapshotCoverage() {}
+func (f matchStringOnly) InitRuntimeCoverage() (mode string, tearDown func(string, string) (string, error), snapcov func() float64) {
+ return
+}
+
// Main is an internal function, part of the implementation of the "go test" command.
// It was exported because it is cross-package and predates "internal" packages.
// It is no longer used by "go test" but preserved, as much as possible, for other
CheckCorpus([]any, []reflect.Type) error
ResetCoverage()
SnapshotCoverage()
+ InitRuntimeCoverage() (mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64)
}
// MainStart is meant for use by tests generated by 'go test'.
// 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())
Init()
return &M{
deps: deps,