Skip to content

Commit

Permalink
test: make runindir tests pass regardless of whether module mode is i…
Browse files Browse the repository at this point in the history
…n use

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>
  • Loading branch information
Bryan C. Mills committed Mar 25, 2020
1 parent 16cfab8 commit 91b8b13
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 37 deletions.
4 changes: 2 additions & 2 deletions test/fixedbugs/issue29612.dir/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
137 changes: 102 additions & 35 deletions test/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -1752,27 +1773,73 @@ 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 != "" {
return value
}
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
})
}

0 comments on commit 91b8b13

Please sign in to comment.