cmd/cover: use -toolexec in tests to run newly built cover program

This ensures that "go test cmd/cover" tests the current cover program,
not the installed cover program.

Change-Id: I58e718ded7eb1cd8da448d0194262209bb025b20
Reviewed-on: https://go-review.googlesource.com/c/153058
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/cmd/cover/cover_test.go b/src/cmd/cover/cover_test.go
index aebe6f8..3e5c076 100644
--- a/src/cmd/cover/cover_test.go
+++ b/src/cmd/cover/cover_test.go
@@ -30,9 +30,10 @@
 
 var (
 	// Input files.
-	testMain     = filepath.Join(testdata, "main.go")
-	testTest     = filepath.Join(testdata, "test.go")
-	coverProfile = filepath.Join(testdata, "profile.cov")
+	testMain       = filepath.Join(testdata, "main.go")
+	testTest       = filepath.Join(testdata, "test.go")
+	coverProfile   = filepath.Join(testdata, "profile.cov")
+	toolexecSource = filepath.Join(testdata, "toolexec.go")
 
 	// The HTML test files are in a separate directory
 	// so they are a complete package.
@@ -53,11 +54,17 @@
 	// testcover is a newly built version of the cover program.
 	testcover string
 
-	// testcoverErr records an error building testcover.
+	// toolexec is a program to use as the go tool's -toolexec argument.
+	toolexec string
+
+	// testcoverErr records an error building testcover or toolexec.
 	testcoverErr error
 
 	// testcoverOnce is used to build testcover once.
 	testcoverOnce sync.Once
+
+	// toolexecArg is the argument to pass to the go tool.
+	toolexecArg string
 )
 
 var debug = flag.Bool("debug", false, "keep rewritten files for debugging")
@@ -94,14 +101,43 @@
 	t.Helper()
 	testenv.MustHaveGoBuild(t)
 	testcoverOnce.Do(func() {
-		testcover = filepath.Join(testTempDir, "testcover.exe")
-		t.Logf("running [go build -o %s]", testcover)
-		out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", testcover).CombinedOutput()
-		t.Logf("%s", out)
-		testcoverErr = err
+		var wg sync.WaitGroup
+		wg.Add(2)
+
+		var err1, err2 error
+		go func() {
+			defer wg.Done()
+			testcover = filepath.Join(testTempDir, "cover.exe")
+			t.Logf("running [go build -o %s]", testcover)
+			out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", testcover).CombinedOutput()
+			if len(out) > 0 {
+				t.Logf("%s", out)
+			}
+			err1 = err
+		}()
+
+		go func() {
+			defer wg.Done()
+			toolexec = filepath.Join(testTempDir, "toolexec.exe")
+			t.Logf("running [go -build -o %s %s]", toolexec, toolexecSource)
+			out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", toolexec, toolexecSource).CombinedOutput()
+			if len(out) > 0 {
+				t.Logf("%s", out)
+			}
+			err2 = err
+		}()
+
+		wg.Wait()
+
+		testcoverErr = err1
+		if err2 != nil && err1 == nil {
+			testcoverErr = err2
+		}
+
+		toolexecArg = "-toolexec=" + toolexec + " " + testcover
 	})
 	if testcoverErr != nil {
-		t.Fatal("failed to build testcover program:", testcoverErr)
+		t.Fatal("failed to build testcover or toolexec program:", testcoverErr)
 	}
 }
 
@@ -335,7 +371,7 @@
 	buildCover(t)
 
 	// go test -coverprofile testdata/html/html.cov cmd/cover/testdata/html
-	cmd := exec.Command(testenv.GoToolPath(t), "test", "-coverprofile", htmlProfile, "cmd/cover/testdata/html")
+	cmd := exec.Command(testenv.GoToolPath(t), "test", toolexecArg, "-coverprofile", htmlProfile, "cmd/cover/testdata/html")
 	run(cmd, t)
 	// testcover -html testdata/html/html.cov -o testdata/html/html.html
 	cmd = exec.Command(testcover, "-html", htmlProfile, "-o", htmlHTML)
diff --git a/src/cmd/cover/testdata/toolexec.go b/src/cmd/cover/testdata/toolexec.go
new file mode 100644
index 0000000..1769efe
--- /dev/null
+++ b/src/cmd/cover/testdata/toolexec.go
@@ -0,0 +1,33 @@
+// Copyright 2018 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 toolexec program is a helper program for cmd/cover tests.
+// It is used so that the go tool will call the newly built version
+// of the cover program, rather than the installed one.
+//
+// The tests arrange to run the go tool with the argument
+//    -toolexec="/path/to/toolexec /path/to/testcover"
+// The go tool will invoke this program (compiled into /path/to/toolexec)
+// with the arguments shown above followed by the command to run.
+// This program will check whether it is expected to run the cover
+// program, and if so replace it with /path/to/testcover.
+package main
+
+import (
+	"os"
+	"os/exec"
+	"strings"
+)
+
+func main() {
+	if strings.HasSuffix(strings.TrimSuffix(os.Args[2], ".exe"), "cover") {
+		os.Args[2] = os.Args[1]
+	}
+	cmd := exec.Command(os.Args[2], os.Args[3:]...)
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Run(); err != nil {
+		os.Exit(1)
+	}
+}