Skip to content

Commit 29be20a

Browse files
rscdominikh
authored andcommitted
cmd/go: invalidate cached test results if env vars or files change
When we write a cached test result, we now also write a log of the environment variables and files inspected by the test run, along with a hash of their content. Before reusing a cached test result, we recompute the hash of the content specified by the log, and only use the result if that content has not changed. This makes test caching behave correctly for tests that consult environment variables or stat or read files or directories. Fixes #22593. Change-Id: I8608798e73c90e0c1911a38bf7e03e1232d784dc Reviewed-on: https://go-review.googlesource.com/81895 Run-TryBot: Russ Cox <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 8c22776 commit 29be20a

File tree

18 files changed

+652
-47
lines changed

18 files changed

+652
-47
lines changed

src/cmd/go/go_test.go

Lines changed: 107 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2479,7 +2479,7 @@ func TestCoveragePattern(t *testing.T) {
24792479
// (as opposed to pattern matching on deps)
24802480
// then it will try to load sleepybad, which does not compile,
24812481
// and the test command will fail.
2482-
tg.run("test", "-coverprofile="+filepath.Join(tg.tempdir, "cover.out"), "-coverpkg=sleepy...", "-run=^$", "sleepy1")
2482+
tg.run("test", "-coverprofile="+tg.path("cover.out"), "-coverpkg=sleepy...", "-run=^$", "sleepy1")
24832483
}
24842484

24852485
func TestCoverageErrorLine(t *testing.T) {
@@ -2530,7 +2530,7 @@ func TestCoverageFunc(t *testing.T) {
25302530
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
25312531

25322532
tg.run("test", "-outputdir="+tg.tempdir, "-coverprofile=cover.out", "coverasm")
2533-
tg.run("tool", "cover", "-func="+filepath.Join(tg.tempdir, "cover.out"))
2533+
tg.run("tool", "cover", "-func="+tg.path("cover.out"))
25342534
tg.grepStdout(`\tg\t*100.0%`, "did not find g 100% covered")
25352535
tg.grepStdoutNot(`\tf\t*[0-9]`, "reported coverage for assembly function f")
25362536
}
@@ -4344,7 +4344,7 @@ func main() {
43444344
}
43454345
`)
43464346
tg.setenv("GOPATH", tg.path("go"))
4347-
exe := filepath.Join(tg.tempdir, "p.exe")
4347+
exe := tg.path("p.exe")
43484348
tg.creatingTemp(exe)
43494349
tg.run("build", "-o", exe, "p")
43504350
}
@@ -4958,7 +4958,7 @@ func TestCacheCoverage(t *testing.T) {
49584958
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
49594959
tg.makeTempdir()
49604960

4961-
tg.setenv("GOCACHE", filepath.Join(tg.tempdir, "c1"))
4961+
tg.setenv("GOCACHE", tg.path("c1"))
49624962
tg.run("test", "-cover", "strings")
49634963
tg.run("test", "-cover", "math", "strings")
49644964
}
@@ -4987,12 +4987,12 @@ func TestIssue22531(t *testing.T) {
49874987
tg.parallel()
49884988
tg.makeTempdir()
49894989
tg.setenv("GOPATH", tg.tempdir)
4990-
tg.setenv("GOCACHE", filepath.Join(tg.tempdir, "cache"))
4990+
tg.setenv("GOCACHE", tg.path("cache"))
49914991
tg.tempFile("src/m/main.go", "package main /* c1 */; func main() {}\n")
49924992
tg.run("install", "-x", "m")
49934993
tg.run("list", "-f", "{{.Stale}}", "m")
49944994
tg.grepStdout("false", "reported m as stale after install")
4995-
tg.run("tool", "buildid", filepath.Join(tg.tempdir, "bin/m"+exeSuffix))
4995+
tg.run("tool", "buildid", tg.path("bin/m"+exeSuffix))
49964996

49974997
// The link action ID did not include the full main build ID,
49984998
// even though the full main build ID is written into the
@@ -5003,7 +5003,7 @@ func TestIssue22531(t *testing.T) {
50035003
tg.run("install", "-x", "m")
50045004
tg.run("list", "-f", "{{.Stale}}", "m")
50055005
tg.grepStdout("false", "reported m as stale after reinstall")
5006-
tg.run("tool", "buildid", filepath.Join(tg.tempdir, "bin/m"+exeSuffix))
5006+
tg.run("tool", "buildid", tg.path("bin/m"+exeSuffix))
50075007
}
50085008

50095009
func TestIssue22596(t *testing.T) {
@@ -5014,17 +5014,17 @@ func TestIssue22596(t *testing.T) {
50145014
defer tg.cleanup()
50155015
tg.parallel()
50165016
tg.makeTempdir()
5017-
tg.setenv("GOCACHE", filepath.Join(tg.tempdir, "cache"))
5017+
tg.setenv("GOCACHE", tg.path("cache"))
50185018
tg.tempFile("gopath1/src/p/p.go", "package p; func F(){}\n")
50195019
tg.tempFile("gopath2/src/p/p.go", "package p; func F(){}\n")
50205020

5021-
tg.setenv("GOPATH", filepath.Join(tg.tempdir, "gopath1"))
5021+
tg.setenv("GOPATH", tg.path("gopath1"))
50225022
tg.run("list", "-f={{.Target}}", "p")
50235023
target1 := strings.TrimSpace(tg.getStdout())
50245024
tg.run("install", "p")
50255025
tg.wantNotStale("p", "", "p stale after install")
50265026

5027-
tg.setenv("GOPATH", filepath.Join(tg.tempdir, "gopath2"))
5027+
tg.setenv("GOPATH", tg.path("gopath2"))
50285028
tg.run("list", "-f={{.Target}}", "p")
50295029
target2 := strings.TrimSpace(tg.getStdout())
50305030
tg.must(os.MkdirAll(filepath.Dir(target2), 0777))
@@ -5043,7 +5043,7 @@ func TestTestCache(t *testing.T) {
50435043
tg.parallel()
50445044
tg.makeTempdir()
50455045
tg.setenv("GOPATH", tg.tempdir)
5046-
tg.setenv("GOCACHE", filepath.Join(tg.tempdir, "cache"))
5046+
tg.setenv("GOCACHE", tg.path("cache"))
50475047

50485048
// timeout here should not affect result being cached
50495049
// or being retrieved later.
@@ -5138,6 +5138,95 @@ func TestTestCache(t *testing.T) {
51385138
tg.grepStdout(`ok \tt/t4\t\(cached\)`, "did not cache t/t4")
51395139
}
51405140

5141+
func TestTestCacheInputs(t *testing.T) {
5142+
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
5143+
t.Skip("GODEBUG gocacheverify")
5144+
}
5145+
tg := testgo(t)
5146+
defer tg.cleanup()
5147+
tg.parallel()
5148+
tg.makeTempdir()
5149+
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
5150+
tg.setenv("GOCACHE", tg.path("cache"))
5151+
5152+
defer os.Remove(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"))
5153+
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("x"), 0644))
5154+
old := time.Now().Add(-1 * time.Minute)
5155+
tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), old, old))
5156+
info, err := os.Stat(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"))
5157+
if err != nil {
5158+
t.Fatal(err)
5159+
}
5160+
t.Logf("file.txt: old=%v, info.ModTime=%v", old, info.ModTime()) // help debug when Chtimes lies about succeeding
5161+
tg.setenv("TESTKEY", "x")
5162+
5163+
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"), []byte("#!/bin/sh\nexit 0\n"), 0755))
5164+
tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"), old, old))
5165+
5166+
tg.run("test", "testcache")
5167+
tg.run("test", "testcache")
5168+
tg.grepStdout(`\(cached\)`, "did not cache")
5169+
5170+
tg.setenv("TESTKEY", "y")
5171+
tg.run("test", "testcache")
5172+
tg.grepStdoutNot(`\(cached\)`, "did not notice env var change")
5173+
tg.run("test", "testcache")
5174+
tg.grepStdout(`\(cached\)`, "did not cache")
5175+
5176+
tg.run("test", "testcache", "-run=FileSize")
5177+
tg.run("test", "testcache", "-run=FileSize")
5178+
tg.grepStdout(`\(cached\)`, "did not cache")
5179+
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("xxx"), 0644))
5180+
tg.run("test", "testcache", "-run=FileSize")
5181+
tg.grepStdoutNot(`\(cached\)`, "did not notice file size change")
5182+
tg.run("test", "testcache", "-run=FileSize")
5183+
tg.grepStdout(`\(cached\)`, "did not cache")
5184+
5185+
tg.run("test", "testcache", "-run=Chdir")
5186+
tg.run("test", "testcache", "-run=Chdir")
5187+
tg.grepStdout(`\(cached\)`, "did not cache")
5188+
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("xxxxx"), 0644))
5189+
tg.run("test", "testcache", "-run=Chdir")
5190+
tg.grepStdoutNot(`\(cached\)`, "did not notice file size change")
5191+
tg.run("test", "testcache", "-run=Chdir")
5192+
tg.grepStdout(`\(cached\)`, "did not cache")
5193+
5194+
tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), old, old))
5195+
tg.run("test", "testcache", "-run=FileContent")
5196+
tg.run("test", "testcache", "-run=FileContent")
5197+
tg.grepStdout(`\(cached\)`, "did not cache")
5198+
tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("yyy"), 0644))
5199+
old2 := old.Add(10 * time.Second)
5200+
tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), old2, old2))
5201+
tg.run("test", "testcache", "-run=FileContent")
5202+
tg.grepStdoutNot(`\(cached\)`, "did not notice file content change")
5203+
tg.run("test", "testcache", "-run=FileContent")
5204+
tg.grepStdout(`\(cached\)`, "did not cache")
5205+
5206+
tg.run("test", "testcache", "-run=DirList")
5207+
tg.run("test", "testcache", "-run=DirList")
5208+
tg.grepStdout(`\(cached\)`, "did not cache")
5209+
tg.must(os.Remove(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt")))
5210+
tg.run("test", "testcache", "-run=DirList")
5211+
tg.grepStdoutNot(`\(cached\)`, "did not notice directory change")
5212+
tg.run("test", "testcache", "-run=DirList")
5213+
tg.grepStdout(`\(cached\)`, "did not cache")
5214+
5215+
switch runtime.GOOS {
5216+
case "nacl", "plan9", "windows":
5217+
// no shell scripts
5218+
default:
5219+
tg.run("test", "testcache", "-run=Exec")
5220+
tg.run("test", "testcache", "-run=Exec")
5221+
tg.grepStdout(`\(cached\)`, "did not cache")
5222+
tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"), old2, old2))
5223+
tg.run("test", "testcache", "-run=Exec")
5224+
tg.grepStdoutNot(`\(cached\)`, "did not notice script change")
5225+
tg.run("test", "testcache", "-run=Exec")
5226+
tg.grepStdout(`\(cached\)`, "did not cache")
5227+
}
5228+
}
5229+
51415230
func TestTestVet(t *testing.T) {
51425231
tg := testgo(t)
51435232
defer tg.cleanup()
@@ -5151,9 +5240,9 @@ func TestTestVet(t *testing.T) {
51515240
}
51525241
`)
51535242

5154-
tg.runFail("test", filepath.Join(tg.tempdir, "p1_test.go"))
5243+
tg.runFail("test", tg.path("p1_test.go"))
51555244
tg.grepStderr(`Logf format %d`, "did not diagnose bad Logf")
5156-
tg.run("test", "-vet=off", filepath.Join(tg.tempdir, "p1_test.go"))
5245+
tg.run("test", "-vet=off", tg.path("p1_test.go"))
51575246
tg.grepStdout(`^ok`, "did not print test summary")
51585247

51595248
tg.tempFile("p1.go", `
@@ -5163,12 +5252,12 @@ func TestTestVet(t *testing.T) {
51635252
fmt.Printf("%d") // oops
51645253
}
51655254
`)
5166-
tg.runFail("test", filepath.Join(tg.tempdir, "p1.go"))
5255+
tg.runFail("test", tg.path("p1.go"))
51675256
tg.grepStderr(`Printf format %d`, "did not diagnose bad Printf")
5168-
tg.run("test", "-x", "-vet=shift", filepath.Join(tg.tempdir, "p1.go"))
5257+
tg.run("test", "-x", "-vet=shift", tg.path("p1.go"))
51695258
tg.grepStderr(`[\\/]vet.*-shift`, "did not run vet with -shift")
51705259
tg.grepStdout(`\[no test files\]`, "did not print test summary")
5171-
tg.run("test", "-vet=off", filepath.Join(tg.tempdir, "p1.go"))
5260+
tg.run("test", "-vet=off", tg.path("p1.go"))
51725261
tg.grepStdout(`\[no test files\]`, "did not print test summary")
51735262

51745263
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
@@ -5293,8 +5382,8 @@ func TestGoTestJSON(t *testing.T) {
52935382
tg.grepStdout(`"Package":"errors"`, "did not see JSON output")
52945383
tg.grepStdout(`"Action":"run"`, "did not see JSON output")
52955384

5296-
tg.run("test", "-o", filepath.Join(tg.tempdir, "errors.test.exe"), "-c", "errors")
5297-
tg.run("tool", "test2json", "-p", "errors", filepath.Join(tg.tempdir, "errors.test.exe"), "-test.v", "-test.short")
5385+
tg.run("test", "-o", tg.path("errors.test.exe"), "-c", "errors")
5386+
tg.run("tool", "test2json", "-p", "errors", tg.path("errors.test.exe"), "-test.v", "-test.short")
52985387
tg.grepStdout(`"Package":"errors"`, "did not see JSON output")
52995388
tg.grepStdout(`"Action":"run"`, "did not see JSON output")
53005389
tg.grepStdout(`\{"Action":"pass","Package":"errors"\}`, "did not see final pass")

src/cmd/go/internal/cache/cache.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ const (
9797
// GODEBUG=gocacheverify=1.
9898
var verify = false
9999

100+
// DebugTest is set when GODEBUG=gocachetest=1 is in the environment.
101+
var DebugTest = false
102+
100103
func init() { initEnv() }
101104

102105
func initEnv() {
@@ -110,6 +113,9 @@ func initEnv() {
110113
if f == "gocachehash=1" {
111114
debugHash = true
112115
}
116+
if f == "gocachetest=1" {
117+
DebugTest = true
118+
}
113119
}
114120
}
115121

0 commit comments

Comments
 (0)