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