Skip to content

Commit 1e2d7eb

Browse files
committed
test: expand explicit panic unwind coverage
Exercise explicit return-based unwinding through nested and deferred panics, repanics, indirect calls, scalar and aggregate results, and runtime-generated faults. The fixture runs with the scheduler disabled on each WebAssembly target.
1 parent dc1d39e commit 1e2d7eb

2 files changed

Lines changed: 182 additions & 10 deletions

File tree

testdata/recover-explicit.go

Lines changed: 154 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,164 @@
11
package main
22

3+
type pair struct {
4+
first int
5+
second int
6+
}
7+
8+
var panicMap = map[any]int{}
9+
310
func main() {
4-
catch()
5-
println("done")
11+
println("# direct panic")
12+
direct()
13+
14+
println("\n# results")
15+
println("scalar result:", scalarResult())
16+
result := aggregateResult()
17+
println("aggregate result:", result.first, result.second)
18+
19+
println("\n# nested panics")
20+
nestedDefer()
21+
nestedPanic()
22+
panicReplace()
23+
deferPanic()
24+
repanic()
25+
26+
println("\n# runtime panics")
27+
mustRecover("index", func() {
28+
values := []int{1}
29+
println(values[2])
30+
})
31+
mustRecover("index from helper", func() {
32+
println(readOutOfBounds([]byte{1}))
33+
})
34+
mustRecover("slice", func() {
35+
values := []int{1}
36+
_ = values[:2]
37+
})
38+
mustRecover("type assertion", func() {
39+
var value any = "string"
40+
println(value.(int))
41+
})
42+
mustRecover("interface comparison", func() {
43+
var value any = []int{}
44+
println(value == value)
45+
})
46+
mustRecover("map assignment", func() {
47+
panicMap[[]int{}] = 1
48+
})
49+
mustRecover("map lookup", func() {
50+
_ = panicMap[[]int{}]
51+
})
52+
mustRecover("map delete", func() {
53+
delete(panicMap, []int{})
54+
})
55+
mustRecover("nil map", func() {
56+
var values map[string]int
57+
values["key"] = 1
58+
})
59+
mustRecover("divide by zero", func() {
60+
var divisor int
61+
println(1 / divisor)
62+
})
63+
mustRecover("nil pointer", func() {
64+
var pointer *int
65+
println(*pointer)
66+
})
67+
}
68+
69+
func direct() {
70+
defer func() {
71+
println("recovered direct:", recover() == "direct panic")
72+
}()
73+
panicHelper("direct panic")
74+
println("unreachable after direct panic")
75+
}
76+
77+
//go:noinline
78+
func panicHelper(value any) {
79+
panic(value)
80+
}
81+
82+
func scalarResult() (result int) {
83+
defer func() {
84+
recover()
85+
}()
86+
result = 3
87+
panicHelper("scalar result panic")
88+
return
89+
}
90+
91+
func aggregateResult() (result pair) {
92+
defer func() {
93+
recover()
94+
}()
95+
result = pair{1, 2}
96+
panicHelper("aggregate result panic")
97+
return
98+
}
99+
100+
func nestedDefer() {
101+
defer func() {
102+
println("recovered nested:", recover() == "nested panic")
103+
}()
104+
func() {
105+
defer println("nested defer ran")
106+
panicHelper("nested panic")
107+
}()
108+
}
109+
110+
func nestedPanic() {
111+
defer func() {
112+
println("recovered outer:", recover() == "outer panic")
113+
}()
114+
defer func() {
115+
println("recovered inner:", recover() == "inner panic")
116+
panicHelper("outer panic")
117+
}()
118+
panicHelper("inner panic")
119+
}
120+
121+
func panicReplace() {
122+
defer func() {
123+
println("recovered replacement:", recover() == "replacement panic")
124+
}()
125+
defer func() {
126+
panicHelper("replacement panic")
127+
}()
128+
panicHelper("original panic")
129+
}
130+
131+
func deferPanic() {
132+
defer func() {
133+
println("recovered deferred:", recover() == "deferred panic")
134+
}()
135+
defer panicHelper("deferred panic")
136+
}
137+
138+
func repanic() {
139+
defer func() {
140+
println("recovered repanic:", recover() == "repanic")
141+
}()
142+
defer func() {
143+
value := recover()
144+
panicHelper(value)
145+
}()
146+
panicHelper("repanic")
6147
}
7148

8-
func catch() {
149+
func mustRecover(name string, fn func()) {
9150
defer func() {
10-
println("recovered:", recover() == "panic")
151+
if recover() == nil {
152+
println("failed to recover:", name)
153+
} else {
154+
println("recovered:", name)
155+
}
11156
}()
12-
call()
13-
println("unreachable after call")
157+
fn()
158+
println("unreachable after:", name)
14159
}
15160

16-
func call() {
17-
panic("panic")
161+
//go:noinline
162+
func readOutOfBounds(values []byte) byte {
163+
return values[2]
18164
}

testdata/recover-explicit.txt

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,28 @@
1-
recovered: true
2-
done
1+
# direct panic
2+
recovered direct: true
3+
4+
# results
5+
scalar result: 3
6+
aggregate result: 1 2
7+
8+
# nested panics
9+
nested defer ran
10+
recovered nested: true
11+
recovered inner: true
12+
recovered outer: true
13+
recovered replacement: true
14+
recovered deferred: true
15+
recovered repanic: true
16+
17+
# runtime panics
18+
recovered: index
19+
recovered: index from helper
20+
recovered: slice
21+
recovered: type assertion
22+
recovered: interface comparison
23+
recovered: map assignment
24+
recovered: map lookup
25+
recovered: map delete
26+
recovered: nil map
27+
recovered: divide by zero
28+
recovered: nil pointer

0 commit comments

Comments
 (0)