Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wiggle/tests/lists.rs
1692 views
1
use proptest::prelude::*;
2
use wiggle::{GuestMemory, GuestPtr, GuestType};
3
use wiggle_test::{HostMemory, MemArea, MemAreas, WasiCtx, impl_errno};
4
5
wiggle::from_witx!({
6
witx: ["tests/lists.witx"],
7
});
8
9
impl_errno!(types::Errno);
10
11
impl<'a> lists::Lists for WasiCtx<'a> {
12
fn reduce_excuses(
13
&mut self,
14
memory: &mut GuestMemory<'_>,
15
excuses: types::ConstExcuseArray,
16
) -> Result<types::Excuse, types::Errno> {
17
let last = memory
18
.read(
19
excuses
20
.iter()
21
.last()
22
.expect("input array is non-empty")
23
.expect("valid ptr to ptr"),
24
)
25
.expect("valid ptr to some Excuse value");
26
Ok(memory.read(last).expect("dereferencing ptr should succeed"))
27
}
28
29
fn populate_excuses(
30
&mut self,
31
memory: &mut GuestMemory<'_>,
32
excuses: types::ExcuseArray,
33
) -> Result<(), types::Errno> {
34
for excuse in excuses.iter() {
35
let ptr_to_excuse = memory
36
.read(excuse.expect("valid ptr to ptr"))
37
.expect("valid ptr to some Excuse value");
38
memory
39
.write(ptr_to_excuse, types::Excuse::Sleeping)
40
.expect("dereferencing mut ptr should succeed");
41
}
42
Ok(())
43
}
44
}
45
46
#[derive(Debug)]
47
struct ReduceExcusesExercise {
48
excuse_values: Vec<types::Excuse>,
49
excuse_ptr_locs: Vec<MemArea>,
50
array_ptr_loc: MemArea,
51
return_ptr_loc: MemArea,
52
}
53
54
impl ReduceExcusesExercise {
55
pub fn strat() -> BoxedStrategy<Self> {
56
(1..256u32)
57
.prop_flat_map(|len| {
58
let len_usize = len as usize;
59
(
60
proptest::collection::vec(excuse_strat(), len_usize..=len_usize),
61
proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize),
62
HostMemory::mem_area_strat(4 * len),
63
HostMemory::mem_area_strat(4),
64
)
65
})
66
.prop_map(
67
|(excuse_values, excuse_ptr_locs, array_ptr_loc, return_ptr_loc)| Self {
68
excuse_values,
69
excuse_ptr_locs,
70
array_ptr_loc,
71
return_ptr_loc,
72
},
73
)
74
.prop_filter("non-overlapping pointers", |e| {
75
let mut all = vec![e.array_ptr_loc, e.return_ptr_loc];
76
all.extend(e.excuse_ptr_locs.iter());
77
MemArea::non_overlapping_set(all)
78
})
79
.boxed()
80
}
81
82
pub fn test(&self) {
83
let mut ctx = WasiCtx::new();
84
let mut host_memory = HostMemory::new();
85
let mut memory = host_memory.guest_memory();
86
87
// Populate memory with pointers to generated Excuse values
88
for (&excuse, ptr) in self.excuse_values.iter().zip(self.excuse_ptr_locs.iter()) {
89
memory
90
.write(GuestPtr::new(ptr.ptr), excuse)
91
.expect("deref ptr mut to Excuse value");
92
}
93
94
// Populate the array with pointers to generated Excuse values
95
{
96
let array: GuestPtr<[GuestPtr<types::Excuse>]> =
97
GuestPtr::new((self.array_ptr_loc.ptr, self.excuse_ptr_locs.len() as u32));
98
for (slot, ptr) in array.iter().zip(&self.excuse_ptr_locs) {
99
let slot = slot.expect("array should be in bounds");
100
memory
101
.write(slot, GuestPtr::new(ptr.ptr))
102
.expect("should succeed in writing array");
103
}
104
}
105
106
let res = lists::reduce_excuses(
107
&mut ctx,
108
&mut memory,
109
self.array_ptr_loc.ptr as i32,
110
self.excuse_ptr_locs.len() as i32,
111
self.return_ptr_loc.ptr as i32,
112
)
113
.unwrap();
114
115
assert_eq!(res, types::Errno::Ok as i32, "reduce excuses errno");
116
117
let expected = *self
118
.excuse_values
119
.last()
120
.expect("generated vec of excuses should be non-empty");
121
let given: types::Excuse = memory
122
.read(GuestPtr::new(self.return_ptr_loc.ptr))
123
.expect("deref ptr to returned value");
124
assert_eq!(expected, given, "reduce excuses return val");
125
}
126
}
127
proptest! {
128
#[test]
129
fn reduce_excuses(e in ReduceExcusesExercise::strat()) {
130
e.test()
131
}
132
}
133
134
fn excuse_strat() -> impl Strategy<Value = types::Excuse> {
135
prop_oneof![
136
Just(types::Excuse::DogAte),
137
Just(types::Excuse::Traffic),
138
Just(types::Excuse::Sleeping),
139
]
140
.boxed()
141
}
142
143
#[derive(Debug)]
144
struct PopulateExcusesExercise {
145
array_ptr_loc: MemArea,
146
elements: Vec<MemArea>,
147
}
148
149
impl PopulateExcusesExercise {
150
pub fn strat() -> BoxedStrategy<Self> {
151
(1..256u32)
152
.prop_flat_map(|len| {
153
let len_usize = len as usize;
154
(
155
HostMemory::mem_area_strat(4 * len),
156
proptest::collection::vec(HostMemory::mem_area_strat(4), len_usize..=len_usize),
157
)
158
})
159
.prop_map(|(array_ptr_loc, elements)| Self {
160
array_ptr_loc,
161
elements,
162
})
163
.prop_filter("non-overlapping pointers", |e| {
164
let mut all = vec![e.array_ptr_loc];
165
all.extend(e.elements.iter());
166
MemArea::non_overlapping_set(all)
167
})
168
.boxed()
169
}
170
171
pub fn test(&self) {
172
let mut ctx = WasiCtx::new();
173
let mut host_memory = HostMemory::new();
174
let mut memory = host_memory.guest_memory();
175
176
// Populate array with valid pointers to Excuse type in memory
177
let ptr = GuestPtr::<[GuestPtr<types::Excuse>]>::new((
178
self.array_ptr_loc.ptr,
179
self.elements.len() as u32,
180
));
181
for (ptr, val) in ptr.iter().zip(&self.elements) {
182
memory
183
.write(
184
ptr.expect("should be valid pointer"),
185
GuestPtr::new(val.ptr),
186
)
187
.expect("failed to write value");
188
}
189
190
let res = lists::populate_excuses(
191
&mut ctx,
192
&mut memory,
193
self.array_ptr_loc.ptr as i32,
194
self.elements.len() as i32,
195
)
196
.unwrap();
197
assert_eq!(res, types::Errno::Ok as i32, "populate excuses errno");
198
199
let arr: GuestPtr<[GuestPtr<types::Excuse>]> =
200
GuestPtr::new((self.array_ptr_loc.ptr, self.elements.len() as u32));
201
for el in arr.iter() {
202
let ptr_to_ptr = memory
203
.read(el.expect("valid ptr to ptr"))
204
.expect("valid ptr to some Excuse value");
205
assert_eq!(
206
memory
207
.read(ptr_to_ptr)
208
.expect("dereferencing ptr to some Excuse value"),
209
types::Excuse::Sleeping,
210
"element should equal Excuse::Sleeping"
211
);
212
}
213
}
214
}
215
proptest! {
216
#[test]
217
fn populate_excuses(e in PopulateExcusesExercise::strat()) {
218
e.test()
219
}
220
}
221
222
impl<'a> array_traversal::ArrayTraversal for WasiCtx<'a> {
223
fn sum_of_element(
224
&mut self,
225
memory: &mut GuestMemory<'_>,
226
elements: GuestPtr<[types::PairInts]>,
227
index: u32,
228
) -> Result<i32, types::Errno> {
229
let elem_ptr = elements.get(index).ok_or(types::Errno::InvalidArg)?;
230
let pair = memory
231
.read(elem_ptr)
232
.map_err(|_| types::Errno::DontWantTo)?;
233
Ok(pair.first.wrapping_add(pair.second))
234
}
235
fn sum_of_elements(
236
&mut self,
237
memory: &mut GuestMemory<'_>,
238
elements: GuestPtr<[types::PairInts]>,
239
start: u32,
240
end: u32,
241
) -> Result<i32, types::Errno> {
242
let elem_range = elements
243
.get_range(start..end)
244
.ok_or(types::Errno::InvalidArg)?;
245
let mut sum: i32 = 0;
246
for e in elem_range.iter() {
247
let pair = memory
248
.read(e.map_err(|_| types::Errno::DontWantTo)?)
249
.map_err(|_| types::Errno::PhysicallyUnable)?;
250
sum = sum.wrapping_add(pair.first).wrapping_add(pair.second);
251
}
252
Ok(sum)
253
}
254
}
255
256
impl types::PairInts {
257
pub fn strat() -> BoxedStrategy<Self> {
258
(prop::num::i32::ANY, prop::num::i32::ANY)
259
.prop_map(|(first, second)| types::PairInts { first, second })
260
.boxed()
261
}
262
}
263
264
#[derive(Debug)]
265
struct SumElementsExercise {
266
elements: Vec<types::PairInts>,
267
element_loc: MemArea,
268
return_loc: MemArea,
269
start_ix: u32,
270
end_ix: u32,
271
}
272
273
impl SumElementsExercise {
274
pub fn strat() -> BoxedStrategy<Self> {
275
(
276
prop::collection::vec(types::PairInts::strat(), 1..256),
277
HostMemory::mem_area_strat(4),
278
)
279
.prop_flat_map(|(elements, return_loc)| {
280
let len = elements.len() as u32;
281
(
282
Just(elements),
283
HostMemory::byte_slice_strat(
284
len * types::PairInts::guest_size(),
285
types::PairInts::guest_size(),
286
&MemAreas::from([return_loc]),
287
),
288
Just(return_loc),
289
0..len,
290
0..len,
291
)
292
})
293
.prop_map(
294
|(elements, element_loc, return_loc, start_ix, end_ix)| SumElementsExercise {
295
elements,
296
element_loc,
297
return_loc,
298
start_ix,
299
end_ix,
300
},
301
)
302
.boxed()
303
}
304
pub fn test(&self) {
305
let mut ctx = WasiCtx::new();
306
let mut host_memory = HostMemory::new();
307
let mut memory = host_memory.guest_memory();
308
309
// Populate array
310
let ptr =
311
GuestPtr::<[types::PairInts]>::new((self.element_loc.ptr, self.elements.len() as u32));
312
for (ptr, val) in ptr.iter().zip(&self.elements) {
313
memory
314
.write(ptr.expect("should be valid pointer"), val.clone())
315
.expect("failed to write value");
316
}
317
318
let res = array_traversal::sum_of_element(
319
&mut ctx,
320
&mut memory,
321
self.element_loc.ptr as i32,
322
self.elements.len() as i32,
323
self.start_ix as i32,
324
self.return_loc.ptr as i32,
325
)
326
.unwrap();
327
assert_eq!(res, types::Errno::Ok as i32, "sum_of_element errno");
328
let result_ptr = GuestPtr::<i32>::new(self.return_loc.ptr);
329
let result = memory.read(result_ptr).expect("read result");
330
331
let e = self
332
.elements
333
.get(self.start_ix as usize)
334
.expect("start_ix must be in bounds");
335
assert_eq!(result, e.first.wrapping_add(e.second), "sum of element");
336
337
// Off the end of the array:
338
let res = array_traversal::sum_of_element(
339
&mut ctx,
340
&mut memory,
341
self.element_loc.ptr as i32,
342
self.elements.len() as i32,
343
self.elements.len() as i32,
344
self.return_loc.ptr as i32,
345
)
346
.unwrap();
347
assert_eq!(
348
res,
349
types::Errno::InvalidArg as i32,
350
"out of bounds sum_of_element errno"
351
);
352
353
let res = array_traversal::sum_of_elements(
354
&mut ctx,
355
&mut memory,
356
self.element_loc.ptr as i32,
357
self.elements.len() as i32,
358
self.start_ix as i32,
359
self.end_ix as i32,
360
self.return_loc.ptr as i32,
361
)
362
.unwrap();
363
if self.start_ix <= self.end_ix {
364
assert_eq!(
365
res,
366
types::Errno::Ok as i32,
367
"expected ok sum_of_elements errno"
368
);
369
let result_ptr = GuestPtr::<i32>::new(self.return_loc.ptr);
370
let result = memory.read(result_ptr).expect("read result");
371
372
let mut expected_sum: i32 = 0;
373
for elem in self
374
.elements
375
.get(self.start_ix as usize..self.end_ix as usize)
376
.unwrap()
377
.iter()
378
{
379
expected_sum = expected_sum
380
.wrapping_add(elem.first)
381
.wrapping_add(elem.second);
382
}
383
assert_eq!(result, expected_sum, "sum of elements");
384
} else {
385
assert_eq!(
386
res,
387
types::Errno::InvalidArg as i32,
388
"expected error out-of-bounds sum_of_elements"
389
);
390
}
391
392
// Index an array off the end of the array:
393
let res = array_traversal::sum_of_elements(
394
&mut ctx,
395
&mut memory,
396
self.element_loc.ptr as i32,
397
self.elements.len() as i32,
398
self.start_ix as i32,
399
self.elements.len() as i32 + 1,
400
self.return_loc.ptr as i32,
401
)
402
.unwrap();
403
assert_eq!(
404
res,
405
types::Errno::InvalidArg as i32,
406
"out of bounds sum_of_elements errno"
407
);
408
}
409
}
410
proptest! {
411
#[test]
412
fn sum_elements(e in SumElementsExercise::strat()) {
413
e.test()
414
}
415
}
416
417