1 package wasmdebug
2
3 import (
4 "errors"
5 "runtime"
6 "testing"
7
8 "github.com/tetratelabs/wazero/api"
9 "github.com/tetratelabs/wazero/internal/testing/require"
10 "github.com/tetratelabs/wazero/internal/wasmruntime"
11 )
12
13 func TestFuncName(t *testing.T) {
14 tests := []struct {
15 name, moduleName, funcName string
16 funcIdx uint32
17 expected string
18 }{
19 {name: "empty", expected: ".$0"},
20 {name: "empty module", funcName: "y", expected: ".y"},
21 {name: "empty function", moduleName: "x", funcIdx: 255, expected: "x.$255"},
22 {name: "looks like index in function", moduleName: "x", funcName: "[255]", expected: "x.[255]"},
23 {name: "no special characters", moduleName: "x", funcName: "y", expected: "x.y"},
24 {name: "dots in module", moduleName: "w.x", funcName: "y", expected: "w.x.y"},
25 {name: "dots in function", moduleName: "x", funcName: "y.z", expected: "x.y.z"},
26 {name: "spaces in module", moduleName: "w x", funcName: "y", expected: "w x.y"},
27 {name: "spaces in function", moduleName: "x", funcName: "y z", expected: "x.y z"},
28 }
29
30 for _, tt := range tests {
31 tc := tt
32 t.Run(tc.name, func(t *testing.T) {
33 funcName := FuncName(tc.moduleName, tc.funcName, tc.funcIdx)
34 require.Equal(t, tc.expected, funcName)
35 })
36 }
37 }
38
39 func TestAddSignature(t *testing.T) {
40 i32, i64, f32, f64 := api.ValueTypeI32, api.ValueTypeI64, api.ValueTypeF32, api.ValueTypeF64
41 tests := []struct {
42 name string
43 paramTypes, resultTypes []api.ValueType
44 expected string
45 }{
46 {name: "v_v", expected: "x.y()"},
47 {name: "i32_v", paramTypes: []api.ValueType{i32}, expected: "x.y(i32)"},
48 {name: "i32f64_v", paramTypes: []api.ValueType{i32, f64}, expected: "x.y(i32,f64)"},
49 {name: "f32i32f64_v", paramTypes: []api.ValueType{f32, i32, f64}, expected: "x.y(f32,i32,f64)"},
50 {name: "v_i64", resultTypes: []api.ValueType{i64}, expected: "x.y() i64"},
51 {name: "v_i64f32", resultTypes: []api.ValueType{i64, f32}, expected: "x.y() (i64,f32)"},
52 {name: "v_f32i32f64", resultTypes: []api.ValueType{f32, i32, f64}, expected: "x.y() (f32,i32,f64)"},
53 {name: "i32_i64", paramTypes: []api.ValueType{i32}, resultTypes: []api.ValueType{i64}, expected: "x.y(i32) i64"},
54 {name: "i64f32_i64f32", paramTypes: []api.ValueType{i64, f32}, resultTypes: []api.ValueType{i64, f32}, expected: "x.y(i64,f32) (i64,f32)"},
55 {name: "i64f32f64_f32i32f64", paramTypes: []api.ValueType{i64, f32, f64}, resultTypes: []api.ValueType{f32, i32, f64}, expected: "x.y(i64,f32,f64) (f32,i32,f64)"},
56 }
57
58 for _, tt := range tests {
59 tc := tt
60 t.Run(tc.name, func(t *testing.T) {
61 withSignature := signature("x.y", tc.paramTypes, tc.resultTypes)
62 require.Equal(t, tc.expected, withSignature)
63 })
64 }
65 }
66
67 var (
68 argErr = errors.New("invalid argument")
69 rteErr = testRuntimeErr("index out of bounds")
70 i32 = api.ValueTypeI32
71 i32i32i32i32 = []api.ValueType{i32, i32, i32, i32}
72 )
73
74 func TestErrorBuilder(t *testing.T) {
75 tests := []struct {
76 name string
77 build func(ErrorBuilder) error
78 expectedErr string
79 expectUnwrap error
80 }{
81 {
82 name: "one",
83 build: func(builder ErrorBuilder) error {
84 builder.AddFrame("x.y", nil, nil, nil)
85 return builder.FromRecovered(argErr)
86 },
87 expectedErr: `invalid argument (recovered by wazero)
88 wasm stack trace:
89 x.y()`,
90 expectUnwrap: argErr,
91 },
92 {
93 name: "two",
94 build: func(builder ErrorBuilder) error {
95 builder.AddFrame("wasi_snapshot_preview1.fd_write", i32i32i32i32, []api.ValueType{i32}, nil)
96 builder.AddFrame("x.y", nil, nil, nil)
97 return builder.FromRecovered(argErr)
98 },
99 expectedErr: `invalid argument (recovered by wazero)
100 wasm stack trace:
101 wasi_snapshot_preview1.fd_write(i32,i32,i32,i32) i32
102 x.y()`,
103 expectUnwrap: argErr,
104 },
105 {
106 name: "wasmruntime.Error",
107 build: func(builder ErrorBuilder) error {
108 builder.AddFrame("wasi_snapshot_preview1.fd_write", i32i32i32i32, []api.ValueType{i32},
109 []string{"/opt/homebrew/Cellar/tinygo/0.26.0/src/runtime/runtime_tinygowasm.go:73:6"})
110 builder.AddFrame("x.y", nil, nil, nil)
111 return builder.FromRecovered(wasmruntime.ErrRuntimeStackOverflow)
112 },
113 expectedErr: `wasm error: stack overflow
114 wasm stack trace:
115 wasi_snapshot_preview1.fd_write(i32,i32,i32,i32) i32
116 /opt/homebrew/Cellar/tinygo/0.26.0/src/runtime/runtime_tinygowasm.go:73:6
117 x.y()`,
118 expectUnwrap: wasmruntime.ErrRuntimeStackOverflow,
119 },
120 }
121
122 for _, tt := range tests {
123 tc := tt
124 t.Run(tc.name, func(t *testing.T) {
125 withStackTrace := tc.build(NewErrorBuilder())
126 require.Equal(t, tc.expectUnwrap, errors.Unwrap(withStackTrace))
127 require.EqualError(t, withStackTrace, tc.expectedErr)
128 })
129 }
130 }
131
132 func TestErrorBuilderGoRuntimeError(t *testing.T) {
133 builder := NewErrorBuilder()
134 builder.AddFrame("wasi_snapshot_preview1.fd_write", i32i32i32i32, []api.ValueType{i32}, nil)
135 builder.AddFrame("x.y", nil, nil, nil)
136 withStackTrace := builder.FromRecovered(rteErr)
137
138 require.Equal(t, rteErr, errors.Unwrap(withStackTrace))
139
140 errStr := withStackTrace.Error()
141 require.Contains(t, errStr, `index out of bounds (recovered by wazero)
142 wasm stack trace:
143 wasi_snapshot_preview1.fd_write(i32,i32,i32,i32) i32
144 x.y()`)
145 require.Contains(t, errStr, "Go runtime stack trace:")
146 require.Contains(t, errStr, "goroutine ")
147 require.Contains(t, errStr, "wazero/internal/wasmdebug/debug_test.go")
148 }
149
150
151 var _ runtime.Error = testRuntimeErr("")
152
153 type testRuntimeErr string
154
155 func (e testRuntimeErr) RuntimeError() {}
156
157 func (e testRuntimeErr) Error() string {
158 return string(e)
159 }
160
View as plain text