diff options
Diffstat (limited to 'src/pkg/runtime/crash_test.go')
-rw-r--r-- | src/pkg/runtime/crash_test.go | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/pkg/runtime/crash_test.go b/src/pkg/runtime/crash_test.go new file mode 100644 index 000000000..5f84cb5a2 --- /dev/null +++ b/src/pkg/runtime/crash_test.go @@ -0,0 +1,177 @@ +// Copyright 2012 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 runtime_test + +import ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "text/template" +) + +func executeTest(t *testing.T, templ string, data interface{}) string { + checkStaleRuntime(t) + + st := template.Must(template.New("crashSource").Parse(templ)) + + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + src := filepath.Join(dir, "main.go") + f, err := os.Create(src) + if err != nil { + t.Fatalf("failed to create %v: %v", src, err) + } + err = st.Execute(f, data) + if err != nil { + f.Close() + t.Fatalf("failed to execute template: %v", err) + } + f.Close() + + got, _ := exec.Command("go", "run", src).CombinedOutput() + return string(got) +} + +func checkStaleRuntime(t *testing.T) { + // 'go run' uses the installed copy of runtime.a, which may be out of date. + out, err := exec.Command("go", "list", "-f", "{{.Stale}}", "runtime").CombinedOutput() + if err != nil { + t.Fatalf("failed to execute 'go list': %v\n%v", err, string(out)) + } + if string(out) != "false\n" { + t.Fatalf("Stale runtime.a. Run 'go install runtime'.") + } +} + +func testCrashHandler(t *testing.T, cgo bool) { + type crashTest struct { + Cgo bool + } + got := executeTest(t, crashSource, &crashTest{Cgo: cgo}) + want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n" + if got != want { + t.Fatalf("expected %q, but got %q", want, got) + } +} + +func TestCrashHandler(t *testing.T) { + testCrashHandler(t, false) +} + +func testDeadlock(t *testing.T, source string) { + got := executeTest(t, source, nil) + want := "fatal error: all goroutines are asleep - deadlock!\n" + if !strings.HasPrefix(got, want) { + t.Fatalf("expected %q, but got %q", want, got) + } +} + +func TestSimpleDeadlock(t *testing.T) { + testDeadlock(t, simpleDeadlockSource) +} + +func TestInitDeadlock(t *testing.T) { + testDeadlock(t, initDeadlockSource) +} + +func TestLockedDeadlock(t *testing.T) { + testDeadlock(t, lockedDeadlockSource) +} + +func TestLockedDeadlock2(t *testing.T) { + testDeadlock(t, lockedDeadlockSource2) +} + +const crashSource = ` +package main + +import ( + "fmt" + "runtime" +) + +{{if .Cgo}} +import "C" +{{end}} + +func test(name string) { + defer func() { + if x := recover(); x != nil { + fmt.Printf(" recovered") + } + fmt.Printf(" done\n") + }() + fmt.Printf("%s:", name) + var s *string + _ = *s + fmt.Print("SHOULD NOT BE HERE") +} + +func testInNewThread(name string) { + c := make(chan bool) + go func() { + runtime.LockOSThread() + test(name) + c <- true + }() + <-c +} + +func main() { + runtime.LockOSThread() + test("main") + testInNewThread("new-thread") + testInNewThread("second-new-thread") + test("main-again") +} +` + +const simpleDeadlockSource = ` +package main +func main() { + select {} +} +` + +const initDeadlockSource = ` +package main +func init() { + select {} +} +func main() { +} +` + +const lockedDeadlockSource = ` +package main +import "runtime" +func main() { + runtime.LockOSThread() + select {} +} +` + +const lockedDeadlockSource2 = ` +package main +import ( + "runtime" + "time" +) +func main() { + go func() { + runtime.LockOSThread() + select {} + }() + time.Sleep(time.Millisecond) + select {} +} +` |