1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
27 func TestMain(m
*testing
.M
) {
29 for _
, file
:= range toRemove
{
38 target
map[string]buildexe
41 type buildexe
struct {
46 func runTestProg(t
*testing
.T
, binary
, name
string, env
...string) string {
51 testenv
.MustHaveGoBuild(t
)
53 exe
, err
:= buildTestProg(t
, binary
)
58 cmd
:= testenv
.CleanCmdEnv(exec
.Command(exe
, name
))
59 cmd
.Env
= append(cmd
.Env
, env
...)
61 cmd
.Env
= append(cmd
.Env
, "RUNTIME_TEST_SHORT=1")
66 if err
:= cmd
.Start(); err
!= nil {
67 t
.Fatalf("starting %s %s: %v", binary
, name
, err
)
70 // If the process doesn't complete within 1 minute,
71 // assume it is hanging and kill it to get a stack trace.
73 done
:= make(chan bool)
76 // This GOARCH/GOOS test is copied from cmd/dist/test.go.
77 // TODO(iant): Have cmd/dist update the environment variable.
78 if runtime
.GOARCH
== "arm" || runtime
.GOOS
== "windows" {
81 if s
:= os
.Getenv("GO_TEST_TIMEOUT_SCALE"); s
!= "" {
82 if sc
, err
:= strconv
.Atoi(s
); err
== nil {
89 case <-time
.After(time
.Duration(scale
) * time
.Minute
):
94 if err
:= cmd
.Wait(); err
!= nil {
95 t
.Logf("%s %s exit status: %v", binary
, name
, err
)
102 func buildTestProg(t
*testing
.T
, binary
string, flags
...string) (string, error
) {
110 defer testprog
.Unlock()
111 if testprog
.dir
== "" {
112 dir
, err
:= ioutil
.TempDir("", "go-build")
114 t
.Fatalf("failed to create temp directory: %v", err
)
117 toRemove
= append(toRemove
, dir
)
120 if testprog
.target
== nil {
121 testprog
.target
= make(map[string]buildexe
)
125 name
+= "_" + strings
.Join(flags
, "_")
127 target
, ok
:= testprog
.target
[name
]
129 return target
.exe
, target
.err
132 exe
:= filepath
.Join(testprog
.dir
, name
+".exe")
133 cmd
:= exec
.Command(testenv
.GoToolPath(t
), append([]string{"build", "-o", exe
}, flags
...)...)
134 cmd
.Dir
= "testdata/" + binary
135 out
, err
:= testenv
.CleanCmdEnv(cmd
).CombinedOutput()
137 target
.err
= fmt
.Errorf("building %s %v: %v\n%s", binary
, flags
, err
, out
)
138 testprog
.target
[name
] = target
139 return "", target
.err
142 testprog
.target
[name
] = target
147 staleRuntimeOnce sync
.Once
// guards init of staleRuntimeErr
148 staleRuntimeErr error
151 func checkStaleRuntime(t
*testing
.T
) {
152 staleRuntimeOnce
.Do(func() {
153 // 'go run' uses the installed copy of runtime.a, which may be out of date.
154 out
, err
:= testenv
.CleanCmdEnv(exec
.Command(testenv
.GoToolPath(t
), "list", "-gcflags=all="+os
.Getenv("GO_GCFLAGS"), "-f", "{{.Stale}}", "runtime")).CombinedOutput()
156 staleRuntimeErr
= fmt
.Errorf("failed to execute 'go list': %v\n%v", err
, string(out
))
159 if string(out
) != "false\n" {
160 t
.Logf("go list -f {{.Stale}} runtime:\n%s", out
)
161 out
, err
:= testenv
.CleanCmdEnv(exec
.Command(testenv
.GoToolPath(t
), "list", "-gcflags=all="+os
.Getenv("GO_GCFLAGS"), "-f", "{{.StaleReason}}", "runtime")).CombinedOutput()
163 t
.Logf("go list -f {{.StaleReason}} failed: %v", err
)
165 t
.Logf("go list -f {{.StaleReason}} runtime:\n%s", out
)
166 staleRuntimeErr
= fmt
.Errorf("Stale runtime.a. Run 'go install runtime'.")
169 if staleRuntimeErr
!= nil {
170 t
.Fatal(staleRuntimeErr
)
174 func testCrashHandler(t
*testing
.T
, cgo
bool) {
175 type crashTest
struct {
180 output
= runTestProg(t
, "testprogcgo", "Crash")
182 output
= runTestProg(t
, "testprog", "Crash")
184 want
:= "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
186 t
.Fatalf("output:\n%s\n\nwanted:\n%s", output
, want
)
190 func TestCrashHandler(t
*testing
.T
) {
191 testCrashHandler(t
, false)
194 func testDeadlock(t
*testing
.T
, name
string) {
195 output
:= runTestProg(t
, "testprog", name
)
196 want
:= "fatal error: all goroutines are asleep - deadlock!\n"
197 if !strings
.HasPrefix(output
, want
) {
198 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
202 func TestSimpleDeadlock(t
*testing
.T
) {
203 testDeadlock(t
, "SimpleDeadlock")
206 func TestInitDeadlock(t
*testing
.T
) {
207 testDeadlock(t
, "InitDeadlock")
210 func TestLockedDeadlock(t
*testing
.T
) {
211 testDeadlock(t
, "LockedDeadlock")
214 func TestLockedDeadlock2(t
*testing
.T
) {
215 testDeadlock(t
, "LockedDeadlock2")
218 func TestGoexitDeadlock(t
*testing
.T
) {
219 output
:= runTestProg(t
, "testprog", "GoexitDeadlock")
220 want
:= "no goroutines (main called runtime.Goexit) - deadlock!"
221 if !strings
.Contains(output
, want
) {
222 t
.Fatalf("output:\n%s\n\nwant output containing: %s", output
, want
)
226 func TestStackOverflow(t
*testing
.T
) {
227 if runtime
.Compiler
== "gccgo" {
228 t
.Skip("gccgo does not do stack overflow checking")
230 output
:= runTestProg(t
, "testprog", "StackOverflow")
231 want
:= "runtime: goroutine stack exceeds 1474560-byte limit\nfatal error: stack overflow"
232 if !strings
.HasPrefix(output
, want
) {
233 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
237 func TestThreadExhaustion(t
*testing
.T
) {
238 output
:= runTestProg(t
, "testprog", "ThreadExhaustion")
239 want
:= "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
240 if !strings
.HasPrefix(output
, want
) {
241 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
245 func TestRecursivePanic(t
*testing
.T
) {
246 output
:= runTestProg(t
, "testprog", "RecursivePanic")
251 if !strings
.HasPrefix(output
, want
) {
252 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
257 func TestGoexitCrash(t
*testing
.T
) {
258 output
:= runTestProg(t
, "testprog", "GoexitExit")
259 want
:= "no goroutines (main called runtime.Goexit) - deadlock!"
260 if !strings
.Contains(output
, want
) {
261 t
.Fatalf("output:\n%s\n\nwant output containing: %s", output
, want
)
265 func TestGoexitDefer(t
*testing
.T
) {
266 c
:= make(chan struct{})
271 t
.Errorf("non-nil recover during Goexit")
277 // Note: if the defer fails to run, we will get a deadlock here
281 func TestGoNil(t
*testing
.T
) {
282 output
:= runTestProg(t
, "testprog", "GoNil")
283 want
:= "go of nil func value"
284 if !strings
.Contains(output
, want
) {
285 t
.Fatalf("output:\n%s\n\nwant output containing: %s", output
, want
)
289 func TestMainGoroutineID(t
*testing
.T
) {
290 output
:= runTestProg(t
, "testprog", "MainGoroutineID")
291 want
:= "panic: test\n\ngoroutine 1 [running]:\n"
292 if !strings
.HasPrefix(output
, want
) {
293 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
297 func TestNoHelperGoroutines(t
*testing
.T
) {
298 output
:= runTestProg(t
, "testprog", "NoHelperGoroutines")
299 matches
:= regexp
.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output
, -1)
300 if len(matches
) != 1 || matches
[0][0] != "goroutine 1 [" {
301 t
.Fatalf("want to see only goroutine 1, see:\n%s", output
)
305 func TestBreakpoint(t
*testing
.T
) {
306 output
:= runTestProg(t
, "testprog", "Breakpoint")
307 // If runtime.Breakpoint() is inlined, then the stack trace prints
308 // "runtime.Breakpoint(...)" instead of "runtime.Breakpoint()".
309 // For gccgo, no parens.
310 want
:= "runtime.Breakpoint"
311 if !strings
.Contains(output
, want
) {
312 t
.Fatalf("output:\n%s\n\nwant output containing: %s", output
, want
)
316 func TestGoexitInPanic(t
*testing
.T
) {
317 // see issue 8774: this code used to trigger an infinite recursion
318 output
:= runTestProg(t
, "testprog", "GoexitInPanic")
319 want
:= "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
320 if !strings
.HasPrefix(output
, want
) {
321 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
325 // Issue 14965: Runtime panics should be of type runtime.Error
326 func TestRuntimePanicWithRuntimeError(t
*testing
.T
) {
327 testCases
:= [...]func(){
329 var m
map[uint64]bool
333 ch
:= make(chan struct{})
338 var ch
= make(chan struct{})
343 var s
= make([]int, 2)
348 _
= make(chan bool, n
)
351 close((chan bool)(nil))
355 for i
, fn
:= range testCases
{
356 got
:= panicValue(fn
)
357 if _
, ok
:= got
.(runtime
.Error
); !ok
{
358 t
.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i
, got
, got
)
363 func panicValue(fn
func()) (recovered
interface{}) {
365 recovered
= recover()
371 func TestPanicAfterGoexit(t
*testing
.T
) {
372 // an uncaught panic should still work after goexit
373 output
:= runTestProg(t
, "testprog", "PanicAfterGoexit")
374 want
:= "panic: hello"
375 if !strings
.HasPrefix(output
, want
) {
376 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
380 func TestRecoveredPanicAfterGoexit(t
*testing
.T
) {
381 output
:= runTestProg(t
, "testprog", "RecoveredPanicAfterGoexit")
382 want
:= "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
383 if !strings
.HasPrefix(output
, want
) {
384 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
388 func TestRecoverBeforePanicAfterGoexit(t
*testing
.T
) {
389 // 1. defer a function that recovers
390 // 2. defer a function that panics
392 // Goexit should run the #2 defer. Its panic
393 // should be caught by the #1 defer, and execution
394 // should resume in the caller. Like the Goexit
408 func TestNetpollDeadlock(t
*testing
.T
) {
410 output
:= runTestProg(t
, "testprognet", "NetpollDeadlock")
412 if !strings
.HasSuffix(output
, want
) {
413 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
417 func TestPanicTraceback(t
*testing
.T
) {
419 output
:= runTestProg(t
, "testprog", "PanicTraceback")
420 want
:= "panic: hello"
421 if !strings
.HasPrefix(output
, want
) {
422 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
425 // Check functions in the traceback.
426 fns
:= []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
427 if runtime
.Compiler
== "gccgo" {
428 fns
= []string{"main.pt1..func1", "panic", "main.pt2..func1", "panic", "main.pt2", "main.pt1"}
430 for _
, fn
:= range fns
{
431 var re
*regexp
.Regexp
432 if runtime
.Compiler
!= "gccgo" {
433 re
= regexp
.MustCompile(`(?m)^` + regexp
.QuoteMeta(fn
) + `\(.*\n`)
435 re
= regexp
.MustCompile(`(?m)^` + regexp
.QuoteMeta(fn
) + `.*\n`)
437 idx
:= re
.FindStringIndex(output
)
439 t
.Fatalf("expected %q function in traceback:\n%s", fn
, output
)
441 output
= output
[idx
[1]:]
445 func testPanicDeadlock(t
*testing
.T
, name
string, want
string) {
447 output
:= runTestProg(t
, "testprog", name
)
448 if !strings
.HasPrefix(output
, want
) {
449 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
453 func TestPanicDeadlockGosched(t
*testing
.T
) {
454 testPanicDeadlock(t
, "GoschedInPanic", "panic: errorThatGosched\n\n")
457 func TestPanicDeadlockSyscall(t
*testing
.T
) {
458 testPanicDeadlock(t
, "SyscallInPanic", "1\n2\npanic: 3\n\n")
461 func TestPanicLoop(t
*testing
.T
) {
462 output
:= runTestProg(t
, "testprog", "PanicLoop")
463 if want
:= "panic while printing panic value"; !strings
.Contains(output
, want
) {
464 t
.Errorf("output does not contain %q:\n%s", want
, output
)
468 func TestMemPprof(t
*testing
.T
) {
469 testenv
.MustHaveGoRun(t
)
470 if runtime
.Compiler
== "gccgo" {
471 t
.Skip("gccgo may not have the pprof tool")
474 exe
, err
:= buildTestProg(t
, "testprog")
479 got
, err
:= testenv
.CleanCmdEnv(exec
.Command(exe
, "MemProf")).CombinedOutput()
483 fn
:= strings
.TrimSpace(string(got
))
486 for try
:= 0; try
< 2; try
++ {
487 cmd
:= testenv
.CleanCmdEnv(exec
.Command(testenv
.GoToolPath(t
), "tool", "pprof", "-alloc_space", "-top"))
488 // Check that pprof works both with and without explicit executable on command line.
490 cmd
.Args
= append(cmd
.Args
, exe
, fn
)
492 cmd
.Args
= append(cmd
.Args
, fn
)
495 for i
, e
:= range cmd
.Env
{
496 if strings
.HasPrefix(e
, "PPROF_TMPDIR=") {
497 cmd
.Env
[i
] = "PPROF_TMPDIR=" + os
.TempDir()
503 cmd
.Env
= append(cmd
.Env
, "PPROF_TMPDIR="+os
.TempDir())
506 top
, err
:= cmd
.CombinedOutput()
507 t
.Logf("%s:\n%s", cmd
.Args
, top
)
510 } else if !bytes
.Contains(top
, []byte("MemProf")) {
511 t
.Error("missing MemProf in pprof output")
516 var concurrentMapTest
= flag
.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
518 func TestConcurrentMapWrites(t
*testing
.T
) {
519 if !*concurrentMapTest
{
520 t
.Skip("skipping without -run_concurrent_map_tests")
522 testenv
.MustHaveGoRun(t
)
523 output
:= runTestProg(t
, "testprog", "concurrentMapWrites")
524 want
:= "fatal error: concurrent map writes"
525 if !strings
.HasPrefix(output
, want
) {
526 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
529 func TestConcurrentMapReadWrite(t
*testing
.T
) {
530 if !*concurrentMapTest
{
531 t
.Skip("skipping without -run_concurrent_map_tests")
533 testenv
.MustHaveGoRun(t
)
534 output
:= runTestProg(t
, "testprog", "concurrentMapReadWrite")
535 want
:= "fatal error: concurrent map read and map write"
536 if !strings
.HasPrefix(output
, want
) {
537 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
540 func TestConcurrentMapIterateWrite(t
*testing
.T
) {
541 if !*concurrentMapTest
{
542 t
.Skip("skipping without -run_concurrent_map_tests")
544 testenv
.MustHaveGoRun(t
)
545 output
:= runTestProg(t
, "testprog", "concurrentMapIterateWrite")
546 want
:= "fatal error: concurrent map iteration and map write"
547 if !strings
.HasPrefix(output
, want
) {
548 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
556 func (p
*point
) negate() {
561 // Test for issue #10152.
562 func TestPanicInlined(t
*testing
.T
) {
566 t
.Fatalf("recover failed")
568 buf
:= make([]byte, 2048)
569 n
:= runtime
.Stack(buf
, false)
571 want
:= []byte("(*point).negate(")
572 if runtime
.Compiler
== "gccgo" {
573 want
= []byte("point.negate")
575 if !bytes
.Contains(buf
, want
) {
577 t
.Fatalf("expecting stack trace to contain call to %s", want
)
585 // Test for issues #3934 and #20018.
586 // We want to delay exiting until a panic print is complete.
587 func TestPanicRace(t
*testing
.T
) {
588 testenv
.MustHaveGoRun(t
)
590 exe
, err
:= buildTestProg(t
, "testprog")
595 // The test is intentionally racy, and in my testing does not
596 // produce the expected output about 0.05% of the time.
597 // So run the program in a loop and only fail the test if we
598 // get the wrong output ten times in a row.
601 for i
:= 0; i
< tries
; i
++ {
602 got
, err
:= testenv
.CleanCmdEnv(exec
.Command(exe
, "PanicRace")).CombinedOutput()
604 t
.Logf("try %d: program exited successfully, should have failed", i
+1)
609 t
.Logf("try %d:\n", i
+1)
618 if runtime
.Compiler
== "gccgo" {
619 // gccgo will dump a function name like main.$nested30.
620 // Match on the file name instead.
621 wants
[1] = "panicrace"
623 for _
, want
:= range wants
{
624 if !bytes
.Contains(got
, []byte(want
)) {
625 t
.Logf("did not find expected string %q", want
)
630 // Test generated expected output.
633 t
.Errorf("test ran %d times without producing expected output", tries
)
636 func TestBadTraceback(t
*testing
.T
) {
637 if runtime
.Compiler
== "gccgo" {
638 t
.Skip("gccgo does not do a hex dump")
640 output
:= runTestProg(t
, "testprog", "BadTraceback")
641 for _
, want
:= range []string{
642 "runtime: unexpected return pc",
644 "00000bad", // Smashed LR in hex dump
645 "<main.badLR", // Symbolization in hex dump (badLR1 or badLR2)
647 if !strings
.Contains(output
, want
) {
648 t
.Errorf("output does not contain %q:\n%s", want
, output
)