internal/testenv: reject the resolved 'go' command if it does not match runtime.GOROOT

Many tests in x/tools invoke the 'go' command found from $PATH.
If that command does not match the 'go' command used to invoke 'go test',
the result will be misleading.

Instead of silently accepting the mismatched result, check the 'go'
tool's self-reported GOROOT and reject it if it doesn't match the 'go'
tool used to invoke 'go test'.

That rejection will cause the x/tools tests to fail if x/tools is the main module.

Updates golang/go#35505

Change-Id: I581906468ef736fad42a0164376a07f876907621
Reviewed-on: https://go-review.googlesource.com/c/tools/+/206517
Run-TryBot: Bryan C. Mills <bcmills@google.com>
Reviewed-by: Jay Conrod <jayconrod@google.com>
diff --git a/internal/testenv/testenv.go b/internal/testenv/testenv.go
index 295cc45..0cc90d2 100644
--- a/internal/testenv/testenv.go
+++ b/internal/testenv/testenv.go
@@ -13,6 +13,7 @@
 	"os/exec"
 	"runtime"
 	"strings"
+	"sync"
 )
 
 // Testing is an abstraction of a *testing.T.
@@ -32,11 +33,17 @@
 // be development versions.
 var packageMainIsDevel = func() bool { return true }
 
+var checkGoGoroot struct {
+	once sync.Once
+	err  error
+}
+
 func hasTool(tool string) error {
 	_, err := exec.LookPath(tool)
 	if err != nil {
 		return err
 	}
+
 	switch tool {
 	case "patch":
 		// check that the patch tools supports the -o argument
@@ -50,7 +57,28 @@
 		if err := cmd.Run(); err != nil {
 			return err
 		}
+
+	case "go":
+		checkGoGoroot.once.Do(func() {
+			// Ensure that the 'go' command found by exec.LookPath is from the correct
+			// GOROOT. Otherwise, 'some/path/go test ./...' will test against some
+			// version of the 'go' binary other than 'some/path/go', which is almost
+			// certainly not what the user intended.
+			out, err := exec.Command(tool, "env", "GOROOT").CombinedOutput()
+			if err != nil {
+				checkGoGoroot.err = err
+				return
+			}
+			GOROOT := strings.TrimSpace(string(out))
+			if GOROOT != runtime.GOROOT() {
+				checkGoGoroot.err = fmt.Errorf("'go env GOROOT' does not match runtime.GOROOT:\n\tgo env: %s\n\tGOROOT: %s", GOROOT, runtime.GOROOT())
+			}
+		})
+		if checkGoGoroot.err != nil {
+			return checkGoGoroot.err
+		}
 	}
+
 	return nil
 }