diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-01-02 15:05:27 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-01-21 23:53:22 -0800 |
commit | 5a8ea165926cb0737ab03bc48c18dc5198ab5305 (patch) | |
tree | 962dc3357c57f019f85658f99e2e753e30201c27 /libgo/misc/cgo/testshared/shared_test.go | |
parent | 6ac6529e155c9baa0aaaed7aca06bd38ebda5b43 (diff) |
libgo: update to Go1.14beta1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/214297
Diffstat (limited to 'libgo/misc/cgo/testshared/shared_test.go')
-rw-r--r-- | libgo/misc/cgo/testshared/shared_test.go | 200 |
1 files changed, 141 insertions, 59 deletions
diff --git a/libgo/misc/cgo/testshared/shared_test.go b/libgo/misc/cgo/testshared/shared_test.go index 9d16338c0f6..b9ef6dad8e2 100644 --- a/libgo/misc/cgo/testshared/shared_test.go +++ b/libgo/misc/cgo/testshared/shared_test.go @@ -9,31 +9,33 @@ import ( "bytes" "debug/elf" "encoding/binary" - "errors" "flag" "fmt" "go/build" "io" "io/ioutil" "log" - "math/rand" "os" "os/exec" "path/filepath" "regexp" "runtime" + "sort" "strings" "testing" "time" ) -var gopathInstallDir, gorootInstallDir, suffix string +var gopathInstallDir, gorootInstallDir string // This is the smallest set of packages we can link into a shared // library (runtime/cgo is built implicitly). var minpkgs = []string{"runtime", "sync/atomic"} var soname = "libruntime,sync-atomic.so" +var testX = flag.Bool("testx", false, "if true, pass -x to 'go' subcommands invoked by the test") +var testWork = flag.Bool("testwork", false, "if true, log and do not delete the temporary working directory") + // run runs a command and calls t.Errorf if it fails. func run(t *testing.T, msg string, args ...string) { c := exec.Command(args[0], args[1:]...) @@ -45,31 +47,34 @@ func run(t *testing.T, msg string, args ...string) { // goCmd invokes the go tool with the installsuffix set up by TestMain. It calls // t.Fatalf if the command fails. func goCmd(t *testing.T, args ...string) string { - newargs := []string{args[0], "-installsuffix=" + suffix} - if testing.Verbose() { + newargs := []string{args[0]} + if *testX { newargs = append(newargs, "-x") } newargs = append(newargs, args[1:]...) c := exec.Command("go", newargs...) - stderr := new(strings.Builder) - var output []byte - var err error - if testing.Verbose() { - fmt.Printf("+ go %s\n", strings.Join(args, " ")) + c.Stderr = stderr + + if testing.Verbose() && t == nil { + fmt.Fprintf(os.Stderr, "+ go %s\n", strings.Join(args, " ")) c.Stderr = os.Stderr - stderr.WriteString("(output above)") - } else { - c.Stderr = stderr } - output, err = c.Output() + output, err := c.Output() if err != nil { if t != nil { t.Helper() t.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr) } else { - log.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr) + // Panic instead of using log.Fatalf so that deferred cleanup may run in testMain. + log.Panicf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr) + } + } + if testing.Verbose() && t != nil { + t.Logf("go %s", strings.Join(args, " ")) + if stderr.Len() > 0 { + t.Logf("%s", stderr) } } return string(bytes.TrimSpace(output)) @@ -77,73 +82,61 @@ func goCmd(t *testing.T, args ...string) string { // TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit). func testMain(m *testing.M) (int, error) { - // Because go install -buildmode=shared $standard_library_package always - // installs into $GOROOT, here are some gymnastics to come up with a unique - // installsuffix to use in this test that we can clean up afterwards. - myContext := build.Default - runtimeP, err := myContext.Import("runtime", ".", build.ImportComment) + workDir, err := ioutil.TempDir("", "shared_test") if err != nil { - return 0, fmt.Errorf("import failed: %v", err) - } - for i := 0; i < 10000; i++ { - try := fmt.Sprintf("%s_%d_dynlink", runtimeP.PkgTargetRoot, rand.Int63()) - err = os.Mkdir(try, 0700) - if os.IsExist(err) { - continue - } - if err == nil { - gorootInstallDir = try - } - break - } - if err != nil { - return 0, fmt.Errorf("can't create temporary directory: %v", err) + return 0, err } - if gorootInstallDir == "" { - return 0, errors.New("could not create temporary directory after 10000 tries") + if *testWork || testing.Verbose() { + fmt.Printf("+ mkdir -p %s\n", workDir) } - if testing.Verbose() { - fmt.Printf("+ mkdir -p %s\n", gorootInstallDir) + if !*testWork { + defer os.RemoveAll(workDir) } - defer os.RemoveAll(gorootInstallDir) // Some tests need to edit the source in GOPATH, so copy this directory to a // temporary directory and chdir to that. - gopath, err := ioutil.TempDir("", "testshared") + gopath := filepath.Join(workDir, "gopath") + modRoot, err := cloneTestdataModule(gopath) if err != nil { - return 0, fmt.Errorf("TempDir failed: %v", err) - } - if testing.Verbose() { - fmt.Printf("+ mkdir -p %s\n", gopath) - } - defer os.RemoveAll(gopath) - - modRoot := filepath.Join(gopath, "src", "testshared") - if err := overlayDir(modRoot, "testdata"); err != nil { return 0, err } if testing.Verbose() { + fmt.Printf("+ export GOPATH=%s\n", gopath) fmt.Printf("+ cd %s\n", modRoot) } + os.Setenv("GOPATH", gopath) os.Chdir(modRoot) os.Setenv("PWD", modRoot) - if err := ioutil.WriteFile("go.mod", []byte("module testshared\n"), 0666); err != nil { + + // The test also needs to install libraries into GOROOT/pkg, so copy the + // subset of GOROOT that we need. + // + // TODO(golang.org/issue/28553): Rework -buildmode=shared so that it does not + // need to write to GOROOT. + goroot := filepath.Join(workDir, "goroot") + if err := cloneGOROOTDeps(goroot); err != nil { return 0, err } - - os.Setenv("GOPATH", gopath) if testing.Verbose() { - fmt.Printf("+ export GOPATH=%s\n", gopath) + fmt.Fprintf(os.Stderr, "+ export GOROOT=%s\n", goroot) } + os.Setenv("GOROOT", goroot) + + myContext := build.Default + myContext.GOROOT = goroot myContext.GOPATH = gopath + runtimeP, err := myContext.Import("runtime", ".", build.ImportComment) + if err != nil { + return 0, fmt.Errorf("import failed: %v", err) + } + gorootInstallDir = runtimeP.PkgTargetRoot + "_dynlink" // All tests depend on runtime being built into a shared library. Because // that takes a few seconds, do it here and have all tests use the version // built here. - suffix = strings.Split(filepath.Base(gorootInstallDir), "_")[2] goCmd(nil, append([]string{"install", "-buildmode=shared"}, minpkgs...)...) - myContext.InstallSuffix = suffix + "_dynlink" + myContext.InstallSuffix = "_dynlink" depP, err := myContext.Import("./depBase", ".", build.ImportComment) if err != nil { return 0, fmt.Errorf("import failed: %v", err) @@ -171,6 +164,75 @@ func TestMain(m *testing.M) { os.Exit(exitCode) } +// cloneTestdataModule clones the packages from src/testshared into gopath. +// It returns the directory within gopath at which the module root is located. +func cloneTestdataModule(gopath string) (string, error) { + modRoot := filepath.Join(gopath, "src", "testshared") + if err := overlayDir(modRoot, "testdata"); err != nil { + return "", err + } + if err := ioutil.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module testshared\n"), 0644); err != nil { + return "", err + } + return modRoot, nil +} + +// cloneGOROOTDeps copies (or symlinks) the portions of GOROOT/src and +// GOROOT/pkg relevant to this test into the given directory. +// It must be run from within the testdata module. +func cloneGOROOTDeps(goroot string) error { + oldGOROOT := strings.TrimSpace(goCmd(nil, "env", "GOROOT")) + if oldGOROOT == "" { + return fmt.Errorf("go env GOROOT returned an empty string") + } + + // Before we clone GOROOT, figure out which packages we need to copy over. + listArgs := []string{ + "list", + "-deps", + "-f", "{{if and .Standard (not .ForTest)}}{{.ImportPath}}{{end}}", + } + stdDeps := goCmd(nil, append(listArgs, minpkgs...)...) + testdataDeps := goCmd(nil, append(listArgs, "-test", "./...")...) + + pkgs := append(strings.Split(strings.TrimSpace(stdDeps), "\n"), + strings.Split(strings.TrimSpace(testdataDeps), "\n")...) + sort.Strings(pkgs) + var pkgRoots []string + for _, pkg := range pkgs { + parentFound := false + for _, prev := range pkgRoots { + if strings.HasPrefix(pkg, prev) { + // We will copy in the source for pkg when we copy in prev. + parentFound = true + break + } + } + if !parentFound { + pkgRoots = append(pkgRoots, pkg) + } + } + + gorootDirs := []string{ + "pkg/tool", + "pkg/include", + } + for _, pkg := range pkgRoots { + gorootDirs = append(gorootDirs, filepath.Join("src", pkg)) + } + + for _, dir := range gorootDirs { + if testing.Verbose() { + fmt.Fprintf(os.Stderr, "+ cp -r %s %s\n", filepath.Join(goroot, dir), filepath.Join(oldGOROOT, dir)) + } + if err := overlayDir(filepath.Join(goroot, dir), filepath.Join(oldGOROOT, dir)); err != nil { + return err + } + } + + return nil +} + // The shared library was built at the expected location. func TestSOBuilt(t *testing.T) { _, err := os.Stat(filepath.Join(gorootInstallDir, soname)) @@ -219,6 +281,7 @@ func TestNoTextrel(t *testing.T) { } // The shared library does not contain symbols called ".dup" +// (See golang.org/issue/14841.) func TestNoDupSymbols(t *testing.T) { sopath := filepath.Join(gorootInstallDir, soname) f, err := elf.Open(sopath) @@ -695,7 +758,7 @@ func resetFileStamps() { } reset := func(path string) { if err := filepath.Walk(path, chtime); err != nil { - log.Fatalf("resetFileStamps failed: %v", err) + log.Panicf("resetFileStamps failed: %v", err) } } @@ -708,6 +771,7 @@ func resetFileStamps() { // touch changes path and returns a function that changes it back. // It also sets the time of the file, so that we can see if it is rewritten. func touch(t *testing.T, path string) (cleanup func()) { + t.Helper() data, err := ioutil.ReadFile(path) if err != nil { t.Fatal(err) @@ -736,14 +800,32 @@ func touch(t *testing.T, path string) (cleanup func()) { // assume it's a text file data = append(data, '\n') } - if err := ioutil.WriteFile(path, data, 0666); err != nil { + + // If the file is still a symlink from an overlay, delete it so that we will + // replace it with a regular file instead of overwriting the symlinked one. + fi, err := os.Lstat(path) + if err == nil && !fi.Mode().IsRegular() { + fi, err = os.Stat(path) + if err := os.Remove(path); err != nil { + t.Fatal(err) + } + } + if err != nil { + t.Fatal(err) + } + + // If we're replacing a symlink to a read-only file, make the new file + // user-writable. + perm := fi.Mode().Perm() | 0200 + + if err := ioutil.WriteFile(path, data, perm); err != nil { t.Fatal(err) } if err := os.Chtimes(path, nearlyNew, nearlyNew); err != nil { t.Fatal(err) } return func() { - if err := ioutil.WriteFile(path, old, 0666); err != nil { + if err := ioutil.WriteFile(path, old, perm); err != nil { t.Fatal(err) } } |