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