Skip to content

Commit bd95f88

Browse files
committed
cmd/go: cache successful test results
This CL adds caching of successful test results, keyed by the action ID of the test binary and its command line arguments. Suppose you run: go test -short std <edit a typo in a comment in math/big/float.go> go test -short std Before this CL, the second go test would re-run all the tests for the std packages. Now, the second go test will use the cached result immediately (without any compile or link steps) for any packages that do not transitively import math/big, and then it will, after compiling math/big and seeing that the .a file didn't change, reuse the cached test results for the remaining packages without any additional compile or link steps. Suppose that instead of editing a typo you made a substantive change to one function, but you left the others (including their line numbers) unchanged. Then the second go test will re-link any of the tests that transitively depend on math/big, but it still will not re-run the tests, because the link will result in the same test binary as the first run. The only cacheable test arguments are: -cpu -list -parallel -run -short -v Using any other test flag disables the cache for that run. The suggested argument to mean "turn off the cache" is -count=1 (asking "please run this 1 time, not 0"). There's an open question about re-running tests when inputs like environment variables and input files change. For now we will assume that users will bypass the test cache when they need to do so, using -count=1 or "go test" with no arguments. This CL documents the new cache but also documents the previously-undocumented distinction between "go test" with no arguments (now called "local directory mode") and with arguments (now called "package list mode"). It also cleans up a minor detail of package list mode buffering that used to change whether test binary stderr was sent to go command stderr based on details like exactly how many packages were listed or how many CPUs the host system had. Clearly the file descriptor receiving output should not depend on those, so package list mode now consistently merges all output to stdout, where before it mostly did that but not always. Fixes #11193. Change-Id: I120edef347b9ddd5b10e247bfd5bd768db9c2182 Reviewed-on: https://go-review.googlesource.com/75631 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: David Crawshaw <crawshaw@golang.org>
1 parent 89bcbf4 commit bd95f88

File tree

8 files changed

+427
-79
lines changed

8 files changed

+427
-79
lines changed

misc/cgo/testplugin/test.bash

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ fi
1414
goos=$(go env GOOS)
1515
goarch=$(go env GOARCH)
1616

17+
echo SKIP: golang.org/issue/22571.
18+
exit 0
19+
1720
function cleanup() {
1821
rm -f plugin*.so unnamed*.so iface*.so issue*
1922
rm -rf host pkg sub iface

src/cmd/dist/deps.go

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/go/alldocs.go

+32-6
Original file line numberDiff line numberDiff line change
@@ -724,22 +724,48 @@
724724
//
725725
// 'Go test' recompiles each package along with any files with names matching
726726
// the file pattern "*_test.go".
727-
// Files whose names begin with "_" (including "_test.go") or "." are ignored.
728727
// These additional files can contain test functions, benchmark functions, and
729728
// example functions. See 'go help testfunc' for more.
730729
// Each listed package causes the execution of a separate test binary.
730+
// Files whose names begin with "_" (including "_test.go") or "." are ignored.
731731
//
732732
// Test files that declare a package with the suffix "_test" will be compiled as a
733733
// separate package, and then linked and run with the main test binary.
734734
//
735735
// The go tool will ignore a directory named "testdata", making it available
736736
// to hold ancillary data needed by the tests.
737737
//
738-
// By default, go test needs no arguments. It compiles and tests the package
739-
// with source in the current directory, including tests, and runs the tests.
740-
//
741-
// The package is built in a temporary directory so it does not interfere with the
742-
// non-test installation.
738+
// Go test runs in two different modes: local directory mode when invoked with
739+
// no package arguments (for example, 'go test'), and package list mode when
740+
// invoked with package arguments (for example 'go test math', 'go test ./...',
741+
// and even 'go test .').
742+
//
743+
// In local directory mode, go test compiles and tests the package sources
744+
// found in the current directory and then runs the resulting test binary.
745+
// In this mode, the test binary runs with standard output and standard error
746+
// connected directly to the go command's own standard output and standard
747+
// error, and test result caching (discussed below) is disabled.
748+
// After the package test finishes, go test prints to standard output a
749+
// summary line showing the test status ('ok' or 'FAIL'), package name,
750+
// and elapsed time.
751+
//
752+
// In package list mode, go test compiles and tests each of the packages
753+
// listed on the command line. If a package test passes, go test prints only
754+
// the final 'ok' summary line. If a package test fails, go test prints the
755+
// full test output. If invoked with the -bench or -v flag, go test prints
756+
// the full output even for passing package tests, in order to display the
757+
// requested benchmark results or verbose logging. In package list mode,
758+
// go test prints all test output and summary lines to standard output.
759+
//
760+
// In package list mode, go test also caches successful package test results.
761+
// If go test has cached a previous test run using the same test binary and
762+
// the same command line consisting entirely of cacheable test flags
763+
// (defined as -cpu, -list, -parallel, -run, -short, and -v),
764+
// go test will redisplay the previous output instead of running the test
765+
// binary again. In the summary line, go test prints '(cached)' in place of
766+
// the elapsed time. To disable test caching, use any test flag or argument
767+
// other than the cacheable flags. The idiomatic way to disable test caching
768+
// explicitly is to use -count=1.
743769
//
744770
// In addition to the build flags, the flags handled by 'go test' itself are:
745771
//

src/cmd/go/go_test.go

+93
Original file line numberDiff line numberDiff line change
@@ -4741,3 +4741,96 @@ func TestBuildCache(t *testing.T) {
47414741
tg.grepStderr(`[\\/]link|gccgo`, "did not run linker")
47424742

47434743
}
4744+
4745+
func TestTestCache(t *testing.T) {
4746+
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
4747+
t.Skip("GODEBUG gocacheverify")
4748+
}
4749+
tg := testgo(t)
4750+
defer tg.cleanup()
4751+
tg.makeTempdir()
4752+
tg.setenv("GOPATH", tg.tempdir)
4753+
tg.setenv("GOCACHE", filepath.Join(tg.tempdir, "cache"))
4754+
4755+
tg.run("test", "-x", "errors")
4756+
tg.grepStderr(`[\\/]compile|gccgo`, "did not run compiler")
4757+
tg.grepStderr(`[\\/]link|gccgo`, "did not run linker")
4758+
tg.grepStderr(`errors\.test`, "did not run test")
4759+
4760+
tg.run("test", "-x", "errors")
4761+
tg.grepStdout(`ok \terrors\t\(cached\)`, "did not report cached result")
4762+
tg.grepStderrNot(`[\\/]compile|gccgo`, "incorrectly ran compiler")
4763+
tg.grepStderrNot(`[\\/]link|gccgo`, "incorrectly ran linker")
4764+
tg.grepStderrNot(`errors\.test`, "incorrectly ran test")
4765+
tg.grepStderrNot("DO NOT USE", "poisoned action status leaked")
4766+
4767+
// The -p=1 in the commands below just makes the -x output easier to read.
4768+
4769+
t.Log("\n\nINITIAL\n\n")
4770+
4771+
tg.tempFile("src/p1/p1.go", "package p1\nvar X = 1\n")
4772+
tg.tempFile("src/p2/p2.go", "package p2\nimport _ \"p1\"\nvar X = 1\n")
4773+
tg.tempFile("src/t/t1/t1_test.go", "package t\nimport \"testing\"\nfunc Test1(*testing.T) {}\n")
4774+
tg.tempFile("src/t/t2/t2_test.go", "package t\nimport _ \"p1\"\nimport \"testing\"\nfunc Test2(*testing.T) {}\n")
4775+
tg.tempFile("src/t/t3/t3_test.go", "package t\nimport \"p1\"\nimport \"testing\"\nfunc Test3(t *testing.T) {t.Log(p1.X)}\n")
4776+
tg.tempFile("src/t/t4/t4_test.go", "package t\nimport \"p2\"\nimport \"testing\"\nfunc Test4(t *testing.T) {t.Log(p2.X)}")
4777+
tg.run("test", "-x", "-v", "-short", "t/...")
4778+
4779+
t.Log("\n\nREPEAT\n\n")
4780+
4781+
tg.run("test", "-x", "-v", "-short", "t/...")
4782+
tg.grepStdout(`ok \tt/t1\t\(cached\)`, "did not cache t1")
4783+
tg.grepStdout(`ok \tt/t2\t\(cached\)`, "did not cache t2")
4784+
tg.grepStdout(`ok \tt/t3\t\(cached\)`, "did not cache t3")
4785+
tg.grepStdout(`ok \tt/t4\t\(cached\)`, "did not cache t4")
4786+
tg.grepStderrNot(`[\\/]compile|gccgo`, "incorrectly ran compiler")
4787+
tg.grepStderrNot(`[\\/]link|gccgo`, "incorrectly ran linker")
4788+
tg.grepStderrNot(`p[0-9]\.test`, "incorrectly ran test")
4789+
4790+
t.Log("\n\nCOMMENT\n\n")
4791+
4792+
// Changing the program text without affecting the compiled package
4793+
// should result in the package being rebuilt but nothing more.
4794+
tg.tempFile("src/p1/p1.go", "package p1\nvar X = 01\n")
4795+
tg.run("test", "-p=1", "-x", "-v", "-short", "t/...")
4796+
tg.grepStdout(`ok \tt/t1\t\(cached\)`, "did not cache t1")
4797+
tg.grepStdout(`ok \tt/t2\t\(cached\)`, "did not cache t2")
4798+
tg.grepStdout(`ok \tt/t3\t\(cached\)`, "did not cache t3")
4799+
tg.grepStdout(`ok \tt/t4\t\(cached\)`, "did not cache t4")
4800+
tg.grepStderrNot(`([\\/]compile|gccgo).*t[0-9]_test\.go`, "incorrectly ran compiler")
4801+
tg.grepStderrNot(`[\\/]link|gccgo`, "incorrectly ran linker")
4802+
tg.grepStderrNot(`t[0-9]\.test.*test\.short`, "incorrectly ran test")
4803+
4804+
t.Log("\n\nCHANGE\n\n")
4805+
4806+
// Changing the actual package should have limited effects.
4807+
tg.tempFile("src/p1/p1.go", "package p1\nvar X = 02\n")
4808+
tg.run("test", "-p=1", "-x", "-v", "-short", "t/...")
4809+
4810+
// p2 should have been rebuilt.
4811+
tg.grepStderr(`([\\/]compile|gccgo).*p2.go`, "did not recompile p2")
4812+
4813+
// t1 does not import anything, should not have been rebuilt.
4814+
tg.grepStderrNot(`([\\/]compile|gccgo).*t1_test.go`, "incorrectly recompiled t1")
4815+
tg.grepStderrNot(`([\\/]link|gccgo).*t1_test`, "incorrectly relinked t1_test")
4816+
tg.grepStdout(`ok \tt/t1\t\(cached\)`, "did not cache t/t1")
4817+
4818+
// t2 imports p1 and must be rebuilt and relinked,
4819+
// but the change should not have any effect on the test binary,
4820+
// so the test should not have been rerun.
4821+
tg.grepStderr(`([\\/]compile|gccgo).*t2_test.go`, "did not recompile t2")
4822+
tg.grepStderr(`([\\/]link|gccgo).*t2\.test`, "did not relink t2_test")
4823+
tg.grepStdout(`ok \tt/t2\t\(cached\)`, "did not cache t/t2")
4824+
4825+
// t3 imports p1, and changing X changes t3's test binary.
4826+
tg.grepStderr(`([\\/]compile|gccgo).*t3_test.go`, "did not recompile t3")
4827+
tg.grepStderr(`([\\/]link|gccgo).*t3\.test`, "did not relink t3_test")
4828+
tg.grepStderr(`t3\.test.*-test.short`, "did not rerun t3_test")
4829+
tg.grepStdoutNot(`ok \tt/t3\t\(cached\)`, "reported cached t3_test result")
4830+
4831+
// t4 imports p2, but p2 did not change, so t4 should be relinked, not recompiled,
4832+
// and not rerun.
4833+
tg.grepStderrNot(`([\\/]compile|gccgo).*t4_test.go`, "incorrectly recompiled t4")
4834+
tg.grepStderr(`([\\/]link|gccgo).*t4\.test`, "did not relink t4_test")
4835+
tg.grepStdout(`ok \tt/t4\t\(cached\)`, "did not cache t/t4")
4836+
}

0 commit comments

Comments
 (0)