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