aboutsummaryrefslogtreecommitdiff
path: root/src/testing
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2017-12-04 13:57:48 -0500
committerDominik Honnef <dominik@honnef.co>2017-12-11 19:08:32 +0000
commit29be20a111d87b41b91e79a59fd3df95062ce91a (patch)
tree7ae1ee760e54cd98e51751682338319f3d048626 /src/testing
parent8c227765f70b5677901692a71d8662f395186a83 (diff)
downloadgo-29be20a111d87b41b91e79a59fd3df95062ce91a.tar.xz
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 <rsc@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/testing')
-rw-r--r--src/testing/internal/testdeps/deps.go62
-rw-r--r--src/testing/testing.go31
2 files changed, 91 insertions, 2 deletions
diff --git a/src/testing/internal/testdeps/deps.go b/src/testing/internal/testdeps/deps.go
index 042f69614e..8c0b3fded1 100644
--- a/src/testing/internal/testdeps/deps.go
+++ b/src/testing/internal/testdeps/deps.go
@@ -11,9 +11,13 @@
package testdeps
import (
+ "bufio"
+ "internal/testlog"
"io"
"regexp"
"runtime/pprof"
+ "strings"
+ "sync"
)
// TestDeps is an implementation of the testing.testDeps interface,
@@ -56,3 +60,61 @@ var ImportPath string
func (TestDeps) ImportPath() string {
return ImportPath
}
+
+// testLog implements testlog.Interface, logging actions by package os.
+type testLog struct {
+ mu sync.Mutex
+ w *bufio.Writer
+}
+
+func (l *testLog) Getenv(key string) {
+ l.add("getenv", key)
+}
+
+func (l *testLog) Open(name string) {
+ l.add("open", name)
+}
+
+func (l *testLog) Stat(name string) {
+ l.add("stat", name)
+}
+
+func (l *testLog) Chdir(name string) {
+ l.add("chdir", name)
+}
+
+// add adds the (op, name) pair to the test log.
+func (l *testLog) add(op, name string) {
+ if strings.Contains(name, "\n") || name == "" {
+ return
+ }
+
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ if l.w == nil {
+ return
+ }
+ l.w.WriteString(op)
+ l.w.WriteByte(' ')
+ l.w.WriteString(name)
+ l.w.WriteByte('\n')
+}
+
+var log testLog
+
+func (TestDeps) StartTestLog(w io.Writer) {
+ log.mu.Lock()
+ log.w = bufio.NewWriter(w)
+ log.w.WriteString("# test log\n") // known to cmd/go/internal/test/test.go
+ log.mu.Unlock()
+
+ testlog.SetLogger(&log)
+}
+
+func (TestDeps) StopTestLog() error {
+ log.mu.Lock()
+ defer log.mu.Unlock()
+ err := log.w.Flush()
+ log.w = nil
+ return err
+}
diff --git a/src/testing/testing.go b/src/testing/testing.go
index cddd475fd7..402780ad64 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -268,10 +268,12 @@ var (
timeout = flag.Duration("test.timeout", 0, "panic test binary after duration `d` (default 0, timeout disabled)")
cpuListStr = flag.String("test.cpu", "", "comma-separated `list` of cpu counts to run each test with")
parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "run at most `n` tests in parallel")
+ testlog = flag.String("test.testlogfile", "", "write test action log to `file` (for use only by cmd/go)")
haveExamples bool // are there examples?
- cpuList []int
+ cpuList []int
+ testlogFile *os.File
numFailed uint32 // number of test failures
)
@@ -889,6 +891,8 @@ func (f matchStringOnly) StopCPUProfile() {}
func (f matchStringOnly) WriteHeapProfile(w io.Writer) error { return errMain }
func (f matchStringOnly) WriteProfileTo(string, io.Writer, int) error { return errMain }
func (f matchStringOnly) ImportPath() string { return "" }
+func (f matchStringOnly) StartTestLog(io.Writer) {}
+func (f matchStringOnly) StopTestLog() error { return errMain }
// Main is an internal function, part of the implementation of the "go test" command.
// It was exported because it is cross-package and predates "internal" packages.
@@ -916,12 +920,14 @@ type M struct {
// The canonical implementation of this interface is
// testing/internal/testdeps's TestDeps.
type testDeps interface {
+ ImportPath() string
MatchString(pat, str string) (bool, error)
StartCPUProfile(io.Writer) error
StopCPUProfile()
+ StartTestLog(io.Writer)
+ StopTestLog() error
WriteHeapProfile(io.Writer) error
WriteProfileTo(string, io.Writer, int) error
- ImportPath() string
}
// MainStart is meant for use by tests generated by 'go test'.
@@ -1100,6 +1106,17 @@ func (m *M) before() {
fmt.Fprintf(os.Stderr, "testing: cannot use -test.coverprofile because test binary was not built with coverage enabled\n")
os.Exit(2)
}
+ if *testlog != "" {
+ // Note: Not using toOutputDir.
+ // This file is for use by cmd/go, not users.
+ f, err := os.Create(*testlog)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ os.Exit(2)
+ }
+ m.deps.StartTestLog(f)
+ testlogFile = f
+ }
}
// after runs after all testing.
@@ -1110,6 +1127,16 @@ func (m *M) after() {
}
func (m *M) writeProfiles() {
+ if *testlog != "" {
+ if err := m.deps.StopTestLog(); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *testlog, err)
+ os.Exit(2)
+ }
+ if err := testlogFile.Close(); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *testlog, err)
+ os.Exit(2)
+ }
+ }
if *cpuProfile != "" {
m.deps.StopCPUProfile() // flushes profile to disk
}