cmd/go: convert TestTestCacheInputs to a script test

Updates #28387
Updates #30316

Change-Id: I48c6dd8619ea9602e9617ce11dfa05f1c70a485d
Reviewed-on: https://go-review.googlesource.com/c/go/+/207958
Reviewed-by: Jay Conrod <jayconrod@google.com>
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index c5c5d41..3760703 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -4870,122 +4870,6 @@
 	}
 }
 
-func TestTestCacheInputs(t *testing.T) {
-	tooSlow(t)
-
-	if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
-		t.Skip("GODEBUG gocacheverify")
-	}
-	tg := testgo(t)
-	defer tg.cleanup()
-	tg.parallel()
-	tg.makeTempdir()
-	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
-	tg.setenv("GOCACHE", tg.path("cache"))
-
-	defer os.Remove(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"))
-	defer os.Remove(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"))
-	tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("x"), 0644))
-	old := time.Now().Add(-1 * time.Minute)
-	tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), old, old))
-	info, err := os.Stat(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"))
-	if err != nil {
-		t.Fatal(err)
-	}
-	t.Logf("file.txt: old=%v, info.ModTime=%v", old, info.ModTime()) // help debug when Chtimes lies about succeeding
-	tg.setenv("TESTKEY", "x")
-
-	tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"), []byte("#!/bin/sh\nexit 0\n"), 0755))
-	tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"), old, old))
-
-	tg.run("test", "testcache")
-	tg.run("test", "testcache")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-
-	tg.setenv("TESTKEY", "y")
-	tg.run("test", "testcache")
-	tg.grepStdoutNot(`\(cached\)`, "did not notice env var change")
-	tg.run("test", "testcache")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-
-	tg.run("test", "testcache", "-run=FileSize")
-	tg.run("test", "testcache", "-run=FileSize")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-	tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("xxx"), 0644))
-	tg.run("test", "testcache", "-run=FileSize")
-	tg.grepStdoutNot(`\(cached\)`, "did not notice file size change")
-	tg.run("test", "testcache", "-run=FileSize")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-
-	tg.run("test", "testcache", "-run=Chdir")
-	tg.run("test", "testcache", "-run=Chdir")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-	tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("xxxxx"), 0644))
-	tg.run("test", "testcache", "-run=Chdir")
-	tg.grepStdoutNot(`\(cached\)`, "did not notice file size change")
-	tg.run("test", "testcache", "-run=Chdir")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-
-	tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), old, old))
-	tg.run("test", "testcache", "-run=FileContent")
-	tg.run("test", "testcache", "-run=FileContent")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-	tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), []byte("yyy"), 0644))
-	old2 := old.Add(10 * time.Second)
-	tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt"), old2, old2))
-	tg.run("test", "testcache", "-run=FileContent")
-	tg.grepStdoutNot(`\(cached\)`, "did not notice file content change")
-	tg.run("test", "testcache", "-run=FileContent")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-
-	tg.run("test", "testcache", "-run=DirList")
-	tg.run("test", "testcache", "-run=DirList")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-	tg.must(os.Remove(filepath.Join(tg.pwd(), "testdata/src/testcache/file.txt")))
-	tg.run("test", "testcache", "-run=DirList")
-	tg.grepStdoutNot(`\(cached\)`, "did not notice directory change")
-	tg.run("test", "testcache", "-run=DirList")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-
-	tg.tempFile("file.txt", "")
-	tg.must(ioutil.WriteFile(filepath.Join(tg.pwd(), "testdata/src/testcache/testcachetmp_test.go"), []byte(`package testcache
-
-		import (
-			"os"
-			"testing"
-		)
-
-		func TestExternalFile(t *testing.T) {
-			os.Open(`+fmt.Sprintf("%q", tg.path("file.txt"))+`)
-			_, err := os.Stat(`+fmt.Sprintf("%q", tg.path("file.txt"))+`)
-			if err != nil {
-				t.Fatal(err)
-			}
-		}
-	`), 0666))
-	defer os.Remove(filepath.Join(tg.pwd(), "testdata/src/testcache/testcachetmp_test.go"))
-	tg.run("test", "testcache", "-run=ExternalFile")
-	tg.run("test", "testcache", "-run=ExternalFile")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-	tg.must(os.Remove(filepath.Join(tg.tempdir, "file.txt")))
-	tg.run("test", "testcache", "-run=ExternalFile")
-	tg.grepStdout(`\(cached\)`, "did not cache")
-
-	switch runtime.GOOS {
-	case "plan9", "windows":
-		// no shell scripts
-	default:
-		tg.run("test", "testcache", "-run=Exec")
-		tg.run("test", "testcache", "-run=Exec")
-		tg.grepStdout(`\(cached\)`, "did not cache")
-		tg.must(os.Chtimes(filepath.Join(tg.pwd(), "testdata/src/testcache/script.sh"), old2, old2))
-		tg.run("test", "testcache", "-run=Exec")
-		tg.grepStdoutNot(`\(cached\)`, "did not notice script change")
-		tg.run("test", "testcache", "-run=Exec")
-		tg.grepStdout(`\(cached\)`, "did not cache")
-	}
-}
-
 func TestTestVet(t *testing.T) {
 	tooSlow(t)
 	tg := testgo(t)
diff --git a/src/cmd/go/testdata/script/cache_vet.txt b/src/cmd/go/testdata/script/cache_vet.txt
index d61e9bc..928024e 100644
--- a/src/cmd/go/testdata/script/cache_vet.txt
+++ b/src/cmd/go/testdata/script/cache_vet.txt
@@ -1,7 +1,7 @@
 env GO111MODULE=off
 
 [short] skip
-[GODEBUG:gocacheverify] skip
+[GODEBUG:gocacheverify=1] skip
 [gccgo] skip  # gccgo has no standard packages
 
 # Start with a clean build cache:
diff --git a/src/cmd/go/testdata/script/test_cache_inputs.txt b/src/cmd/go/testdata/script/test_cache_inputs.txt
new file mode 100644
index 0000000..46faca0
--- /dev/null
+++ b/src/cmd/go/testdata/script/test_cache_inputs.txt
@@ -0,0 +1,230 @@
+env GO111MODULE=off
+
+# Test that cached test results are invalidated in response to
+# changes to the external inputs to the test.
+
+[short] skip
+[GODEBUG:gocacheverify=1] skip
+
+# We're testing cache behavior, so start with a clean GOCACHE.
+env GOCACHE=$WORK/cache
+
+# Build a helper binary to invoke os.Chtimes.
+go build -o mkold$GOEXE mkold.go
+
+# Make test input files appear to be a minute old.
+exec ./mkold$GOEXE 1m testcache/file.txt
+exec ./mkold$GOEXE 1m testcache/script.sh
+
+# If the test reads an environment variable, changes to that variable
+# should invalidate cached test results.
+env TESTKEY=x
+go test testcache -run=TestLookupEnv
+go test testcache -run=TestLookupEnv
+stdout '\(cached\)'
+
+env TESTKEY=y
+go test testcache -run=TestLookupEnv
+! stdout '\(cached\)'
+go test testcache -run=TestLookupEnv
+stdout '\(cached\)'
+
+# If the test stats a file, changes to the file should invalidate the cache.
+go test testcache -run=FileSize
+go test testcache -run=FileSize
+stdout '\(cached\)'
+
+cp 4x.txt testcache/file.txt
+go test testcache -run=FileSize
+! stdout '\(cached\)'
+go test testcache -run=FileSize
+stdout '\(cached\)'
+
+# Files should be tracked even if the test changes its working directory.
+go test testcache -run=Chdir
+go test testcache -run=Chdir
+stdout '\(cached\)'
+cp 6x.txt testcache/file.txt
+go test testcache -run=Chdir
+! stdout '\(cached\)'
+go test testcache -run=Chdir
+stdout '\(cached\)'
+
+# The content of files should affect caching, provided that the mtime also changes.
+exec ./mkold$GOEXE 1m testcache/file.txt
+go test testcache -run=FileContent
+go test testcache -run=FileContent
+stdout '\(cached\)'
+cp 2y.txt testcache/file.txt
+exec ./mkold$GOEXE 50s testcache/file.txt
+go test testcache -run=FileContent
+! stdout '\(cached\)'
+go test testcache -run=FileContent
+stdout '\(cached\)'
+
+# Directory contents read via os.ReadDirNames should affect caching.
+go test testcache -run=DirList
+go test testcache -run=DirList
+stdout '\(cached\)'
+rm testcache/file.txt
+go test testcache -run=DirList
+! stdout '\(cached\)'
+go test testcache -run=DirList
+stdout '\(cached\)'
+
+# Files outside GOROOT and GOPATH should not affect caching.
+env TEST_EXTERNAL_FILE=$WORK/external.txt
+go test testcache -run=ExternalFile
+go test testcache -run=ExternalFile
+stdout '\(cached\)'
+
+rm $WORK/external.txt
+go test testcache -run=ExternalFile
+stdout '\(cached\)'
+
+# Executables within GOROOT and GOPATH should affect caching,
+# even if the test does not stat them explicitly.
+
+[!exec:/bin/sh] skip
+chmod 0755 ./testcache/script.sh
+
+exec ./mkold$GOEXEC 1m testcache/script.sh
+go test testcache -run=Exec
+go test testcache -run=Exec
+stdout '\(cached\)'
+
+exec ./mkold$GOEXE 50s testcache/script.sh
+go test testcache -run=Exec
+! stdout '\(cached\)'
+go test testcache -run=Exec
+stdout '\(cached\)'
+
+-- testcache/file.txt --
+xx
+-- 4x.txt --
+xxxx
+-- 6x.txt --
+xxxxxx
+-- 2y.txt --
+yy
+-- $WORK/external.txt --
+This file is outside of GOPATH.
+-- testcache/script.sh --
+#!/bin/sh
+exit 0
+-- testcache/testcache_test.go --
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testcache
+
+import (
+	"io/ioutil"
+	"os"
+	"testing"
+)
+
+func TestChdir(t *testing.T) {
+	os.Chdir("..")
+	defer os.Chdir("testcache")
+	info, err := os.Stat("testcache/file.txt")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if info.Size()%2 != 1 {
+		t.Fatal("even file")
+	}
+}
+
+func TestOddFileContent(t *testing.T) {
+	f, err := os.Open("file.txt")
+	if err != nil {
+		t.Fatal(err)
+	}
+	data, err := ioutil.ReadAll(f)
+	f.Close()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(data)%2 != 1 {
+		t.Fatal("even file")
+	}
+}
+
+func TestOddFileSize(t *testing.T) {
+	info, err := os.Stat("file.txt")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if info.Size()%2 != 1 {
+		t.Fatal("even file")
+	}
+}
+
+func TestOddGetenv(t *testing.T) {
+	val := os.Getenv("TESTKEY")
+	if len(val)%2 != 1 {
+		t.Fatal("even env value")
+	}
+}
+
+func TestLookupEnv(t *testing.T) {
+	_, ok := os.LookupEnv("TESTKEY")
+	if !ok {
+		t.Fatal("env missing")
+	}
+}
+
+func TestDirList(t *testing.T) {
+	f, err := os.Open(".")
+	if err != nil {
+		t.Fatal(err)
+	}
+	f.Readdirnames(-1)
+	f.Close()
+}
+
+func TestExec(t *testing.T) {
+	// Note: not using os/exec to make sure there is no unexpected stat.
+	p, err := os.StartProcess("./script.sh", []string{"script"}, new(os.ProcAttr))
+	if err != nil {
+		t.Fatal(err)
+	}
+	ps, err := p.Wait()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !ps.Success() {
+		t.Fatalf("script failed: %v", err)
+	}
+}
+
+func TestExternalFile(t *testing.T) {
+	os.Open(os.Getenv("TEST_EXTERNAL_FILE"))
+	_, err := os.Stat(os.Getenv("TEST_EXTERNAL_FILE"))
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+-- mkold.go --
+package main
+
+import (
+	"log"
+	"os"
+	"time"
+)
+
+func main() {
+	d, err := time.ParseDuration(os.Args[1])
+	if err != nil {
+		log.Fatal(err)
+	}
+	path := os.Args[2]
+	old := time.Now().Add(-d)
+	err = os.Chtimes(path, old, old)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/src/cmd/go/testdata/src/testcache/testcache_test.go b/src/cmd/go/testdata/src/testcache/testcache_test.go
deleted file mode 100644
index 3de7c42..0000000
--- a/src/cmd/go/testdata/src/testcache/testcache_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2017 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package testcache
-
-import (
-	"io/ioutil"
-	"os"
-	"runtime"
-	"testing"
-)
-
-func TestChdir(t *testing.T) {
-	os.Chdir("..")
-	defer os.Chdir("testcache")
-	info, err := os.Stat("testcache/file.txt")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if info.Size()%2 != 1 {
-		t.Fatal("even file")
-	}
-}
-
-func TestOddFileContent(t *testing.T) {
-	f, err := os.Open("file.txt")
-	if err != nil {
-		t.Fatal(err)
-	}
-	data, err := ioutil.ReadAll(f)
-	f.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(data)%2 != 1 {
-		t.Fatal("even file")
-	}
-}
-
-func TestOddFileSize(t *testing.T) {
-	info, err := os.Stat("file.txt")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if info.Size()%2 != 1 {
-		t.Fatal("even file")
-	}
-}
-
-func TestOddGetenv(t *testing.T) {
-	val := os.Getenv("TESTKEY")
-	if len(val)%2 != 1 {
-		t.Fatal("even env value")
-	}
-}
-
-func TestLookupEnv(t *testing.T) {
-	_, ok := os.LookupEnv("TESTKEY")
-	if !ok {
-		t.Fatal("env missing")
-	}
-}
-
-func TestDirList(t *testing.T) {
-	f, err := os.Open(".")
-	if err != nil {
-		t.Fatal(err)
-	}
-	f.Readdirnames(-1)
-	f.Close()
-}
-
-func TestExec(t *testing.T) {
-	if runtime.GOOS == "plan9" || runtime.GOOS == "windows" {
-		t.Skip("non-unix")
-	}
-
-	// Note: not using os/exec to make sure there is no unexpected stat.
-	p, err := os.StartProcess("./script.sh", []string{"script"}, new(os.ProcAttr))
-	if err != nil {
-		t.Fatal(err)
-	}
-	ps, err := p.Wait()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !ps.Success() {
-		t.Fatalf("script failed: %v", err)
-	}
-}