| // Copyright 2020 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 ( |
| "slices" |
| "strings" |
| "testing" |
| "time" |
| ) |
| |
| // Test that panics print out the underlying value |
| // when the underlying kind is directly printable. |
| // Issue: https://golang.org/issues/37531 |
| func TestPanicWithDirectlyPrintableCustomTypes(t *testing.T) { |
| tests := []struct { |
| name string |
| wantPanicPrefix string |
| }{ |
| {"panicCustomBool", `panic: main.MyBool(true)`}, |
| {"panicCustomComplex128", `panic: main.MyComplex128(32.1+10i)`}, |
| {"panicCustomComplex64", `panic: main.MyComplex64(0.11+3i)`}, |
| {"panicCustomFloat32", `panic: main.MyFloat32(-93.7)`}, |
| {"panicCustomFloat64", `panic: main.MyFloat64(-93.7)`}, |
| {"panicCustomInt", `panic: main.MyInt(93)`}, |
| {"panicCustomInt8", `panic: main.MyInt8(93)`}, |
| {"panicCustomInt16", `panic: main.MyInt16(93)`}, |
| {"panicCustomInt32", `panic: main.MyInt32(93)`}, |
| {"panicCustomInt64", `panic: main.MyInt64(93)`}, |
| {"panicCustomString", `panic: main.MyString("Panic` + "\n\t" + `line two")`}, |
| {"panicCustomUint", `panic: main.MyUint(93)`}, |
| {"panicCustomUint8", `panic: main.MyUint8(93)`}, |
| {"panicCustomUint16", `panic: main.MyUint16(93)`}, |
| {"panicCustomUint32", `panic: main.MyUint32(93)`}, |
| {"panicCustomUint64", `panic: main.MyUint64(93)`}, |
| {"panicCustomUintptr", `panic: main.MyUintptr(93)`}, |
| {"panicDeferFatal", "panic: runtime.errorString(\"invalid memory address or nil pointer dereference\")\n\tfatal error: sync: unlock of unlocked mutex"}, |
| {"panicDoublieDeferFatal", "panic: runtime.errorString(\"invalid memory address or nil pointer dereference\") [recovered, repanicked]\n\tfatal error: sync: unlock of unlocked mutex"}, |
| } |
| |
| for _, tt := range tests { |
| t := t |
| t.Run(tt.name, func(t *testing.T) { |
| output := runTestProg(t, "testprog", tt.name) |
| if !strings.HasPrefix(output, tt.wantPanicPrefix) { |
| t.Fatalf("%q\nis not present in\n%s", tt.wantPanicPrefix, output) |
| } |
| }) |
| } |
| } |
| |
| func TestPanicRecoverSpeed(t *testing.T) { |
| // For issue 77062. |
| t.Skip("This test is too flaky at the moment. But it does normally pass. Suggestions for making it less flaky are welcome.") |
| |
| // Recursive function that does defer/recover/repanic. |
| var f func(int) |
| f = func(n int) { |
| if n == 0 { |
| panic("done") |
| } |
| defer func() { |
| err := recover() |
| panic(err) |
| }() |
| f(n - 1) |
| } |
| |
| time := func(f func()) time.Duration { |
| var times []time.Duration |
| for range 10 { |
| start := time.Now() |
| f() |
| times = append(times, time.Since(start)) |
| } |
| slices.Sort(times) |
| times = times[1 : len(times)-1] // skip high and low, to reduce noise |
| var avg time.Duration |
| for _, v := range times { |
| avg += v / time.Duration(len(times)) |
| } |
| return avg |
| } |
| |
| a := time(func() { |
| defer func() { recover() }() |
| f(1024) |
| }) |
| b := time(func() { |
| defer func() { recover() }() |
| f(2048) |
| }) |
| m := b.Seconds() / a.Seconds() |
| t.Logf("a: %v, b: %v, m: %v", a, b, m) |
| if m > 3.5 { |
| t.Errorf("more than 2x time increase: %v", m) |
| } |
| } |