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