Skip to content

Commit 91b8b13

Browse files
author
Bryan C. Mills
committed
test: make runindir tests pass regardless of whether module mode is in 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 <[email protected]>
1 parent 16cfab8 commit 91b8b13

File tree

2 files changed

+104
-37
lines changed

2 files changed

+104
-37
lines changed

test/fixedbugs/issue29612.dir/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ package main
1212
import (
1313
"fmt"
1414

15-
ssa1 "./p1/ssa"
16-
ssa2 "./p2/ssa"
15+
ssa1 "issue29612.dir/p1/ssa"
16+
ssa2 "issue29612.dir/p2/ssa"
1717
)
1818

1919
func main() {

test/run.go

+102-35
Original file line numberDiff line numberDiff line change
@@ -607,20 +607,23 @@ func (t *test) run() {
607607
os.Setenv("GOARCH", runtime.GOARCH)
608608
}
609609

610-
useTmp := true
611-
runInDir := false
610+
var (
611+
runInDir = t.tempDir
612+
tempDirIsGOPATH = false
613+
)
612614
runcmd := func(args ...string) ([]byte, error) {
613615
cmd := exec.Command(args[0], args[1:]...)
614616
var buf bytes.Buffer
615617
cmd.Stdout = &buf
616618
cmd.Stderr = &buf
617-
cmd.Env = os.Environ()
618-
if useTmp {
619-
cmd.Dir = t.tempDir
620-
cmd.Env = envForDir(cmd.Dir)
619+
cmd.Env = append(os.Environ(), "GOENV=off", "GOFLAGS=")
620+
if runInDir != "" {
621+
cmd.Dir = runInDir
622+
// Set PWD to match Dir to speed up os.Getwd in the child process.
623+
cmd.Env = append(cmd.Env, "PWD="+cmd.Dir)
621624
}
622-
if runInDir {
623-
cmd.Dir = t.goDirName()
625+
if tempDirIsGOPATH {
626+
cmd.Env = append(cmd.Env, "GOPATH="+t.tempDir)
624627
}
625628

626629
var err error
@@ -863,13 +866,31 @@ func (t *test) run() {
863866
}
864867

865868
case "runindir":
866-
// run "go run ." in t.goDirName()
867-
// It's used when test requires go build and run the binary success.
868-
// Example when long import path require (see issue29612.dir) or test
869-
// contains assembly file (see issue15609.dir).
870-
// Verify the expected output.
871-
useTmp = false
872-
runInDir = true
869+
// Make a shallow copy of t.goDirName() in its own module and GOPATH, and
870+
// run "go run ." in it. The module path (and hence import path prefix) of
871+
// the copy is equal to the basename of the source directory.
872+
//
873+
// It's used when test a requires a full 'go build' in order to compile
874+
// the sources, such as when importing multiple packages (issue29612.dir)
875+
// or compiling a package containing assembly files (see issue15609.dir),
876+
// but still needs to be run to verify the expected output.
877+
tempDirIsGOPATH = true
878+
srcDir := t.goDirName()
879+
modName := filepath.Base(srcDir)
880+
gopathSrcDir := filepath.Join(t.tempDir, "src", modName)
881+
runInDir = gopathSrcDir
882+
883+
if err := overlayDir(gopathSrcDir, srcDir); err != nil {
884+
t.err = err
885+
return
886+
}
887+
888+
modFile := fmt.Sprintf("module %s\ngo 1.14\n", modName)
889+
if err := ioutil.WriteFile(filepath.Join(gopathSrcDir, "go.mod"), []byte(modFile), 0666); err != nil {
890+
t.err = err
891+
return
892+
}
893+
873894
cmd := []string{goTool(), "run", goGcflags()}
874895
if *linkshared {
875896
cmd = append(cmd, "-linkshared")
@@ -1003,7 +1024,7 @@ func (t *test) run() {
10031024
// Run Go file if no special go command flags are provided;
10041025
// otherwise build an executable and run it.
10051026
// Verify the output.
1006-
useTmp = false
1027+
runInDir = ""
10071028
var out []byte
10081029
var err error
10091030
if len(flags)+len(args) == 0 && goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS {
@@ -1051,7 +1072,7 @@ func (t *test) run() {
10511072
defer func() {
10521073
<-rungatec
10531074
}()
1054-
useTmp = false
1075+
runInDir = ""
10551076
cmd := []string{goTool(), "run", goGcflags()}
10561077
if *linkshared {
10571078
cmd = append(cmd, "-linkshared")
@@ -1084,7 +1105,7 @@ func (t *test) run() {
10841105
case "errorcheckoutput":
10851106
// Run Go file and write its output into temporary Go file.
10861107
// Compile and errorCheck generated Go file.
1087-
useTmp = false
1108+
runInDir = ""
10881109
cmd := []string{goTool(), "run", goGcflags()}
10891110
if *linkshared {
10901111
cmd = append(cmd, "-linkshared")
@@ -1752,27 +1773,73 @@ func checkShouldTest() {
17521773
assert(shouldTest("// +build !windows !plan9", "windows", "amd64"))
17531774
}
17541775

1755-
// envForDir returns a copy of the environment
1756-
// suitable for running in the given directory.
1757-
// The environment is the current process's environment
1758-
// but with an updated $PWD, so that an os.Getwd in the
1759-
// child will be faster.
1760-
func envForDir(dir string) []string {
1761-
env := os.Environ()
1762-
for i, kv := range env {
1763-
if strings.HasPrefix(kv, "PWD=") {
1764-
env[i] = "PWD=" + dir
1765-
return env
1766-
}
1767-
}
1768-
env = append(env, "PWD="+dir)
1769-
return env
1770-
}
1771-
17721776
func getenv(key, def string) string {
17731777
value := os.Getenv(key)
17741778
if value != "" {
17751779
return value
17761780
}
17771781
return def
17781782
}
1783+
1784+
// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added.
1785+
func overlayDir(dstRoot, srcRoot string) error {
1786+
dstRoot = filepath.Clean(dstRoot)
1787+
if err := os.MkdirAll(dstRoot, 0777); err != nil {
1788+
return err
1789+
}
1790+
1791+
srcRoot, err := filepath.Abs(srcRoot)
1792+
if err != nil {
1793+
return err
1794+
}
1795+
1796+
return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error {
1797+
if err != nil || srcPath == srcRoot {
1798+
return err
1799+
}
1800+
1801+
suffix := strings.TrimPrefix(srcPath, srcRoot)
1802+
for len(suffix) > 0 && suffix[0] == filepath.Separator {
1803+
suffix = suffix[1:]
1804+
}
1805+
dstPath := filepath.Join(dstRoot, suffix)
1806+
1807+
perm := info.Mode() & os.ModePerm
1808+
if info.Mode()&os.ModeSymlink != 0 {
1809+
info, err = os.Stat(srcPath)
1810+
if err != nil {
1811+
return err
1812+
}
1813+
perm = info.Mode() & os.ModePerm
1814+
}
1815+
1816+
// Always copy directories (don't symlink them).
1817+
// If we add a file in the overlay, we don't want to add it in the original.
1818+
if info.IsDir() {
1819+
return os.MkdirAll(dstPath, perm|0200)
1820+
}
1821+
1822+
// If the OS supports symlinks, use them instead of copying bytes.
1823+
if err := os.Symlink(srcPath, dstPath); err == nil {
1824+
return nil
1825+
}
1826+
1827+
// Otherwise, copy the bytes.
1828+
src, err := os.Open(srcPath)
1829+
if err != nil {
1830+
return err
1831+
}
1832+
defer src.Close()
1833+
1834+
dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
1835+
if err != nil {
1836+
return err
1837+
}
1838+
1839+
_, err = io.Copy(dst, src)
1840+
if closeErr := dst.Close(); err == nil {
1841+
err = closeErr
1842+
}
1843+
return err
1844+
})
1845+
}

0 commit comments

Comments
 (0)