1 use proptest::prelude::*;
2 use wiggle::{GuestMemory, GuestPtr};
3 use wiggle_test::{HostMemory, MemArea, WasiCtx, impl_errno};
4 
5 wiggle::from_witx!({
6     witx: ["tests/flags.witx"],
7 });
8 
9 impl_errno!(types::Errno);
10 
11 impl<'a> flags::Flags for WasiCtx<'a> {
configure_car( &mut self, memory: &mut GuestMemory<'_>, old_config: types::CarConfig, other_config_ptr: GuestPtr<types::CarConfig>, ) -> Result<types::CarConfig, types::Errno>12     fn configure_car(
13         &mut self,
14         memory: &mut GuestMemory<'_>,
15         old_config: types::CarConfig,
16         other_config_ptr: GuestPtr<types::CarConfig>,
17     ) -> Result<types::CarConfig, types::Errno> {
18         let other_config = memory.read(other_config_ptr).map_err(|e| {
19             eprintln!("old_config_ptr error: {e}");
20             types::Errno::InvalidArg
21         })?;
22         Ok(old_config ^ other_config)
23     }
24 }
25 
car_config_strat() -> impl Strategy<Value = types::CarConfig>26 fn car_config_strat() -> impl Strategy<Value = types::CarConfig> {
27     (1u8..=types::CarConfig::all().into())
28         .prop_map(|v| {
29             types::CarConfig::try_from(v).expect("invalid value for types::CarConfig flag")
30         })
31         .boxed()
32 }
33 
34 #[derive(Debug)]
35 struct ConfigureCarExercise {
36     old_config: types::CarConfig,
37     other_config: types::CarConfig,
38     other_config_by_ptr: MemArea,
39     return_ptr_loc: MemArea,
40 }
41 
42 impl ConfigureCarExercise {
strat() -> BoxedStrategy<Self>43     pub fn strat() -> BoxedStrategy<Self> {
44         (
45             car_config_strat(),
46             car_config_strat(),
47             HostMemory::mem_area_strat(4),
48             HostMemory::mem_area_strat(4),
49         )
50             .prop_map(
51                 |(old_config, other_config, other_config_by_ptr, return_ptr_loc)| Self {
52                     old_config,
53                     other_config,
54                     other_config_by_ptr,
55                     return_ptr_loc,
56                 },
57             )
58             .prop_filter("non-overlapping ptrs", |e| {
59                 MemArea::non_overlapping_set(&[e.other_config_by_ptr, e.return_ptr_loc])
60             })
61             .boxed()
62     }
63 
test(&self)64     pub fn test(&self) {
65         let mut ctx = WasiCtx::new();
66         let mut host_memory = HostMemory::new();
67         let mut memory = host_memory.guest_memory();
68 
69         // Populate input ptr
70         memory
71             .write(
72                 GuestPtr::new(self.other_config_by_ptr.ptr),
73                 self.other_config,
74             )
75             .expect("deref ptr mut to CarConfig");
76 
77         let res = flags::configure_car(
78             &mut ctx,
79             &mut memory,
80             self.old_config.bits() as i32,
81             self.other_config_by_ptr.ptr as i32,
82             self.return_ptr_loc.ptr as i32,
83         )
84         .unwrap();
85         assert_eq!(res, types::Errno::Ok as i32, "configure car errno");
86 
87         let res_config = memory
88             .read(GuestPtr::<types::CarConfig>::new(self.return_ptr_loc.ptr))
89             .expect("deref to CarConfig value");
90 
91         assert_eq!(
92             self.old_config ^ self.other_config,
93             res_config,
94             "returned CarConfig should be an XOR of inputs"
95         );
96     }
97 }
98 proptest! {
99     #[test]
100     fn configure_car(e in ConfigureCarExercise::strat()) {
101         e.test()
102     }
103 }
104 
105 #[test]
flags_fmt()106 fn flags_fmt() {
107     let empty = format!("{}", types::CarConfig::empty());
108     assert_eq!(empty, "CarConfig(CarConfig(0x0) (0x0))");
109     let one_flag = format!("{}", types::CarConfig::AWD);
110     assert_eq!(one_flag, "CarConfig(CarConfig(AWD) (0x2))");
111     let two_flags = format!("{}", types::CarConfig::AUTOMATIC | types::CarConfig::SUV);
112     assert_eq!(two_flags, "CarConfig(CarConfig(AUTOMATIC | SUV) (0x5))");
113     let all = format!("{}", types::CarConfig::all());
114     assert_eq!(all, "CarConfig(CarConfig(AUTOMATIC | AWD | SUV) (0x7))");
115 }
116