if regexp.MustCompile(`[^0-9]0\.0%`).MatchString(data) {
tg.t.Error("some coverage results are 0.0%")
}
- tg.t.Log(data)
}
func TestCoverageRuns(t *testing.T) {
}
// Check that coverage analysis uses set mode.
+// Also check that coverage profiles merge correctly.
func TestCoverageUsesSetMode(t *testing.T) {
if testing.Short() {
t.Skip("don't build libraries for coverage in short mode")
tg := testgo(t)
defer tg.cleanup()
tg.creatingTemp("testdata/cover.out")
- tg.run("test", "-short", "-cover", "encoding/binary", "-coverprofile=testdata/cover.out")
+ tg.run("test", "-short", "-cover", "encoding/binary", "errors", "-coverprofile=testdata/cover.out")
data := tg.getStdout() + tg.getStderr()
if out, err := ioutil.ReadFile("testdata/cover.out"); err != nil {
t.Error(err)
if !bytes.Contains(out, []byte("mode: set")) {
t.Error("missing mode: set")
}
+ if !bytes.Contains(out, []byte("errors.go")) {
+ t.Error("missing errors.go")
+ }
+ if !bytes.Contains(out, []byte("binary.go")) {
+ t.Error("missing binary.go")
+ }
+ if bytes.Count(out, []byte("mode: set")) != 1 {
+ t.Error("too many mode: set")
+ }
}
checkCoverage(tg, data)
}
--- /dev/null
+// Copyright 2017 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 test
+
+import (
+ "cmd/go/internal/base"
+ "fmt"
+ "io"
+ "os"
+ "sync"
+)
+
+var coverMerge struct {
+ f *os.File
+ sync.Mutex // for f.Write
+}
+
+// initCoverProfile initializes the test coverage profile.
+// It must be run before any calls to mergeCoverProfile or closeCoverProfile.
+// Using this function clears the profile in case it existed from a previous run,
+// or in case it doesn't exist and the test is going to fail to create it (or not run).
+func initCoverProfile() {
+ if testCoverProfile == "" {
+ return
+ }
+
+ // No mutex - caller's responsibility to call with no racing goroutines.
+ f, err := os.Create(testCoverProfile)
+ if err != nil {
+ base.Fatalf("%v", err)
+ }
+ _, err = fmt.Fprintf(f, "mode: %s\n", testCoverMode)
+ if err != nil {
+ base.Fatalf("%v", err)
+ }
+ coverMerge.f = f
+}
+
+// mergeCoverProfile merges file into the profile stored in testCoverProfile.
+// It prints any errors it encounters to ew.
+func mergeCoverProfile(ew io.Writer, file string) {
+ if coverMerge.f == nil {
+ return
+ }
+ coverMerge.Lock()
+ defer coverMerge.Unlock()
+
+ expect := fmt.Sprintf("mode: %s\n", testCoverMode)
+ buf := make([]byte, len(expect))
+ r, err := os.Open(file)
+ if err != nil {
+ // Test did not create profile, which is OK.
+ return
+ }
+ defer r.Close()
+
+ n, err := io.ReadFull(r, buf)
+ if n == 0 {
+ return
+ }
+ if err != nil || string(buf) != expect {
+ fmt.Fprintf(ew, "error: test wrote malformed coverage profile.\n")
+ return
+ }
+ _, err = io.Copy(coverMerge.f, r)
+ if err != nil {
+ fmt.Fprintf(ew, "error: saving coverage profile: %v\n", err)
+ }
+}
+
+func closeCoverProfile() {
+ if coverMerge.f == nil {
+ return
+ }
+ if err := coverMerge.f.Close(); err != nil {
+ base.Errorf("closing coverage profile: %v", err)
+ }
+}
}
var (
- testC bool // -c flag
- testCover bool // -cover flag
- testCoverMode string // -covermode flag
- testCoverPaths []string // -coverpkg flag
- testCoverPkgs []*load.Package // -coverpkg flag
- testO string // -o flag
- testProfile bool // some profiling flag
- testNeedBinary bool // profile needs to keep binary around
- testJSON bool // -json flag
- testV bool // -v flag
- testTimeout string // -timeout flag
- testArgs []string
- testBench bool
- testList bool
- testShowPass bool // show passing output
- testVetList string // -vet flag
- pkgArgs []string
- pkgs []*load.Package
+ testC bool // -c flag
+ testCover bool // -cover flag
+ testCoverMode string // -covermode flag
+ testCoverPaths []string // -coverpkg flag
+ testCoverPkgs []*load.Package // -coverpkg flag
+ testCoverProfile string // -coverprofile flag
+ testO string // -o flag
+ testProfile string // profiling flag that limits test to one package
+ testNeedBinary bool // profile needs to keep binary around
+ testJSON bool // -json flag
+ testV bool // -v flag
+ testTimeout string // -timeout flag
+ testArgs []string
+ testBench bool
+ testList bool
+ testShowPass bool // show passing output
+ testVetList string // -vet flag
+ pkgArgs []string
+ pkgs []*load.Package
testKillTimeout = 10 * time.Minute
)
if testO != "" && len(pkgs) != 1 {
base.Fatalf("cannot use -o flag with multiple packages")
}
- if testProfile && len(pkgs) != 1 {
- base.Fatalf("cannot use test profile flag with multiple packages")
+ if testProfile != "" && len(pkgs) != 1 {
+ base.Fatalf("cannot use %s flag with multiple packages", testProfile)
}
+ initCoverProfile()
+ defer closeCoverProfile()
// If a test timeout was given and is parseable, set our kill timeout
// to that timeout plus one minute. This is a backup alarm in case
Package: p,
IgnoreFail: true,
TryCache: c.tryCache,
+ Objdir: testDir,
}
if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 {
addTestVet(b, ptest, runAction, installAction)
return nil
}
+ if testCoverProfile != "" {
+ // Write coverage to temporary profile, for merging later.
+ for i, arg := range args {
+ if strings.HasPrefix(arg, "-test.coverprofile=") {
+ args[i] = "-test.coverprofile=" + a.Objdir + "_cover_.out"
+ }
+ }
+ }
+
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = a.Package.Dir
cmd.Env = base.EnvForDir(cmd.Dir, cfg.OrigEnv)
out := buf.Bytes()
a.TestOutput = &buf
t := fmt.Sprintf("%.3fs", time.Since(t0).Seconds())
+
+ mergeCoverProfile(cmd.Stdout, a.Objdir+"_cover_.out")
+
if err == nil {
norun := ""
if !testShowPass {
case "timeout":
testTimeout = value
case "blockprofile", "cpuprofile", "memprofile", "mutexprofile":
- testProfile = true
+ testProfile = "-" + f.Name
testNeedBinary = true
case "trace":
- testProfile = true
+ testProfile = "-trace"
case "coverpkg":
testCover = true
if value == "" {
}
case "coverprofile":
testCover = true
- testProfile = true
+ testCoverProfile = value
case "covermode":
switch value {
case "set", "count", "atomic":
}
// Tell the test what directory we're running in, so it can write the profiles there.
- if testProfile && outputDir == "" {
+ if testProfile != "" && outputDir == "" {
dir, err := os.Getwd()
if err != nil {
base.Fatalf("error from os.Getwd: %s", err)