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