internal/testenv: add GOROOT and use it to fix tests broken with -trimpath

This fixes many (but not all) of the tests that currently fail
(due to a bogus path reported by runtime.GOROOT) when run with
'go test -trimpath std cmd'.

Updates #51461

Change-Id: Ia2cc05705529c4859e7928f32eeceed647f2e986
Reviewed-on: https://go-review.googlesource.com/c/go/+/391806
Trust: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Bryan C. Mills 2022-03-09 17:19:23 -05:00 committed by Bryan Mills
parent 9ac75d3951
commit 58631ba54f
22 changed files with 242 additions and 83 deletions

View file

@ -34,9 +34,11 @@ func goCmd() string {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
exeSuffix = ".exe" exeSuffix = ".exe"
} }
path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) if goroot := build.Default.GOROOT; goroot != "" {
if _, err := os.Stat(path); err == nil { path := filepath.Join(goroot, "bin", "go"+exeSuffix)
return path if _, err := os.Stat(path); err == nil {
return path
}
} }
return "go" return "go"
} }
@ -127,6 +129,10 @@ var internalPkg = regexp.MustCompile(`(^|/)internal($|/)`)
func main() { func main() {
flag.Parse() flag.Parse()
if build.Default.GOROOT == "" {
log.Fatalf("GOROOT not found. (If binary was built with -trimpath, $GOROOT must be set.)")
}
if !strings.Contains(runtime.Version(), "weekly") && !strings.Contains(runtime.Version(), "devel") { if !strings.Contains(runtime.Version(), "weekly") && !strings.Contains(runtime.Version(), "devel") {
if *nextFiles != "" { if *nextFiles != "" {
fmt.Printf("Go version is %q, ignoring -next %s\n", runtime.Version(), *nextFiles) fmt.Printf("Go version is %q, ignoring -next %s\n", runtime.Version(), *nextFiles)

View file

@ -9,6 +9,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"go/build" "go/build"
"internal/testenv"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@ -22,6 +23,7 @@ func TestMain(m *testing.M) {
for _, c := range contexts { for _, c := range contexts {
c.Compiler = build.Default.Compiler c.Compiler = build.Default.Compiler
} }
build.Default.GOROOT = testenv.GOROOT(nil)
// Warm up the import cache in parallel. // Warm up the import cache in parallel.
var wg sync.WaitGroup var wg sync.WaitGroup

View file

@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"cmd/compile/internal/types2" "cmd/compile/internal/types2"
"fmt" "fmt"
"go/build"
"internal/goexperiment" "internal/goexperiment"
"internal/testenv" "internal/testenv"
"os" "os"
@ -19,6 +20,11 @@ import (
"time" "time"
) )
func TestMain(m *testing.M) {
build.Default.GOROOT = testenv.GOROOT(nil)
os.Exit(m.Run())
}
// skipSpecialPlatforms causes the test to be skipped for platforms where // skipSpecialPlatforms causes the test to be skipped for platforms where
// builders (build.golang.org) don't have access to compiled packages for // builders (build.golang.org) don't have access to compiled packages for
// import. // import.
@ -62,7 +68,7 @@ func testPath(t *testing.T, path, srcDir string) *types2.Package {
const maxTime = 30 * time.Second const maxTime = 30 * time.Second
func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir) dirname := filepath.Join(testenv.GOROOT(t), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
list, err := os.ReadDir(dirname) list, err := os.ReadDir(dirname)
if err != nil { if err != nil {
t.Fatalf("testDir(%s): %s", dirname, err) t.Fatalf("testDir(%s): %s", dirname, err)

View file

@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"flag" "flag"
"fmt" "fmt"
"internal/testenv"
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
"regexp" "regexp"
@ -74,12 +75,14 @@ func TestStdLib(t *testing.T) {
lines uint lines uint
} }
goroot := testenv.GOROOT(t)
results := make(chan parseResult) results := make(chan parseResult)
go func() { go func() {
defer close(results) defer close(results)
for _, dir := range []string{ for _, dir := range []string{
filepath.Join(runtime.GOROOT(), "src"), filepath.Join(goroot, "src"),
filepath.Join(runtime.GOROOT(), "misc"), filepath.Join(goroot, "misc"),
} { } {
walkDirs(t, dir, func(filename string) { walkDirs(t, dir, func(filename string) {
if skipRx != nil && skipRx.MatchString(filename) { if skipRx != nil && skipRx.MatchString(filename) {

View file

@ -0,0 +1,17 @@
// 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 types2_test
import (
"go/build"
"internal/testenv"
"os"
"testing"
)
func TestMain(m *testing.M) {
build.Default.GOROOT = testenv.GOROOT(nil)
os.Exit(m.Run())
}

View file

@ -15,7 +15,6 @@ import (
"internal/testenv" "internal/testenv"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -29,7 +28,7 @@ func TestStdlib(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
pkgCount := 0 pkgCount := 0
duration := walkPkgDirs(filepath.Join(runtime.GOROOT(), "src"), func(dir string, filenames []string) { duration := walkPkgDirs(filepath.Join(testenv.GOROOT(t), "src"), func(dir string, filenames []string) {
typecheck(t, dir, filenames) typecheck(t, dir, filenames)
pkgCount++ pkgCount++
}, t.Error) }, t.Error)
@ -162,7 +161,7 @@ func TestStdTest(t *testing.T) {
t.Skip("skipping in short mode") t.Skip("skipping in short mode")
} }
testTestDir(t, filepath.Join(runtime.GOROOT(), "test"), testTestDir(t, filepath.Join(testenv.GOROOT(t), "test"),
"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
"directive.go", // tests compiler rejection of bad directive placement - ignore "directive.go", // tests compiler rejection of bad directive placement - ignore
"directive2.go", // tests compiler rejection of bad directive placement - ignore "directive2.go", // tests compiler rejection of bad directive placement - ignore
@ -180,7 +179,7 @@ func TestStdFixed(t *testing.T) {
t.Skip("skipping in short mode") t.Skip("skipping in short mode")
} }
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"), testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "fixedbugs"),
"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
"issue6889.go", // gc-specific test "issue6889.go", // gc-specific test
"issue11362.go", // canonical import path check "issue11362.go", // canonical import path check
@ -204,7 +203,7 @@ func TestStdFixed(t *testing.T) {
func TestStdKen(t *testing.T) { func TestStdKen(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken")) testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "ken"))
} }
// Package paths of excluded packages. // Package paths of excluded packages.
@ -311,16 +310,13 @@ func (w *walker) walk(dir string) {
} }
// apply pkgh to the files in directory dir // apply pkgh to the files in directory dir
// but ignore files directly under $GOROOT/src (might be temporary test files). pkgFiles, err := pkgFilenames(dir)
if dir != filepath.Join(runtime.GOROOT(), "src") { if err != nil {
files, err := pkgFilenames(dir) w.errh(err)
if err != nil { return
w.errh(err) }
return if pkgFiles != nil {
} w.pkgh(dir, pkgFiles)
if files != nil {
w.pkgh(dir, files)
}
} }
// traverse subdirectories, but don't walk into testdata // traverse subdirectories, but don't walk into testdata

View file

@ -7,6 +7,8 @@ package main
import ( import (
"bytes" "bytes"
"flag" "flag"
"go/build"
"internal/testenv"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -21,6 +23,12 @@ func TestMain(m *testing.M) {
buildCtx.GOPATH = "" buildCtx.GOPATH = ""
testGOPATH = true // force GOPATH mode; module test is in cmd/go/testdata/script/mod_doc.txt testGOPATH = true // force GOPATH mode; module test is in cmd/go/testdata/script/mod_doc.txt
// Set GOROOT in case runtime.GOROOT is wrong (for example, if the test was
// built with -trimpath). dirsInit would identify it using 'go env GOROOT',
// but we can't be sure that the 'go' in $PATH is the right one either.
buildCtx.GOROOT = testenv.GOROOT(nil)
build.Default.GOROOT = testenv.GOROOT(nil)
// Add $GOROOT/src/cmd/doc/testdata explicitly so we can access its contents in the test. // Add $GOROOT/src/cmd/doc/testdata explicitly so we can access its contents in the test.
// Normally testdata directories are ignored, but sending it to dirs.scan directly is // Normally testdata directories are ignored, but sending it to dirs.scan directly is
// a hack that works around the check. // a hack that works around the check.

View file

@ -10,7 +10,6 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"testing" "testing"
) )
@ -18,7 +17,7 @@ import (
func TestScan(t *testing.T) { func TestScan(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
imports, testImports, err := ScanDir(filepath.Join(runtime.GOROOT(), "src/encoding/json"), Tags()) imports, testImports, err := ScanDir(filepath.Join(testenv.GOROOT(t), "src/encoding/json"), Tags())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -15,6 +15,7 @@ import (
"go/ast" "go/ast"
"go/printer" "go/printer"
"go/token" "go/token"
"internal/testenv"
"io" "io"
"io/fs" "io/fs"
"os" "os"
@ -130,7 +131,11 @@ func genFilenames(t *testing.T, filenames chan<- string) {
} }
// otherwise, test all Go files under *root // otherwise, test all Go files under *root
filepath.WalkDir(*root, handleFile) goroot := *root
if goroot == "" {
goroot = testenv.GOROOT(t)
}
filepath.WalkDir(goroot, handleFile)
} }
func TestAll(t *testing.T) { func TestAll(t *testing.T) {

View file

@ -15,7 +15,6 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"sync" "sync"
"testing" "testing"
@ -153,7 +152,7 @@ func TestAllDependencies(t *testing.T) {
// module version specified in GOROOT/src/cmd/go.mod. // module version specified in GOROOT/src/cmd/go.mod.
bundleDir := t.TempDir() bundleDir := t.TempDir()
r := runner{ r := runner{
Dir: filepath.Join(runtime.GOROOT(), "src/cmd"), Dir: filepath.Join(testenv.GOROOT(t), "src/cmd"),
Env: append(os.Environ(), modcacheEnv...), Env: append(os.Environ(), modcacheEnv...),
} }
r.run(t, goBin, "build", "-mod=readonly", "-o", bundleDir, "golang.org/x/tools/cmd/bundle") r.run(t, goBin, "build", "-mod=readonly", "-o", bundleDir, "golang.org/x/tools/cmd/bundle")
@ -183,9 +182,9 @@ func TestAllDependencies(t *testing.T) {
} }
}() }()
rel, err := filepath.Rel(runtime.GOROOT(), m.Dir) rel, err := filepath.Rel(testenv.GOROOT(t), m.Dir)
if err != nil { if err != nil {
t.Fatalf("filepath.Rel(%q, %q): %v", runtime.GOROOT(), m.Dir, err) t.Fatalf("filepath.Rel(%q, %q): %v", testenv.GOROOT(t), m.Dir, err)
} }
r := runner{ r := runner{
Dir: filepath.Join(gorootCopyDir, rel), Dir: filepath.Join(gorootCopyDir, rel),
@ -252,22 +251,22 @@ func packagePattern(modulePath string) string {
func makeGOROOTCopy(t *testing.T) string { func makeGOROOTCopy(t *testing.T) string {
t.Helper() t.Helper()
gorootCopyDir := t.TempDir() gorootCopyDir := t.TempDir()
err := filepath.Walk(runtime.GOROOT(), func(src string, info os.FileInfo, err error) error { err := filepath.Walk(testenv.GOROOT(t), func(src string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
if info.IsDir() && src == filepath.Join(runtime.GOROOT(), ".git") { if info.IsDir() && src == filepath.Join(testenv.GOROOT(t), ".git") {
return filepath.SkipDir return filepath.SkipDir
} }
rel, err := filepath.Rel(runtime.GOROOT(), src) rel, err := filepath.Rel(testenv.GOROOT(t), src)
if err != nil { if err != nil {
return fmt.Errorf("filepath.Rel(%q, %q): %v", runtime.GOROOT(), src, err) return fmt.Errorf("filepath.Rel(%q, %q): %v", testenv.GOROOT(t), src, err)
} }
dst := filepath.Join(gorootCopyDir, rel) dst := filepath.Join(gorootCopyDir, rel)
if info.IsDir() && (src == filepath.Join(runtime.GOROOT(), "bin") || if info.IsDir() && (src == filepath.Join(testenv.GOROOT(t), "bin") ||
src == filepath.Join(runtime.GOROOT(), "pkg")) { src == filepath.Join(testenv.GOROOT(t), "pkg")) {
// If the OS supports symlinks, use them instead // If the OS supports symlinks, use them instead
// of copying the bin and pkg directories. // of copying the bin and pkg directories.
if err := os.Symlink(src, dst); err == nil { if err := os.Symlink(src, dst); err == nil {
@ -435,14 +434,14 @@ func findGorootModules(t *testing.T) []gorootModule {
goBin := testenv.GoToolPath(t) goBin := testenv.GoToolPath(t)
goroot.once.Do(func() { goroot.once.Do(func() {
goroot.err = filepath.WalkDir(runtime.GOROOT(), func(path string, info fs.DirEntry, err error) error { goroot.err = filepath.WalkDir(testenv.GOROOT(t), func(path string, info fs.DirEntry, err error) error {
if err != nil { if err != nil {
return err return err
} }
if info.IsDir() && (info.Name() == "vendor" || info.Name() == "testdata") { if info.IsDir() && (info.Name() == "vendor" || info.Name() == "testdata") {
return filepath.SkipDir return filepath.SkipDir
} }
if info.IsDir() && path == filepath.Join(runtime.GOROOT(), "pkg") { if info.IsDir() && path == filepath.Join(testenv.GOROOT(t), "pkg") {
// GOROOT/pkg contains generated artifacts, not source code. // GOROOT/pkg contains generated artifacts, not source code.
// //
// In https://golang.org/issue/37929 it was observed to somehow contain // In https://golang.org/issue/37929 it was observed to somehow contain

View file

@ -8,7 +8,6 @@ import (
"internal/testenv" "internal/testenv"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime"
"testing" "testing"
) )
@ -22,7 +21,7 @@ func TestNooptCgoBuild(t *testing.T) {
testenv.MustHaveCGO(t) testenv.MustHaveCGO(t)
dir := t.TempDir() dir := t.TempDir()
cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", filepath.Join(dir, "a.out")) cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", filepath.Join(dir, "a.out"))
cmd.Dir = filepath.Join(runtime.GOROOT(), "src", "runtime", "testdata", "testprogcgo") cmd.Dir = filepath.Join(testenv.GOROOT(t), "src", "runtime", "testdata", "testprogcgo")
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
t.Logf("go build output: %s", out) t.Logf("go build output: %s", out)

View file

@ -66,7 +66,7 @@ func TestNonGoExecs(t *testing.T) {
"internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec", "internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec",
} }
for _, f := range testfiles { for _, f := range testfiles {
exepath := filepath.Join(runtime.GOROOT(), "src", f) exepath := filepath.Join(testenv.GOROOT(t), "src", f)
if strings.HasSuffix(f, ".base64") { if strings.HasSuffix(f, ".base64") {
tf, err := obscuretestdata.DecodeToTempFile(exepath) tf, err := obscuretestdata.DecodeToTempFile(exepath)
if err != nil { if err != nil {

View file

@ -5,16 +5,21 @@
package importer package importer
import ( import (
"go/build"
"go/token" "go/token"
"internal/testenv" "internal/testenv"
"io" "io"
"os" "os"
"os/exec" "os/exec"
"runtime"
"strings" "strings"
"testing" "testing"
) )
func TestMain(m *testing.M) {
build.Default.GOROOT = testenv.GOROOT(nil)
os.Exit(m.Run())
}
func TestForCompiler(t *testing.T) { func TestForCompiler(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
@ -49,7 +54,7 @@ func TestForCompiler(t *testing.T) {
// https://github.com/golang/go#28995 // https://github.com/golang/go#28995
mathBigInt := pkg.Scope().Lookup("Int") mathBigInt := pkg.Scope().Lookup("Int")
posn := fset.Position(mathBigInt.Pos()) // "$GOROOT/src/math/big/int.go:25:1" posn := fset.Position(mathBigInt.Pos()) // "$GOROOT/src/math/big/int.go:25:1"
filename := strings.Replace(posn.Filename, "$GOROOT", runtime.GOROOT(), 1) filename := strings.Replace(posn.Filename, "$GOROOT", testenv.GOROOT(t), 1)
data, err := os.ReadFile(filename) data, err := os.ReadFile(filename)
if err != nil { if err != nil {
t.Fatalf("can't read file containing declaration of math/big.Int: %v", err) t.Fatalf("can't read file containing declaration of math/big.Int: %v", err)

View file

@ -18,6 +18,7 @@ import (
"time" "time"
"go/ast" "go/ast"
"go/build"
"go/importer" "go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
@ -26,6 +27,11 @@ import (
. "go/internal/gcimporter" . "go/internal/gcimporter"
) )
func TestMain(m *testing.M) {
build.Default.GOROOT = testenv.GOROOT(nil)
os.Exit(m.Run())
}
// skipSpecialPlatforms causes the test to be skipped for platforms where // skipSpecialPlatforms causes the test to be skipped for platforms where
// builders (build.golang.org) don't have access to compiled packages for // builders (build.golang.org) don't have access to compiled packages for
// import. // import.
@ -72,7 +78,7 @@ const maxTime = 30 * time.Second
var pkgExts = [...]string{".a", ".o"} // keep in sync with gcimporter.go var pkgExts = [...]string{".a", ".o"} // keep in sync with gcimporter.go
func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir) dirname := filepath.Join(testenv.GOROOT(t), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
list, err := os.ReadDir(dirname) list, err := os.ReadDir(dirname)
if err != nil { if err != nil {
t.Fatalf("testDir(%s): %s", dirname, err) t.Fatalf("testDir(%s): %s", dirname, err)
@ -162,7 +168,7 @@ func TestImportTypeparamTests(t *testing.T) {
// Check go files in test/typeparam, except those that fail for a known // Check go files in test/typeparam, except those that fail for a known
// reason. // reason.
rootDir := filepath.Join(runtime.GOROOT(), "test", "typeparam") rootDir := filepath.Join(testenv.GOROOT(t), "test", "typeparam")
list, err := os.ReadDir(rootDir) list, err := os.ReadDir(rootDir)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View file

@ -13,7 +13,6 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -21,9 +20,7 @@ import (
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
flag.Parse() flag.Parse()
if goTool, err := testenv.GoTool(); err == nil { build.Default.GOROOT = testenv.GOROOT(nil)
os.Setenv("PATH", filepath.Dir(goTool)+string(os.PathListSeparator)+os.Getenv("PATH"))
}
os.Exit(m.Run()) os.Exit(m.Run())
} }
@ -58,7 +55,7 @@ func walkDir(t *testing.T, path string, endTime time.Time) (int, bool) {
return 0, false return 0, false
} }
list, err := os.ReadDir(filepath.Join(runtime.GOROOT(), "src", path)) list, err := os.ReadDir(filepath.Join(testenv.GOROOT(t), "src", path))
if err != nil { if err != nil {
t.Fatalf("walkDir %s failed (%v)", path, err) t.Fatalf("walkDir %s failed (%v)", path, err)
} }
@ -247,7 +244,7 @@ func TestCgo(t *testing.T) {
testenv.MustHaveCGO(t) testenv.MustHaveCGO(t)
importer := New(&build.Default, token.NewFileSet(), make(map[string]*types.Package)) importer := New(&build.Default, token.NewFileSet(), make(map[string]*types.Package))
_, err := importer.ImportFrom("./misc/cgo/test", runtime.GOROOT(), 0) _, err := importer.ImportFrom("./misc/cgo/test", testenv.GOROOT(t), 0)
if err != nil { if err != nil {
t.Fatalf("Import failed: %v", err) t.Fatalf("Import failed: %v", err)
} }

17
src/go/types/main_test.go Normal file
View file

@ -0,0 +1,17 @@
// 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 types_test
import (
"go/build"
"internal/testenv"
"os"
"testing"
)
func TestMain(m *testing.M) {
build.Default.GOROOT = testenv.GOROOT(nil)
os.Exit(m.Run())
}

View file

@ -18,7 +18,6 @@ import (
"internal/testenv" "internal/testenv"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -40,7 +39,7 @@ func TestStdlib(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
pkgCount := 0 pkgCount := 0
duration := walkPkgDirs(filepath.Join(runtime.GOROOT(), "src"), func(dir string, filenames []string) { duration := walkPkgDirs(filepath.Join(testenv.GOROOT(t), "src"), func(dir string, filenames []string) {
typecheck(t, dir, filenames) typecheck(t, dir, filenames)
pkgCount++ pkgCount++
}, t.Error) }, t.Error)
@ -163,7 +162,7 @@ func TestStdTest(t *testing.T) {
t.Skip("skipping in short mode") t.Skip("skipping in short mode")
} }
testTestDir(t, filepath.Join(runtime.GOROOT(), "test"), testTestDir(t, filepath.Join(testenv.GOROOT(t), "test"),
"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
"directive.go", // tests compiler rejection of bad directive placement - ignore "directive.go", // tests compiler rejection of bad directive placement - ignore
"directive2.go", // tests compiler rejection of bad directive placement - ignore "directive2.go", // tests compiler rejection of bad directive placement - ignore
@ -181,7 +180,7 @@ func TestStdFixed(t *testing.T) {
t.Skip("skipping in short mode") t.Skip("skipping in short mode")
} }
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"), testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "fixedbugs"),
"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
"issue6889.go", // gc-specific test "issue6889.go", // gc-specific test
"issue11362.go", // canonical import path check "issue11362.go", // canonical import path check
@ -206,7 +205,7 @@ func TestStdFixed(t *testing.T) {
func TestStdKen(t *testing.T) { func TestStdKen(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken")) testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "ken"))
} }
// Package paths of excluded packages. // Package paths of excluded packages.
@ -249,7 +248,10 @@ func typecheck(t *testing.T, path string, filenames []string) {
// typecheck package files // typecheck package files
conf := Config{ conf := Config{
Error: func(err error) { t.Error(err) }, Error: func(err error) {
t.Helper()
t.Error(err)
},
Importer: stdLibImporter, Importer: stdLibImporter,
} }
info := Info{Uses: make(map[*ast.Ident]Object)} info := Info{Uses: make(map[*ast.Ident]Object)}
@ -322,16 +324,13 @@ func (w *walker) walk(dir string) {
} }
// apply pkgh to the files in directory dir // apply pkgh to the files in directory dir
// but ignore files directly under $GOROOT/src (might be temporary test files). pkgFiles, err := pkgFilenames(dir)
if dir != filepath.Join(runtime.GOROOT(), "src") { if err != nil {
files, err := pkgFilenames(dir) w.errh(err)
if err != nil { return
w.errh(err) }
return if pkgFiles != nil {
} w.pkgh(dir, pkgFiles)
if files != nil {
w.pkgh(dir, files)
}
} }
// traverse subdirectories, but don't walk into testdata // traverse subdirectories, but don't walk into testdata

View file

@ -14,6 +14,7 @@ import (
"bytes" "bytes"
"errors" "errors"
"flag" "flag"
"fmt"
"internal/cfg" "internal/cfg"
"os" "os"
"os/exec" "os/exec"
@ -96,6 +97,100 @@ func GoToolPath(t testing.TB) string {
return path return path
} }
var (
gorootOnce sync.Once
gorootPath string
gorootErr error
)
func findGOROOT() (string, error) {
gorootOnce.Do(func() {
gorootPath = runtime.GOROOT()
if gorootPath != "" {
// If runtime.GOROOT() is non-empty, assume that it is valid.
//
// (It might not be: for example, the user may have explicitly set GOROOT
// to the wrong directory, or explicitly set GOROOT_FINAL but not GOROOT
// and hasn't moved the tree to GOROOT_FINAL yet. But those cases are
// rare, and if that happens the user can fix what they broke.)
return
}
// runtime.GOROOT doesn't know where GOROOT is (perhaps because the test
// binary was built with -trimpath, or perhaps because GOROOT_FINAL was set
// without GOROOT and the tree hasn't been moved there yet).
//
// Since this is internal/testenv, we can cheat and assume that the caller
// is a test of some package in a subdirectory of GOROOT/src. ('go test'
// runs the test in the directory containing the packaged under test.) That
// means that if we start walking up the tree, we should eventually find
// GOROOT/src/go.mod, and we can report the parent directory of that.
cwd, err := os.Getwd()
if err != nil {
gorootErr = fmt.Errorf("finding GOROOT: %w", err)
return
}
dir := cwd
for {
parent := filepath.Dir(dir)
if parent == dir {
// dir is either "." or only a volume name.
gorootErr = fmt.Errorf("failed to locate GOROOT/src in any parent directory")
return
}
if base := filepath.Base(dir); base != "src" {
dir = parent
continue // dir cannot be GOROOT/src if it doesn't end in "src".
}
b, err := os.ReadFile(filepath.Join(dir, "go.mod"))
if err != nil {
if os.IsNotExist(err) {
dir = parent
continue
}
gorootErr = fmt.Errorf("finding GOROOT: %w", err)
return
}
goMod := string(b)
for goMod != "" {
var line string
line, goMod, _ = strings.Cut(goMod, "\n")
fields := strings.Fields(line)
if len(fields) >= 2 && fields[0] == "module" && fields[1] == "std" {
// Found "module std", which is the module declaration in GOROOT/src!
gorootPath = parent
return
}
}
}
})
return gorootPath, gorootErr
}
// GOROOT reports the path to the directory containing the root of the Go
// project source tree. This is normally equivalent to runtime.GOROOT, but
// works even if the test binary was built with -trimpath.
//
// If GOROOT cannot be found, GOROOT skips t if t is non-nil,
// or panics otherwise.
func GOROOT(t testing.TB) string {
path, err := findGOROOT()
if err != nil {
if t == nil {
panic(err)
}
t.Helper()
t.Skip(err)
}
return path
}
// GoTool reports the path to the Go tool. // GoTool reports the path to the Go tool.
func GoTool() (string, error) { func GoTool() (string, error) {
if !HasGoBuild() { if !HasGoBuild() {
@ -105,7 +200,11 @@ func GoTool() (string, error) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
exeSuffix = ".exe" exeSuffix = ".exe"
} }
path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) goroot, err := findGOROOT()
if err != nil {
return "", fmt.Errorf("cannot find go tool: %w", err)
}
path := filepath.Join(goroot, "bin", "go"+exeSuffix)
if _, err := os.Stat(path); err == nil { if _, err := os.Stat(path); err == nil {
return path, nil return path, nil
} }

View file

@ -7,7 +7,6 @@ package netip
import ( import (
"internal/testenv" "internal/testenv"
"os/exec" "os/exec"
"path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"strings" "strings"
@ -17,12 +16,8 @@ import (
func TestInlining(t *testing.T) { func TestInlining(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
t.Parallel() t.Parallel()
var exe string
if runtime.GOOS == "windows" {
exe = ".exe"
}
out, err := exec.Command( out, err := exec.Command(
filepath.Join(runtime.GOROOT(), "bin", "go"+exe), testenv.GoToolPath(t),
"build", "build",
"--gcflags=-m", "--gcflags=-m",
"net/netip").CombinedOutput() "net/netip").CombinedOutput()

View file

@ -1329,7 +1329,7 @@ func TestBug3486(t *testing.T) { // https://golang.org/issue/3486
if runtime.GOOS == "ios" { if runtime.GOOS == "ios" {
t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH) t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
} }
root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test") root, err := filepath.EvalSymlinks(testenv.GOROOT(t) + "/test")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -49,7 +49,7 @@ func checkGdbEnvironment(t *testing.T) {
case "plan9": case "plan9":
t.Skip("there is no gdb on Plan 9") t.Skip("there is no gdb on Plan 9")
} }
if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final { if final := os.Getenv("GOROOT_FINAL"); final != "" && testenv.GOROOT(t) != final {
t.Skip("gdb test can fail with GOROOT_FINAL pending") t.Skip("gdb test can fail with GOROOT_FINAL pending")
} }
} }
@ -204,7 +204,7 @@ func testGdbPython(t *testing.T, cgo bool) {
} }
args := []string{"-nx", "-q", "--batch", args := []string{"-nx", "-q", "--batch",
"-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
"-ex", "set startup-with-shell off", "-ex", "set startup-with-shell off",
"-ex", "set print thread-events off", "-ex", "set print thread-events off",
} }
@ -215,7 +215,7 @@ func testGdbPython(t *testing.T, cgo bool) {
// Until gold and gdb can work together, temporarily load the // Until gold and gdb can work together, temporarily load the
// python script directly. // python script directly.
args = append(args, args = append(args,
"-ex", "source "+filepath.Join(runtime.GOROOT(), "src", "runtime", "runtime-gdb.py"), "-ex", "source "+filepath.Join(testenv.GOROOT(t), "src", "runtime", "runtime-gdb.py"),
) )
} else { } else {
args = append(args, args = append(args,
@ -276,7 +276,7 @@ func testGdbPython(t *testing.T, cgo bool) {
cmd.Env = []string{} cmd.Env = []string{}
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) { if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) {
t.Skipf("skipping because GOROOT=%s does not exist", runtime.GOROOT()) t.Skipf("skipping because GOROOT=%s does not exist", testenv.GOROOT(t))
} }
_, file, _, _ := runtime.Caller(1) _, file, _, _ := runtime.Caller(1)
@ -416,7 +416,7 @@ func TestGdbBacktrace(t *testing.T) {
// Execute gdb commands. // Execute gdb commands.
args := []string{"-nx", "-batch", args := []string{"-nx", "-batch",
"-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
"-ex", "set startup-with-shell off", "-ex", "set startup-with-shell off",
"-ex", "break main.eee", "-ex", "break main.eee",
"-ex", "run", "-ex", "run",
@ -498,7 +498,7 @@ func TestGdbAutotmpTypes(t *testing.T) {
// Execute gdb commands. // Execute gdb commands.
args := []string{"-nx", "-batch", args := []string{"-nx", "-batch",
"-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
"-ex", "set startup-with-shell off", "-ex", "set startup-with-shell off",
"-ex", "break main.main", "-ex", "break main.main",
"-ex", "run", "-ex", "run",
@ -563,7 +563,7 @@ func TestGdbConst(t *testing.T) {
// Execute gdb commands. // Execute gdb commands.
args := []string{"-nx", "-batch", args := []string{"-nx", "-batch",
"-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
"-ex", "set startup-with-shell off", "-ex", "set startup-with-shell off",
"-ex", "break main.main", "-ex", "break main.main",
"-ex", "run", "-ex", "run",
@ -626,7 +626,7 @@ func TestGdbPanic(t *testing.T) {
// Execute gdb commands. // Execute gdb commands.
args := []string{"-nx", "-batch", args := []string{"-nx", "-batch",
"-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
"-ex", "set startup-with-shell off", "-ex", "set startup-with-shell off",
"-ex", "run", "-ex", "run",
"-ex", "backtrace", "-ex", "backtrace",
@ -701,7 +701,7 @@ func TestGdbInfCallstack(t *testing.T) {
// Execute gdb commands. // Execute gdb commands.
// 'setg_gcc' is the first point where we can reproduce the issue with just one 'run' command. // 'setg_gcc' is the first point where we can reproduce the issue with just one 'run' command.
args := []string{"-nx", "-batch", args := []string{"-nx", "-batch",
"-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"),
"-ex", "set startup-with-shell off", "-ex", "set startup-with-shell off",
"-ex", "break setg_gcc", "-ex", "break setg_gcc",
"-ex", "run", "-ex", "run",

View file

@ -7,6 +7,7 @@ package time_test
import ( import (
"errors" "errors"
"fmt" "fmt"
"internal/testenv"
"os" "os"
"reflect" "reflect"
"testing" "testing"
@ -137,7 +138,7 @@ func TestLoadLocationFromTZData(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
gorootSource, ok := time.GorootZoneSource("../..") gorootSource, ok := time.GorootZoneSource(testenv.GOROOT(t))
if !ok { if !ok {
t.Fatal("Failed to locate tzinfo source in GOROOT.") t.Fatal("Failed to locate tzinfo source in GOROOT.")
} }