1 use proptest::prelude::*;
2 use wiggle::{GuestMemory, GuestPtr};
3 use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx};
4 
5 wiggle::from_witx!({
6     witx: ["tests/arrays.witx"],
7     ctx: WasiCtx,
8 });
9 
10 impl_errno!(types::Errno, types::GuestErrorConversion);
11 
12 impl<'a> arrays::Arrays for WasiCtx<'a> {
reduce_excuses( &self, excuses: &types::ConstExcuseArray, ) -> Result<types::Excuse, types::Errno>13     fn reduce_excuses(
14         &self,
15         excuses: &types::ConstExcuseArray,
16     ) -> Result<types::Excuse, types::Errno> {
17         let last = &excuses
18             .iter()
19             .last()
20             .expect("input array is non-empty")
21             .expect("valid ptr to ptr")
22             .read()
23             .expect("valid ptr to some Excuse value");
24         Ok(last.read().expect("dereferencing ptr should succeed"))
25     }
26 
populate_excuses(&self, excuses: &types::ExcuseArray) -> Result<(), types::Errno>27     fn populate_excuses(&self, excuses: &types::ExcuseArray) -> Result<(), types::Errno> {
28         for excuse in excuses.iter() {
29             let ptr_to_excuse = excuse
30                 .expect("valid ptr to ptr")
31                 .read()
32                 .expect("valid ptr to some Excuse value");
33             ptr_to_excuse
34                 .write(types::Excuse::Sleeping)
35                 .expect("dereferencing mut ptr should succeed");
36         }
37         Ok(())
38     }
39 }
40 
41 #[derive(Debug)]
42 struct ReduceExcusesExcercise {
43     excuse_values: Vec<types::Excuse>,
44     excuse_ptr_locs: Vec<MemArea>,
45     array_ptr_loc: MemArea,
46     return_ptr_loc: MemArea,
47 }
48 
49 impl ReduceExcusesExcercise {
strat() -> BoxedStrategy<Self>50     pub fn strat() -> BoxedStrategy<Self> {
51         (1..256u32)
52             .prop_flat_map(|len| {
53                 let len_usize = len as usize;
54                 (
55                     proptest::collection::vec(excuse_strat(), len_usize..=len_usize),
56                     proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize),
57                     HostMemory::mem_area_strat(4 * len),
58                     HostMemory::mem_area_strat(4),
59                 )
60             })
61             .prop_map(
62                 |(excuse_values, excuse_ptr_locs, array_ptr_loc, return_ptr_loc)| Self {
63                     excuse_values,
64                     excuse_ptr_locs,
65                     array_ptr_loc,
66                     return_ptr_loc,
67                 },
68             )
69             .prop_filter("non-overlapping pointers", |e| {
70                 let mut all = vec![e.array_ptr_loc, e.return_ptr_loc];
71                 all.extend(e.excuse_ptr_locs.iter());
72                 MemArea::non_overlapping_set(all)
73             })
74             .boxed()
75     }
76 
test(&self)77     pub fn test(&self) {
78         let ctx = WasiCtx::new();
79         let host_memory = HostMemory::new();
80 
81         // Populate memory with pointers to generated Excuse values
82         for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) {
83             host_memory
84                 .ptr(ptr.ptr)
85                 .write(excuse)
86                 .expect("deref ptr mut to Excuse value");
87         }
88 
89         // Populate the array with pointers to generated Excuse values
90         {
91             let array: GuestPtr<'_, [GuestPtr<types::Excuse>]> =
92                 host_memory.ptr((self.array_ptr_loc.ptr, self.excuse_ptr_locs.len() as u32));
93             for (slot, ptr) in array.iter().zip(&self.excuse_ptr_locs) {
94                 let slot = slot.expect("array should be in bounds");
95                 slot.write(host_memory.ptr(ptr.ptr))
96                     .expect("should succeed in writing array");
97             }
98         }
99 
100         let res = arrays::reduce_excuses(
101             &ctx,
102             &host_memory,
103             self.array_ptr_loc.ptr as i32,
104             self.excuse_ptr_locs.len() as i32,
105             self.return_ptr_loc.ptr as i32,
106         );
107 
108         assert_eq!(res, types::Errno::Ok.into(), "reduce excuses errno");
109 
110         let expected = *self
111             .excuse_values
112             .last()
113             .expect("generated vec of excuses should be non-empty");
114         let given: types::Excuse = host_memory
115             .ptr(self.return_ptr_loc.ptr)
116             .read()
117             .expect("deref ptr to returned value");
118         assert_eq!(expected, given, "reduce excuses return val");
119     }
120 }
121 proptest! {
122     #[test]
123     fn reduce_excuses(e in ReduceExcusesExcercise::strat()) {
124         e.test()
125     }
126 }
127 
excuse_strat() -> impl Strategy<Value = types::Excuse>128 fn excuse_strat() -> impl Strategy<Value = types::Excuse> {
129     prop_oneof![
130         Just(types::Excuse::DogAte),
131         Just(types::Excuse::Traffic),
132         Just(types::Excuse::Sleeping),
133     ]
134     .boxed()
135 }
136 
137 #[derive(Debug)]
138 struct PopulateExcusesExcercise {
139     array_ptr_loc: MemArea,
140     elements: Vec<MemArea>,
141 }
142 
143 impl PopulateExcusesExcercise {
strat() -> BoxedStrategy<Self>144     pub fn strat() -> BoxedStrategy<Self> {
145         (1..256u32)
146             .prop_flat_map(|len| {
147                 let len_usize = len as usize;
148                 (
149                     HostMemory::mem_area_strat(4 * len),
150                     proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize),
151                 )
152             })
153             .prop_map(|(array_ptr_loc, elements)| Self {
154                 array_ptr_loc,
155                 elements,
156             })
157             .prop_filter("non-overlapping pointers", |e| {
158                 let mut all = vec![e.array_ptr_loc];
159                 all.extend(e.elements.iter());
160                 MemArea::non_overlapping_set(all)
161             })
162             .boxed()
163     }
164 
test(&self)165     pub fn test(&self) {
166         let ctx = WasiCtx::new();
167         let host_memory = HostMemory::new();
168 
169         // Populate array with valid pointers to Excuse type in memory
170         let ptr = host_memory.ptr::<[GuestPtr<'_, types::Excuse>]>((
171             self.array_ptr_loc.ptr,
172             self.elements.len() as u32,
173         ));
174         for (ptr, val) in ptr.iter().zip(&self.elements) {
175             ptr.expect("should be valid pointer")
176                 .write(host_memory.ptr(val.ptr))
177                 .expect("failed to write value");
178         }
179 
180         let res = arrays::populate_excuses(
181             &ctx,
182             &host_memory,
183             self.array_ptr_loc.ptr as i32,
184             self.elements.len() as i32,
185         );
186         assert_eq!(res, types::Errno::Ok.into(), "populate excuses errno");
187 
188         let arr: GuestPtr<'_, [GuestPtr<'_, types::Excuse>]> =
189             host_memory.ptr((self.array_ptr_loc.ptr, self.elements.len() as u32));
190         for el in arr.iter() {
191             let ptr_to_ptr = el
192                 .expect("valid ptr to ptr")
193                 .read()
194                 .expect("valid ptr to some Excuse value");
195             assert_eq!(
196                 ptr_to_ptr
197                     .read()
198                     .expect("dereferencing ptr to some Excuse value"),
199                 types::Excuse::Sleeping,
200                 "element should equal Excuse::Sleeping"
201             );
202         }
203     }
204 }
205 proptest! {
206     #[test]
207     fn populate_excuses(e in PopulateExcusesExcercise::strat()) {
208         e.test()
209     }
210 }
211