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
{
35 func testEnv(cmd
*exec
.Cmd
) *exec
.Cmd
{
37 panic("environment already set")
39 for _
, env
:= range os
.Environ() {
40 // Exclude GODEBUG from the environment to prevent its output
41 // from breaking tests that are trying to parse other command output.
42 if strings
.HasPrefix(env
, "GODEBUG=") {
45 // Exclude GOTRACEBACK for the same reason.
46 if strings
.HasPrefix(env
, "GOTRACEBACK=") {
49 cmd
.Env
= append(cmd
.Env
, env
)
57 target
map[string]buildexe
60 type buildexe
struct {
65 func runTestProg(t
*testing
.T
, binary
, name
string) string {
66 testenv
.MustHaveGoBuild(t
)
68 exe
, err
:= buildTestProg(t
, binary
)
73 cmd
:= testEnv(exec
.Command(exe
, name
))
77 if err
:= cmd
.Start(); err
!= nil {
78 t
.Fatalf("starting %s %s: %v", binary
, name
, err
)
81 // If the process doesn't complete within 1 minute,
82 // assume it is hanging and kill it to get a stack trace.
84 done
:= make(chan bool)
87 // This GOARCH/GOOS test is copied from cmd/dist/test.go.
88 // TODO(iant): Have cmd/dist update the environment variable.
89 if runtime
.GOARCH
== "arm" || runtime
.GOOS
== "windows" {
92 if s
:= os
.Getenv("GO_TEST_TIMEOUT_SCALE"); s
!= "" {
93 if sc
, err
:= strconv
.Atoi(s
); err
== nil {
100 case <-time
.After(time
.Duration(scale
) * time
.Minute
):
105 if err
:= cmd
.Wait(); err
!= nil {
106 t
.Logf("%s %s exit status: %v", binary
, name
, err
)
113 func buildTestProg(t
*testing
.T
, binary
string, flags
...string) (string, error
) {
117 defer testprog
.Unlock()
118 if testprog
.dir
== "" {
119 dir
, err
:= ioutil
.TempDir("", "go-build")
121 t
.Fatalf("failed to create temp directory: %v", err
)
124 toRemove
= append(toRemove
, dir
)
127 if testprog
.target
== nil {
128 testprog
.target
= make(map[string]buildexe
)
132 name
+= "_" + strings
.Join(flags
, "_")
134 target
, ok
:= testprog
.target
[name
]
136 return target
.exe
, target
.err
139 exe
:= filepath
.Join(testprog
.dir
, name
+".exe")
140 cmd
:= exec
.Command(testenv
.GoToolPath(t
), append([]string{"build", "-o", exe
}, flags
...)...)
141 cmd
.Dir
= "testdata/" + binary
142 out
, err
:= testEnv(cmd
).CombinedOutput()
144 target
.err
= fmt
.Errorf("building %s %v: %v\n%s", binary
, flags
, err
, out
)
145 testprog
.target
[name
] = target
146 return "", target
.err
149 testprog
.target
[name
] = target
154 staleRuntimeOnce sync
.Once
// guards init of staleRuntimeErr
155 staleRuntimeErr error
158 func checkStaleRuntime(t
*testing
.T
) {
159 staleRuntimeOnce
.Do(func() {
160 // 'go run' uses the installed copy of runtime.a, which may be out of date.
161 out
, err
:= testEnv(exec
.Command(testenv
.GoToolPath(t
), "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
163 staleRuntimeErr
= fmt
.Errorf("failed to execute 'go list': %v\n%v", err
, string(out
))
166 if string(out
) != "false\n" {
167 staleRuntimeErr
= fmt
.Errorf("Stale runtime.a. Run 'go install runtime'.")
170 if staleRuntimeErr
!= nil {
171 t
.Fatal(staleRuntimeErr
)
175 func testCrashHandler(t
*testing
.T
, cgo
bool) {
176 type crashTest
struct {
181 output
= runTestProg(t
, "testprogcgo", "Crash")
183 output
= runTestProg(t
, "testprog", "Crash")
185 want
:= "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
187 t
.Fatalf("output:\n%s\n\nwanted:\n%s", output
, want
)
191 func TestCrashHandler(t
*testing
.T
) {
192 testCrashHandler(t
, false)
195 func testDeadlock(t
*testing
.T
, name
string) {
196 output
:= runTestProg(t
, "testprog", name
)
197 want
:= "fatal error: all goroutines are asleep - deadlock!\n"
198 if !strings
.HasPrefix(output
, want
) {
199 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
203 func TestSimpleDeadlock(t
*testing
.T
) {
204 testDeadlock(t
, "SimpleDeadlock")
207 func TestInitDeadlock(t
*testing
.T
) {
208 testDeadlock(t
, "InitDeadlock")
211 func TestLockedDeadlock(t
*testing
.T
) {
212 testDeadlock(t
, "LockedDeadlock")
215 func TestLockedDeadlock2(t
*testing
.T
) {
216 testDeadlock(t
, "LockedDeadlock2")
219 func TestGoexitDeadlock(t
*testing
.T
) {
220 output
:= runTestProg(t
, "testprog", "GoexitDeadlock")
221 want
:= "no goroutines (main called runtime.Goexit) - deadlock!"
222 if !strings
.Contains(output
, want
) {
223 t
.Fatalf("output:\n%s\n\nwant output containing: %s", output
, want
)
227 func TestStackOverflow(t
*testing
.T
) {
228 output
:= runTestProg(t
, "testprog", "StackOverflow")
229 want
:= "runtime: goroutine stack exceeds 1474560-byte limit\nfatal error: stack overflow"
230 if !strings
.HasPrefix(output
, want
) {
231 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
235 func TestThreadExhaustion(t
*testing
.T
) {
236 output
:= runTestProg(t
, "testprog", "ThreadExhaustion")
237 want
:= "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
238 if !strings
.HasPrefix(output
, want
) {
239 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
243 func TestRecursivePanic(t
*testing
.T
) {
244 output
:= runTestProg(t
, "testprog", "RecursivePanic")
249 if !strings
.HasPrefix(output
, want
) {
250 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
255 func TestGoexitCrash(t
*testing
.T
) {
256 output
:= runTestProg(t
, "testprog", "GoexitExit")
257 want
:= "no goroutines (main called runtime.Goexit) - deadlock!"
258 if !strings
.Contains(output
, want
) {
259 t
.Fatalf("output:\n%s\n\nwant output containing: %s", output
, want
)
263 func TestGoexitDefer(t
*testing
.T
) {
264 c
:= make(chan struct{})
269 t
.Errorf("non-nil recover during Goexit")
275 // Note: if the defer fails to run, we will get a deadlock here
279 func TestGoNil(t
*testing
.T
) {
280 output
:= runTestProg(t
, "testprog", "GoNil")
281 want
:= "go of nil func value"
282 if !strings
.Contains(output
, want
) {
283 t
.Fatalf("output:\n%s\n\nwant output containing: %s", output
, want
)
287 func TestMainGoroutineID(t
*testing
.T
) {
288 output
:= runTestProg(t
, "testprog", "MainGoroutineID")
289 want
:= "panic: test\n\ngoroutine 1 [running]:\n"
290 if !strings
.HasPrefix(output
, want
) {
291 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
295 func TestNoHelperGoroutines(t
*testing
.T
) {
296 output
:= runTestProg(t
, "testprog", "NoHelperGoroutines")
297 matches
:= regexp
.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output
, -1)
298 if len(matches
) != 1 || matches
[0][0] != "goroutine 1 [" {
299 t
.Fatalf("want to see only goroutine 1, see:\n%s", output
)
303 func TestBreakpoint(t
*testing
.T
) {
304 output
:= runTestProg(t
, "testprog", "Breakpoint")
305 want
:= "runtime.Breakpoint()"
306 if !strings
.Contains(output
, want
) {
307 t
.Fatalf("output:\n%s\n\nwant output containing: %s", output
, want
)
311 func TestGoexitInPanic(t
*testing
.T
) {
312 // see issue 8774: this code used to trigger an infinite recursion
313 output
:= runTestProg(t
, "testprog", "GoexitInPanic")
314 want
:= "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
315 if !strings
.HasPrefix(output
, want
) {
316 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
320 // Issue 14965: Runtime panics should be of type runtime.Error
321 func TestRuntimePanicWithRuntimeError(t
*testing
.T
) {
322 testCases
:= [...]func(){
324 var m
map[uint64]bool
328 ch
:= make(chan struct{})
333 var ch
= make(chan struct{})
338 var s
= make([]int, 2)
343 _
= make(chan bool, n
)
346 close((chan bool)(nil))
350 for i
, fn
:= range testCases
{
351 got
:= panicValue(fn
)
352 if _
, ok
:= got
.(runtime
.Error
); !ok
{
353 t
.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i
, got
, got
)
358 func panicValue(fn
func()) (recovered
interface{}) {
360 recovered
= recover()
366 func TestPanicAfterGoexit(t
*testing
.T
) {
367 // an uncaught panic should still work after goexit
368 output
:= runTestProg(t
, "testprog", "PanicAfterGoexit")
369 want
:= "panic: hello"
370 if !strings
.HasPrefix(output
, want
) {
371 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
375 func TestRecoveredPanicAfterGoexit(t
*testing
.T
) {
376 output
:= runTestProg(t
, "testprog", "RecoveredPanicAfterGoexit")
377 want
:= "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
378 if !strings
.HasPrefix(output
, want
) {
379 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
383 func TestRecoverBeforePanicAfterGoexit(t
*testing
.T
) {
384 // 1. defer a function that recovers
385 // 2. defer a function that panics
387 // Goexit should run the #2 defer. Its panic
388 // should be caught by the #1 defer, and execution
389 // should resume in the caller. Like the Goexit
403 func TestNetpollDeadlock(t
*testing
.T
) {
405 output
:= runTestProg(t
, "testprognet", "NetpollDeadlock")
407 if !strings
.HasSuffix(output
, want
) {
408 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
412 func TestPanicTraceback(t
*testing
.T
) {
414 output
:= runTestProg(t
, "testprog", "PanicTraceback")
415 want
:= "panic: hello"
416 if !strings
.HasPrefix(output
, want
) {
417 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
420 // Check functions in the traceback.
421 fns
:= []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
422 for _
, fn
:= range fns
{
423 re
:= regexp
.MustCompile(`(?m)^` + regexp
.QuoteMeta(fn
) + `\(.*\n`)
424 idx
:= re
.FindStringIndex(output
)
426 t
.Fatalf("expected %q function in traceback:\n%s", fn
, output
)
428 output
= output
[idx
[1]:]
432 func testPanicDeadlock(t
*testing
.T
, name
string, want
string) {
434 output
:= runTestProg(t
, "testprog", name
)
435 if !strings
.HasPrefix(output
, want
) {
436 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
440 func TestPanicDeadlockGosched(t
*testing
.T
) {
441 testPanicDeadlock(t
, "GoschedInPanic", "panic: errorThatGosched\n\n")
444 func TestPanicDeadlockSyscall(t
*testing
.T
) {
445 testPanicDeadlock(t
, "SyscallInPanic", "1\n2\npanic: 3\n\n")
448 func TestPanicLoop(t
*testing
.T
) {
449 output
:= runTestProg(t
, "testprog", "PanicLoop")
450 if want
:= "panic while printing panic value"; !strings
.Contains(output
, want
) {
451 t
.Errorf("output does not contain %q:\n%s", want
, output
)
455 func TestMemPprof(t
*testing
.T
) {
456 testenv
.MustHaveGoRun(t
)
458 exe
, err
:= buildTestProg(t
, "testprog")
463 got
, err
:= testEnv(exec
.Command(exe
, "MemProf")).CombinedOutput()
467 fn
:= strings
.TrimSpace(string(got
))
470 cmd
:= testEnv(exec
.Command(testenv
.GoToolPath(t
), "tool", "pprof", "-alloc_space", "-top", exe
, fn
))
473 for i
, e
:= range cmd
.Env
{
474 if strings
.HasPrefix(e
, "PPROF_TMPDIR=") {
475 cmd
.Env
[i
] = "PPROF_TMPDIR=" + os
.TempDir()
481 cmd
.Env
= append(cmd
.Env
, "PPROF_TMPDIR="+os
.TempDir())
484 top
, err
:= cmd
.CombinedOutput()
490 if !bytes
.Contains(top
, []byte("MemProf")) {
491 t
.Error("missing MemProf in pprof output")
495 var concurrentMapTest
= flag
.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
497 func TestConcurrentMapWrites(t
*testing
.T
) {
498 if !*concurrentMapTest
{
499 t
.Skip("skipping without -run_concurrent_map_tests")
501 testenv
.MustHaveGoRun(t
)
502 output
:= runTestProg(t
, "testprog", "concurrentMapWrites")
503 want
:= "fatal error: concurrent map writes"
504 if !strings
.HasPrefix(output
, want
) {
505 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
508 func TestConcurrentMapReadWrite(t
*testing
.T
) {
509 if !*concurrentMapTest
{
510 t
.Skip("skipping without -run_concurrent_map_tests")
512 testenv
.MustHaveGoRun(t
)
513 output
:= runTestProg(t
, "testprog", "concurrentMapReadWrite")
514 want
:= "fatal error: concurrent map read and map write"
515 if !strings
.HasPrefix(output
, want
) {
516 t
.Fatalf("output does not start with %q:\n%s", want
, output
)
519 func TestConcurrentMapIterateWrite(t
*testing
.T
) {
520 if !*concurrentMapTest
{
521 t
.Skip("skipping without -run_concurrent_map_tests")
523 testenv
.MustHaveGoRun(t
)
524 output
:= runTestProg(t
, "testprog", "concurrentMapIterateWrite")
525 want
:= "fatal error: concurrent map iteration and map write"
526 if !strings
.HasPrefix(output
, want
) {
527 t
.Fatalf("output does not start with %q:\n%s", want
, output
)