Source file
src/runtime/panic_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "runtime"
9 "slices"
10 "strings"
11 "testing"
12 "time"
13 )
14
15
16
17
18 func TestPanicWithDirectlyPrintableCustomTypes(t *testing.T) {
19 tests := []struct {
20 name string
21 wantPanicPrefix string
22 }{
23 {"panicCustomBool", `panic: main.MyBool(true)`},
24 {"panicCustomComplex128", `panic: main.MyComplex128(32.1+10i)`},
25 {"panicCustomComplex64", `panic: main.MyComplex64(0.11+3i)`},
26 {"panicCustomFloat32", `panic: main.MyFloat32(-93.7)`},
27 {"panicCustomFloat64", `panic: main.MyFloat64(-93.7)`},
28 {"panicCustomInt", `panic: main.MyInt(93)`},
29 {"panicCustomInt8", `panic: main.MyInt8(93)`},
30 {"panicCustomInt16", `panic: main.MyInt16(93)`},
31 {"panicCustomInt32", `panic: main.MyInt32(93)`},
32 {"panicCustomInt64", `panic: main.MyInt64(93)`},
33 {"panicCustomString", `panic: main.MyString("Panic` + "\n\t" + `line two")`},
34 {"panicCustomUint", `panic: main.MyUint(93)`},
35 {"panicCustomUint8", `panic: main.MyUint8(93)`},
36 {"panicCustomUint16", `panic: main.MyUint16(93)`},
37 {"panicCustomUint32", `panic: main.MyUint32(93)`},
38 {"panicCustomUint64", `panic: main.MyUint64(93)`},
39 {"panicCustomUintptr", `panic: main.MyUintptr(93)`},
40 {"panicDeferFatal", "panic: runtime.errorString(\"invalid memory address or nil pointer dereference\")\n\tfatal error: sync: unlock of unlocked mutex"},
41 {"panicDoublieDeferFatal", "panic: runtime.errorString(\"invalid memory address or nil pointer dereference\") [recovered, repanicked]\n\tfatal error: sync: unlock of unlocked mutex"},
42 }
43
44 for _, tt := range tests {
45 t := t
46 t.Run(tt.name, func(t *testing.T) {
47 output := runTestProg(t, "testprog", tt.name)
48 if !strings.HasPrefix(output, tt.wantPanicPrefix) {
49 t.Fatalf("%q\nis not present in\n%s", tt.wantPanicPrefix, output)
50 }
51 })
52 }
53 }
54
55 func TestPanicRecoverSpeed(t *testing.T) {
56
57 t.Skip("This test is too flaky at the moment. But it does normally pass. Suggestions for making it less flaky are welcome.")
58
59
60 var f func(int)
61 f = func(n int) {
62 if n == 0 {
63 panic("done")
64 }
65 defer func() {
66 err := recover()
67 panic(err)
68 }()
69 f(n - 1)
70 }
71
72 time := func(f func()) time.Duration {
73 var times []time.Duration
74 for range 10 {
75 start := time.Now()
76 f()
77 times = append(times, time.Since(start))
78 }
79 slices.Sort(times)
80 times = times[1 : len(times)-1]
81 var avg time.Duration
82 for _, v := range times {
83 avg += v / time.Duration(len(times))
84 }
85 return avg
86 }
87
88 a := time(func() {
89 defer func() { recover() }()
90 f(1024)
91 })
92 b := time(func() {
93 defer func() { recover() }()
94 f(2048)
95 })
96 m := b.Seconds() / a.Seconds()
97 t.Logf("a: %v, b: %v, m: %v", a, b, m)
98 if m > 3.5 {
99 t.Errorf("more than 2x time increase: %v", m)
100 }
101 }
102
103
104
105 func TestPanicNilErrorPrefix(t *testing.T) {
106 tests := []struct {
107 name string
108 wantPanic string
109 fn func()
110 }{
111 {
112 name: "panic(nil)",
113 wantPanic: "runtime error: panic with nil argument",
114 fn: func() {
115 panic(nil)
116 },
117 },
118 {
119 name: "panic((any)(nil))",
120 wantPanic: "runtime error: panic with nil argument",
121 fn: func() {
122 var foo any = nil
123 panic(foo)
124 },
125 },
126 {
127 name: "panic((error)(nil))",
128 wantPanic: "runtime error: panic with nil argument",
129 fn: func() {
130 var err error
131 panic(err)
132 },
133 },
134 }
135
136 for _, tt := range tests {
137 tt := tt
138 t.Run(tt.name, func(t *testing.T) {
139 defer func() {
140 r := recover()
141 if r == nil {
142 t.Fatal("expected a panic")
143 }
144
145 re, ok := r.(runtime.Error)
146 if !ok {
147 t.Fatalf("wrong panic type: got %T, want runtime.Error", r)
148 }
149 if !strings.Contains(re.Error(), "runtime error: panic called with nil argument") {
150 t.Fatalf("mismatched message, missing `runtime error: panic with nil`, : got:\n%s", re.Error())
151 }
152 }()
153
154 tt.fn()
155 })
156 }
157 }
158
View as plain text