Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wiggle/tests/errors.rs
1692 views
1
/// Execute the wiggle guest conversion code to exercise it
2
mod convert_just_errno {
3
use anyhow::Result;
4
use wiggle::GuestMemory;
5
use wiggle_test::{HostMemory, WasiCtx, impl_errno};
6
7
/// The `errors` argument to the wiggle gives us a hook to map a rich error
8
/// type like this one (typical of wiggle use cases in wasi-common and beyond)
9
/// down to the flat error enums that witx can specify.
10
#[derive(Debug, thiserror::Error)]
11
pub enum RichError {
12
#[error("Invalid argument: {0}")]
13
InvalidArg(String),
14
#[error("Won't cross picket line: {0}")]
15
PicketLine(String),
16
}
17
18
// Define an errno with variants corresponding to RichError. Use it in a
19
// trivial function.
20
wiggle::from_witx!({
21
witx_literal: "
22
(typename $errno (enum (@witx tag u8) $ok $invalid_arg $picket_line))
23
(module $one_error_conversion
24
(@interface func (export \"foo\")
25
(param $strike u32)
26
(result $err (expected (error $errno)))))
27
",
28
errors: { errno => trappable ErrnoT },
29
});
30
31
impl_errno!(types::Errno);
32
33
impl From<RichError> for types::ErrnoT {
34
fn from(rich: RichError) -> types::ErrnoT {
35
match rich {
36
RichError::InvalidArg(s) => {
37
types::ErrnoT::from(types::Errno::InvalidArg).context(s)
38
}
39
RichError::PicketLine(s) => {
40
types::ErrnoT::from(types::Errno::PicketLine).context(s)
41
}
42
}
43
}
44
}
45
46
impl<'a> one_error_conversion::OneErrorConversion for WasiCtx<'a> {
47
fn foo(&mut self, _memory: &mut GuestMemory<'_>, strike: u32) -> Result<(), types::ErrnoT> {
48
// We use the argument to this function to exercise all of the
49
// possible error cases we could hit here
50
match strike {
51
0 => Ok(()),
52
1 => Err(RichError::PicketLine(format!("I'm not a scab")))?,
53
_ => Err(RichError::InvalidArg(format!("out-of-bounds: {strike}")))?,
54
}
55
}
56
}
57
58
#[test]
59
fn one_error_conversion_test() {
60
let mut ctx = WasiCtx::new();
61
let mut host_memory = HostMemory::new();
62
let mut memory = host_memory.guest_memory();
63
64
// Exercise each of the branches in `foo`.
65
// Start with the success case:
66
let r0 = one_error_conversion::foo(&mut ctx, &mut memory, 0).unwrap();
67
assert_eq!(
68
r0,
69
types::Errno::Ok as i32,
70
"Expected return value for strike=0"
71
);
72
assert!(ctx.log.borrow().is_empty(), "No error log for strike=0");
73
74
// First error case:
75
let r1 = one_error_conversion::foo(&mut ctx, &mut memory, 1).unwrap();
76
assert_eq!(
77
r1,
78
types::Errno::PicketLine as i32,
79
"Expected return value for strike=1"
80
);
81
82
// Second error case:
83
let r2 = one_error_conversion::foo(&mut ctx, &mut memory, 2).unwrap();
84
assert_eq!(
85
r2,
86
types::Errno::InvalidArg as i32,
87
"Expected return value for strike=2"
88
);
89
}
90
}
91
92
/// Type-check the wiggle guest conversion code against a more complex case where
93
/// we use two distinct error types.
94
mod convert_multiple_error_types {
95
pub use super::convert_just_errno::RichError;
96
use anyhow::Result;
97
use wiggle::GuestMemory;
98
use wiggle_test::{WasiCtx, impl_errno};
99
100
/// Test that we can map multiple types of errors.
101
#[derive(Debug, thiserror::Error)]
102
#[expect(dead_code, reason = "testing codegen below")]
103
pub enum AnotherRichError {
104
#[error("I've had this many cups of coffee and can't even think straight: {0}")]
105
TooMuchCoffee(usize),
106
}
107
108
// Just like the prior test, except that we have a second errno type. This should mean there
109
// are two functions in UserErrorConversion.
110
// Additionally, test that the function "baz" marked noreturn always returns a wasmtime::Trap.
111
wiggle::from_witx!({
112
witx_literal: "
113
(typename $errno (enum (@witx tag u8) $ok $invalid_arg $picket_line))
114
(typename $errno2 (enum (@witx tag u8) $ok $too_much_coffee))
115
(module $two_error_conversions
116
(@interface func (export \"foo\")
117
(param $strike u32)
118
(result $err (expected (error $errno))))
119
(@interface func (export \"bar\")
120
(param $drink u32)
121
(result $err (expected (error $errno2))))
122
(@interface func (export \"baz\")
123
(param $drink u32)
124
(@witx noreturn)))
125
",
126
errors: { errno => RichError, errno2 => AnotherRichError },
127
});
128
129
impl_errno!(types::Errno);
130
impl_errno!(types::Errno2);
131
132
// The UserErrorConversion trait will also have two methods for this test. They correspond to
133
// each member of the `errors` mapping.
134
// Bodies elided.
135
impl<'a> types::UserErrorConversion for WasiCtx<'a> {
136
fn errno_from_rich_error(&mut self, _e: RichError) -> Result<types::Errno> {
137
unimplemented!()
138
}
139
fn errno2_from_another_rich_error(
140
&mut self,
141
_e: AnotherRichError,
142
) -> Result<types::Errno2> {
143
unimplemented!()
144
}
145
}
146
147
// And here's the witx module trait impl, bodies elided
148
impl<'a> two_error_conversions::TwoErrorConversions for WasiCtx<'a> {
149
fn foo(&mut self, _: &mut GuestMemory<'_>, _: u32) -> Result<(), RichError> {
150
unimplemented!()
151
}
152
fn bar(&mut self, _: &mut GuestMemory<'_>, _: u32) -> Result<(), AnotherRichError> {
153
unimplemented!()
154
}
155
fn baz(&mut self, _: &mut GuestMemory<'_>, _: u32) -> anyhow::Error {
156
unimplemented!()
157
}
158
}
159
}
160
161