1
0
mirror of https://github.com/golang/go synced 2024-10-02 08:18:32 -06:00

testing: ensure profiles are written upon -timeout panic

This addresses the case of a -timeout panic, but not the more
general case of a signal arriving. See CL 48370 and CL 44352
for recent difficulties in that area.

"-timeout" here means flag usage to distinguish from the
default timeout termination which uses signals.

Fixes #19394

Change-Id: I5452d5422c0c080e940cbcc8c6606049975268c6
Reviewed-on: https://go-review.googlesource.com/48491
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Meir Fischer 2017-07-13 22:51:08 -04:00 committed by Ian Lance Taylor
parent 85deaf6077
commit 7e455b628c
2 changed files with 48 additions and 10 deletions

View File

@ -599,6 +599,19 @@ func (tg *testgoData) mustNotExist(path string) {
} }
} }
// mustHaveContent succeeds if filePath is a path to a file,
// and that file is readable and not empty.
func (tg *testgoData) mustHaveContent(filePath string) {
tg.mustExist(filePath)
f, err := os.Stat(filePath)
if err != nil {
tg.t.Fatal(err)
}
if f.Size() == 0 {
tg.t.Fatalf("expected %s to have data, but is empty", filePath)
}
}
// wantExecutable fails with msg if path is not executable. // wantExecutable fails with msg if path is not executable.
func (tg *testgoData) wantExecutable(path, msg string) { func (tg *testgoData) wantExecutable(path, msg string) {
tg.t.Helper() tg.t.Helper()
@ -3909,6 +3922,24 @@ func TestBenchTimeout(t *testing.T) {
tg.run("test", "-bench", ".", "-timeout", "750ms", "testdata/timeoutbench_test.go") tg.run("test", "-bench", ".", "-timeout", "750ms", "testdata/timeoutbench_test.go")
} }
// Issue 19394
func TestWriteProfilesOnTimeout(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.tempDir("profiling")
tg.tempFile("profiling/timeouttest_test.go", `package timeouttest_test
import "testing"
import "time"
func TestSleep(t *testing.T) { time.Sleep(time.Second) }`)
tg.cd(tg.path("profiling"))
tg.runFail(
"test",
"-cpuprofile", tg.path("profiling/cpu.pprof"), "-memprofile", tg.path("profiling/mem.pprof"),
"-timeout", "1ms")
tg.mustHaveContent(tg.path("profiling/cpu.pprof"))
tg.mustHaveContent(tg.path("profiling/mem.pprof"))
}
func TestLinkXImportPathEscape(t *testing.T) { func TestLinkXImportPathEscape(t *testing.T) {
// golang.org/issue/16710 // golang.org/issue/16710
tg := testgo(t) tg := testgo(t)

View File

@ -876,6 +876,9 @@ type M struct {
tests []InternalTest tests []InternalTest
benchmarks []InternalBenchmark benchmarks []InternalBenchmark
examples []InternalExample examples []InternalExample
timer *time.Timer
afterOnce sync.Once
} }
// testDeps is an internal interface of functionality that is // testDeps is an internal interface of functionality that is
@ -918,22 +921,21 @@ func (m *M) Run() int {
parseCpuList() parseCpuList()
m.before() m.before()
startAlarm() defer m.after()
m.startAlarm()
haveExamples = len(m.examples) > 0 haveExamples = len(m.examples) > 0
testRan, testOk := runTests(m.deps.MatchString, m.tests) testRan, testOk := runTests(m.deps.MatchString, m.tests)
exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples) exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
stopAlarm() m.stopAlarm()
if !testRan && !exampleRan && *matchBenchmarks == "" { if !testRan && !exampleRan && *matchBenchmarks == "" {
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run") fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
} }
if !testOk || !exampleOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 { if !testOk || !exampleOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 {
fmt.Println("FAIL") fmt.Println("FAIL")
m.after()
return 1 return 1
} }
fmt.Println("PASS") fmt.Println("PASS")
m.after()
return 0 return 0
} }
@ -1063,6 +1065,12 @@ func (m *M) before() {
// after runs after all testing. // after runs after all testing.
func (m *M) after() { func (m *M) after() {
m.afterOnce.Do(func() {
m.writeProfiles()
})
}
func (m *M) writeProfiles() {
if *cpuProfile != "" { if *cpuProfile != "" {
m.deps.StopCPUProfile() // flushes profile to disk m.deps.StopCPUProfile() // flushes profile to disk
} }
@ -1139,12 +1147,11 @@ func toOutputDir(path string) string {
return fmt.Sprintf("%s%c%s", *outputDir, os.PathSeparator, path) return fmt.Sprintf("%s%c%s", *outputDir, os.PathSeparator, path)
} }
var timer *time.Timer
// startAlarm starts an alarm if requested. // startAlarm starts an alarm if requested.
func startAlarm() { func (m *M) startAlarm() {
if *timeout > 0 { if *timeout > 0 {
timer = time.AfterFunc(*timeout, func() { m.timer = time.AfterFunc(*timeout, func() {
m.after()
debug.SetTraceback("all") debug.SetTraceback("all")
panic(fmt.Sprintf("test timed out after %v", *timeout)) panic(fmt.Sprintf("test timed out after %v", *timeout))
}) })
@ -1152,9 +1159,9 @@ func startAlarm() {
} }
// stopAlarm turns off the alarm. // stopAlarm turns off the alarm.
func stopAlarm() { func (m *M) stopAlarm() {
if *timeout > 0 { if *timeout > 0 {
timer.Stop() m.timer.Stop()
} }
} }