]> Cypherpunks repositories - gostls13.git/commitdiff
test: make runindir tests pass regardless of whether module mode is in use
authorBryan C. Mills <bcmills@google.com>
Tue, 24 Mar 2020 20:18:02 +0000 (16:18 -0400)
committerBryan C. Mills <bcmills@google.com>
Wed, 25 Mar 2020 14:19:25 +0000 (14:19 +0000)
The "runindir" tests used "go run", but relied on relative imports
(which are not supported by "go run" in module mode). Instead, such
tests must use fully-qualified imports, which require either a go.mod
file (in module mode) or that the package be in an appropriate
subdirectory of GOPATH/src (in GOPATH mode).

To set up such a directory, we use yet another copy of the same
overlayDir function currently found in the misc subdirectory of this
repository.

Fixes #33912
Updates #30228

Change-Id: If3d7ea2f7942ba496d98aaaf24a90bcdcf4df9f7
Reviewed-on: https://go-review.googlesource.com/c/go/+/225205
Reviewed-by: Ian Lance Taylor <iant@golang.org>
test/fixedbugs/issue29612.dir/main.go
test/run.go

index d860eaac7e93fa1bfbe3fb926f6d9b4508dee60a..97415c445fa34792486e38d1b72594a3a5634a3e 100644 (file)
@@ -12,8 +12,8 @@ package main
 import (
        "fmt"
 
-       ssa1 "./p1/ssa"
-       ssa2 "./p2/ssa"
+       ssa1 "issue29612.dir/p1/ssa"
+       ssa2 "issue29612.dir/p2/ssa"
 )
 
 func main() {
index bd63d7142bc507a8fc2289fa62ad13daebc9cd48..95b94b7277281c4687dee13b9494d2c4383810c4 100644 (file)
@@ -607,20 +607,23 @@ func (t *test) run() {
                os.Setenv("GOARCH", runtime.GOARCH)
        }
 
-       useTmp := true
-       runInDir := false
+       var (
+               runInDir        = t.tempDir
+               tempDirIsGOPATH = false
+       )
        runcmd := func(args ...string) ([]byte, error) {
                cmd := exec.Command(args[0], args[1:]...)
                var buf bytes.Buffer
                cmd.Stdout = &buf
                cmd.Stderr = &buf
-               cmd.Env = os.Environ()
-               if useTmp {
-                       cmd.Dir = t.tempDir
-                       cmd.Env = envForDir(cmd.Dir)
+               cmd.Env = append(os.Environ(), "GOENV=off", "GOFLAGS=")
+               if runInDir != "" {
+                       cmd.Dir = runInDir
+                       // Set PWD to match Dir to speed up os.Getwd in the child process.
+                       cmd.Env = append(cmd.Env, "PWD="+cmd.Dir)
                }
-               if runInDir {
-                       cmd.Dir = t.goDirName()
+               if tempDirIsGOPATH {
+                       cmd.Env = append(cmd.Env, "GOPATH="+t.tempDir)
                }
 
                var err error
@@ -863,13 +866,31 @@ func (t *test) run() {
                }
 
        case "runindir":
-               // run "go run ." in t.goDirName()
-               // It's used when test requires go build and run the binary success.
-               // Example when long import path require (see issue29612.dir) or test
-               // contains assembly file (see issue15609.dir).
-               // Verify the expected output.
-               useTmp = false
-               runInDir = true
+               // Make a shallow copy of t.goDirName() in its own module and GOPATH, and
+               // run "go run ." in it. The module path (and hence import path prefix) of
+               // the copy is equal to the basename of the source directory.
+               //
+               // It's used when test a requires a full 'go build' in order to compile
+               // the sources, such as when importing multiple packages (issue29612.dir)
+               // or compiling a package containing assembly files (see issue15609.dir),
+               // but still needs to be run to verify the expected output.
+               tempDirIsGOPATH = true
+               srcDir := t.goDirName()
+               modName := filepath.Base(srcDir)
+               gopathSrcDir := filepath.Join(t.tempDir, "src", modName)
+               runInDir = gopathSrcDir
+
+               if err := overlayDir(gopathSrcDir, srcDir); err != nil {
+                       t.err = err
+                       return
+               }
+
+               modFile := fmt.Sprintf("module %s\ngo 1.14\n", modName)
+               if err := ioutil.WriteFile(filepath.Join(gopathSrcDir, "go.mod"), []byte(modFile), 0666); err != nil {
+                       t.err = err
+                       return
+               }
+
                cmd := []string{goTool(), "run", goGcflags()}
                if *linkshared {
                        cmd = append(cmd, "-linkshared")
@@ -1003,7 +1024,7 @@ func (t *test) run() {
                // Run Go file if no special go command flags are provided;
                // otherwise build an executable and run it.
                // Verify the output.
-               useTmp = false
+               runInDir = ""
                var out []byte
                var err error
                if len(flags)+len(args) == 0 && goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS {
@@ -1051,7 +1072,7 @@ func (t *test) run() {
                defer func() {
                        <-rungatec
                }()
-               useTmp = false
+               runInDir = ""
                cmd := []string{goTool(), "run", goGcflags()}
                if *linkshared {
                        cmd = append(cmd, "-linkshared")
@@ -1084,7 +1105,7 @@ func (t *test) run() {
        case "errorcheckoutput":
                // Run Go file and write its output into temporary Go file.
                // Compile and errorCheck generated Go file.
-               useTmp = false
+               runInDir = ""
                cmd := []string{goTool(), "run", goGcflags()}
                if *linkshared {
                        cmd = append(cmd, "-linkshared")
@@ -1752,23 +1773,6 @@ func checkShouldTest() {
        assert(shouldTest("// +build !windows !plan9", "windows", "amd64"))
 }
 
-// envForDir returns a copy of the environment
-// suitable for running in the given directory.
-// The environment is the current process's environment
-// but with an updated $PWD, so that an os.Getwd in the
-// child will be faster.
-func envForDir(dir string) []string {
-       env := os.Environ()
-       for i, kv := range env {
-               if strings.HasPrefix(kv, "PWD=") {
-                       env[i] = "PWD=" + dir
-                       return env
-               }
-       }
-       env = append(env, "PWD="+dir)
-       return env
-}
-
 func getenv(key, def string) string {
        value := os.Getenv(key)
        if value != "" {
@@ -1776,3 +1780,66 @@ func getenv(key, def string) string {
        }
        return def
 }
+
+// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added.
+func overlayDir(dstRoot, srcRoot string) error {
+       dstRoot = filepath.Clean(dstRoot)
+       if err := os.MkdirAll(dstRoot, 0777); err != nil {
+               return err
+       }
+
+       srcRoot, err := filepath.Abs(srcRoot)
+       if err != nil {
+               return err
+       }
+
+       return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error {
+               if err != nil || srcPath == srcRoot {
+                       return err
+               }
+
+               suffix := strings.TrimPrefix(srcPath, srcRoot)
+               for len(suffix) > 0 && suffix[0] == filepath.Separator {
+                       suffix = suffix[1:]
+               }
+               dstPath := filepath.Join(dstRoot, suffix)
+
+               perm := info.Mode() & os.ModePerm
+               if info.Mode()&os.ModeSymlink != 0 {
+                       info, err = os.Stat(srcPath)
+                       if err != nil {
+                               return err
+                       }
+                       perm = info.Mode() & os.ModePerm
+               }
+
+               // Always copy directories (don't symlink them).
+               // If we add a file in the overlay, we don't want to add it in the original.
+               if info.IsDir() {
+                       return os.MkdirAll(dstPath, perm|0200)
+               }
+
+               // If the OS supports symlinks, use them instead of copying bytes.
+               if err := os.Symlink(srcPath, dstPath); err == nil {
+                       return nil
+               }
+
+               // Otherwise, copy the bytes.
+               src, err := os.Open(srcPath)
+               if err != nil {
+                       return err
+               }
+               defer src.Close()
+
+               dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
+               if err != nil {
+                       return err
+               }
+
+               _, err = io.Copy(dst, src)
+               if closeErr := dst.Close(); err == nil {
+                       err = closeErr
+               }
+               return err
+       })
+}