From 67d2ce6d85af70aefa378fd65404e7deb9380929 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Tue, 4 Feb 2020 13:35:39 -0800 Subject: [PATCH] Refactor tests to use proptest (#6) * generator: take an &mut GuestMemory rather than pass the owned GuestMemory in, just give exclusive access to it. Makes testing easier. * tests: start transforming tests to check abi-level generated code as well * finish lowering of test funcs * tests: rename variables to more sensible names * proptesting: reliably finds that we dont allow stuff to be right against end of memory! * memory: fix off-by-one calc in GuestMemory::contains(&self, Region) ty proptest! also, refactored the Region::overlaps to be the same code but easier to read. * generator: better location information in GuestError * testing: proptest generates memory areas, tests everything --- .gitignore | 1 + Cargo.toml | 3 + crates/generate/src/funcs.rs | 28 +- crates/generate/src/types.rs | 8 +- crates/memory/src/error.rs | 11 +- crates/memory/src/memory.rs | 2 +- crates/memory/src/region.rs | 4 +- tests/main.rs | 517 +++++++++++++++++++++++++++++------ 8 files changed, 469 insertions(+), 105 deletions(-) diff --git a/.gitignore b/.gitignore index 693699042b..d95f1f6a78 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target **/*.rs.bk Cargo.lock +proptest-regressions diff --git a/Cargo.toml b/Cargo.toml index 80d1545ab0..f71705b021 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,9 @@ edition = "2018" generate = { path = "crates/generate" } memory = { path = "crates/memory" } +[dev-dependencies] +proptest = "0.9" + [workspace] members = [ "crates/generate", diff --git a/crates/generate/src/funcs.rs b/crates/generate/src/funcs.rs index bd3d1f0349..336199d222 100644 --- a/crates/generate/src/funcs.rs +++ b/crates/generate/src/funcs.rs @@ -5,6 +5,8 @@ use crate::names::Names; use crate::types::{anon_lifetime, struct_is_copy}; pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { + let funcname = func.name.as_str(); + let ident = names.func(&func.name); let ctx_type = names.ctx_type(); let coretype = func.core_type(); @@ -28,7 +30,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { }); let abi_args = quote!( - ctx: &mut #ctx_type, memory: ::memory::GuestMemory, + ctx: &mut #ctx_type, memory: &mut ::memory::GuestMemory, #(#params),* ); let abi_ret = if let Some(ret) = &coretype.ret { @@ -52,7 +54,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { .map(|_res| quote!(#abi_ret::from(e))) .unwrap_or_else(|| quote!(())); - let error_handling: TokenStream = { + let error_handling = |location: &str| -> TokenStream { if let Some(tref) = &err_type { let abi_ret = match tref.type_().passed_by() { witx::TypePassedBy::Value(atom) => names.atom_type(atom), @@ -60,6 +62,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { }; let err_typename = names.type_ref(&tref, anon_lifetime()); quote! { + let e = ::memory::GuestError::InFunc { funcname: #funcname, location: #location, err: Box::new(e) }; let err: #err_typename = ::memory::GuestErrorType::from_error(e, ctx); return #abi_ret::from(err); } @@ -73,7 +76,7 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { let marshal_args = func .params .iter() - .map(|p| marshal_arg(names, p, error_handling.clone())); + .map(|p| marshal_arg(names, p, error_handling(p.name.as_str()))); let trait_args = func.params.iter().map(|param| { let name = names.func_param(¶m.name); match param.tref.type_().passed_by() { @@ -101,11 +104,11 @@ pub fn define_func(names: &Names, func: &witx::InterfaceFunc) -> TokenStream { .results .iter() .skip(1) - .map(|result| marshal_result(names, result, error_handling.clone())); + .map(|result| marshal_result(names, result, &error_handling)); let marshal_rets_pre = marshal_rets.clone().map(|(pre, _post)| pre); let marshal_rets_post = marshal_rets.map(|(_pre, post)| post); - let success = if let Some(err_type) = err_type { + let success = if let Some(ref err_type) = err_type { let err_typename = names.type_ref(&err_type, anon_lifetime()); quote! { let success:#err_typename = ::memory::GuestErrorType::success(); @@ -243,27 +246,32 @@ fn marshal_arg( } } -fn marshal_result( +fn marshal_result( names: &Names, result: &witx::InterfaceFuncParam, - error_handling: TokenStream, -) -> (TokenStream, TokenStream) { + error_handling: F, +) -> (TokenStream, TokenStream) +where + F: Fn(&str) -> TokenStream, +{ let tref = &result.tref; let write_val_to_ptr = { let pointee_type = names.type_ref(tref, anon_lifetime()); // core type is given func_ptr_binding name. let ptr_name = names.func_ptr_binding(&result.name); + let ptr_err_handling = error_handling(&format!("{}:result_ptr_mut", result.name.as_str())); + let ref_err_handling = error_handling(&format!("{}:result_ref_mut", result.name.as_str())); let pre = quote! { let mut #ptr_name = match memory.ptr_mut::<#pointee_type>(#ptr_name as u32) { Ok(p) => match p.as_ref_mut() { Ok(r) => r, Err(e) => { - #error_handling + #ref_err_handling } }, Err(e) => { - #error_handling + #ptr_err_handling } }; }; diff --git a/crates/generate/src/types.rs b/crates/generate/src/types.rs index e5cd4c0abe..f5bfd42bd1 100644 --- a/crates/generate/src/types.rs +++ b/crates/generate/src/types.rs @@ -192,13 +192,13 @@ fn define_copy_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) quote! { #type_::validate( &ptr.cast(#offset).map_err(|e| - ::memory::GuestError::InField{ + ::memory::GuestError::InDataField{ typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), })? ).map_err(|e| - ::memory::GuestError::InField { + ::memory::GuestError::InDataField { typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), @@ -278,13 +278,13 @@ fn define_ptr_struct(names: &Names, name: &witx::Id, s: &witx::StructDatatype) - quote! { #type_::validate( &ptr.cast(#offset).map_err(|e| - ::memory::GuestError::InField{ + ::memory::GuestError::InDataField{ typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), })? ).map_err(|e| - ::memory::GuestError::InField { + ::memory::GuestError::InDataField { typename: stringify!(#ident).to_owned(), field: stringify!(#fieldname).to_owned(), err: Box::new(e), diff --git a/crates/memory/src/error.rs b/crates/memory/src/error.rs index afbed26416..657faa3384 100644 --- a/crates/memory/src/error.rs +++ b/crates/memory/src/error.rs @@ -11,8 +11,15 @@ pub enum GuestError { PtrNotAligned(Region, u32), #[error("Pointer already borrowed: {0:?}")] PtrBorrowed(Region), - #[error("In {typename}.{field}:")] - InField { + #[error("In func {funcname}:{location}:")] + InFunc { + funcname: &'static str, + location: &'static str, + #[source] + err: Box, + }, + #[error("In data {typename}.{field}:")] + InDataField { typename: String, field: String, #[source] diff --git a/crates/memory/src/memory.rs b/crates/memory/src/memory.rs index f301647c14..a8857f893d 100644 --- a/crates/memory/src/memory.rs +++ b/crates/memory/src/memory.rs @@ -26,7 +26,7 @@ impl<'a> GuestMemory<'a> { fn contains(&self, r: Region) -> bool { r.start < self.len && r.len < self.len // make sure next clause doesnt underflow - && r.start < (self.len - r.len) + && r.start <= (self.len - r.len) } pub fn ptr(&'a self, at: u32) -> Result, GuestError> { diff --git a/crates/memory/src/region.rs b/crates/memory/src/region.rs index 414f6e1970..2e0aadc50f 100644 --- a/crates/memory/src/region.rs +++ b/crates/memory/src/region.rs @@ -7,10 +7,10 @@ pub struct Region { impl Region { pub fn overlaps(&self, rhs: Region) -> bool { let self_start = self.start as u64; - let self_end = ((self_start + self.len as u64) as i64 - 1) as u64; + let self_end = self_start + (self.len - 1) as u64; let rhs_start = rhs.start as u64; - let rhs_end = ((rhs_start + rhs.len as u64) as i64 - 1) as u64; + let rhs_end = rhs_start + (rhs.len - 1) as u64; // start of rhs inside self: if rhs_start >= self_start && rhs_start < self_end { diff --git a/tests/main.rs b/tests/main.rs index df4f6341c7..c03a128d5a 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,10 +1,11 @@ +use memory::GuestRef; +use proptest::prelude::*; + generate::from_witx!({ witx: ["tests/test.witx"], ctx: WasiCtx, }); -use crate::foo::Foo; - pub struct WasiCtx { guest_errors: Vec<::memory::GuestError>, } @@ -25,45 +26,48 @@ impl foo::Foo for WasiCtx { fn baz( &mut self, - _excuse: types::Excuse, - a_better_excuse_by_reference: ::memory::GuestPtrMut, - a_lamer_excuse_by_reference: ::memory::GuestPtr, - two_layers_of_excuses: ::memory::GuestPtrMut<::memory::GuestPtr>, + input1: types::Excuse, + input2_ptr: ::memory::GuestPtrMut, + input3_ptr: ::memory::GuestPtr, + input4_ptr_ptr: ::memory::GuestPtrMut<::memory::GuestPtr>, ) -> Result<(), types::Errno> { + println!("BAZ input1 {:?}", input1); // Read enum value from mutable: - let mut a_better_excuse_ref: ::memory::GuestRefMut = - a_better_excuse_by_reference.as_ref_mut().map_err(|e| { - eprintln!("a_better_excuse_by_reference error: {}", e); + let mut input2_ref: ::memory::GuestRefMut = + input2_ptr.as_ref_mut().map_err(|e| { + eprintln!("input2_ptr error: {}", e); types::Errno::InvalidArg })?; - let _a_better_excuse: types::Excuse = *a_better_excuse_ref; + let input2: types::Excuse = *input2_ref; + println!("input2 {:?}", input2); // Read enum value from immutable ptr: - let a_lamer_excuse = *a_lamer_excuse_by_reference.as_ref().map_err(|e| { - eprintln!("a_lamer_excuse_by_reference error: {}", e); + let input3 = *input3_ptr.as_ref().map_err(|e| { + eprintln!("input3_ptr error: {}", e); types::Errno::InvalidArg })?; - println!("{:?}", a_lamer_excuse); + println!("input3 {:?}", input3); // Write enum to mutable ptr: - *a_better_excuse_ref = a_lamer_excuse; - println!("{:?}", *a_better_excuse_ref); + *input2_ref = input3; + println!("wrote to input2_ref {:?}", input3); // Read ptr value from mutable ptr: - let one_layer_down: ::memory::GuestPtr = - two_layers_of_excuses.read_ptr_from_guest().map_err(|e| { - eprintln!("one_layer_down error: {}", e); + let input4_ptr: ::memory::GuestPtr = + input4_ptr_ptr.read_ptr_from_guest().map_err(|e| { + eprintln!("input4_ptr_ptr error: {}", e); types::Errno::InvalidArg })?; // Read enum value from that ptr: - let _two_layers_down: types::Excuse = *one_layer_down.as_ref().map_err(|e| { - eprintln!("two_layers_down error: {}", e); + let input4: types::Excuse = *input4_ptr.as_ref().map_err(|e| { + eprintln!("input4_ptr error: {}", e); types::Errno::InvalidArg })?; + println!("input4 {:?}", input4); // Write ptr value to mutable ptr: - two_layers_of_excuses.write_ptr_to_guest(&a_better_excuse_by_reference.as_immut()); + input4_ptr_ptr.write_ptr_to_guest(&input2_ptr.as_immut()); Ok(()) } @@ -98,6 +102,7 @@ impl ::memory::GuestErrorType for types::Errno { types::Errno::Ok } fn from_error(e: ::memory::GuestError, ctx: &mut WasiCtx) -> types::Errno { + eprintln!("GUEST ERROR: {:?}", e); ctx.guest_errors.push(e); types::Errno::InvalidArg } @@ -117,6 +122,64 @@ impl HostMemory { pub fn len(&self) -> usize { self.buffer.len() } + pub fn mem_area_strat(align: u32) -> BoxedStrategy { + prop::num::u32::ANY + .prop_map(move |p| { + let p_aligned = p - (p % align); // Align according to argument + let ptr = p_aligned % 4096; // Put inside memory + MemArea { ptr, len: align } + }) + .boxed() + } +} + +#[derive(Debug)] +struct MemArea { + ptr: u32, + len: u32, +} + +// This code is a whole lot like the Region::overlaps func thats at the core of the code under +// test. +// So, I implemented this one with std::ops::Range so it is less likely I wrote the same bug in two +// places. +fn overlapping(a: &MemArea, b: &MemArea) -> bool { + // a_range is all elems in A + let a_range = std::ops::Range { + start: a.ptr, + end: a.ptr + a.len - 1, + }; + // b_range is all elems in B + let b_range = std::ops::Range { + start: b.ptr, + end: b.ptr + b.len - 1, + }; + // No element in B is contained in A: + for b_elem in b_range.clone() { + if a_range.contains(&b_elem) { + return true; + } + } + // No element in A is contained in B: + for a_elem in a_range { + if b_range.contains(&a_elem) { + return true; + } + } + return false; +} + +fn non_overlapping_set(areas: &[&MemArea]) -> bool { + // A is all areas + for (i, a) in areas.iter().enumerate() { + // (A, B) is every pair of areas + for b in areas[i + 1..].iter() { + if overlapping(a, b) { + return false; + } + } + } + return true; } #[test] @@ -127,76 +190,358 @@ fn hostmemory_is_aligned() { assert_eq!(h.as_mut_ptr() as usize % 4096, 0); } -#[test] -fn bat() { - let mut ctx = WasiCtx::new(); - assert_eq!(ctx.bat(2), Ok(4.0)); +#[derive(Debug)] +struct BatExercise { + pub input: u32, + pub return_loc: MemArea, } -#[test] -fn baz() { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let guest_memory = memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - let sizeof_excuse = std::mem::size_of::(); - let padding = 4 - sizeof_excuse % 4; - { - let lame_mut: memory::GuestPtrMut = guest_memory.ptr_mut(0).unwrap(); - let mut lame = lame_mut.as_ref_mut().unwrap(); - *lame = types::Excuse::Sleeping; - } - let lame: memory::GuestPtr = guest_memory - .ptr(0) - .expect("GuestPtr fits in the memory"); - assert_eq!(*lame.as_ref().unwrap(), types::Excuse::Sleeping); - let better: memory::GuestPtrMut = guest_memory - .ptr_mut((sizeof_excuse + padding) as u32) - .expect("GuestPtr fits in the memory"); - let ptr_to_ptr: memory::GuestPtrMut> = guest_memory - .ptr_mut((sizeof_excuse + padding) as u32 * 2) - .expect("GuestPtr> fits in the memory"); - assert!(ctx - .baz( - types::Excuse::DogAte, - better.clone(), - lame, - ptr_to_ptr.clone() +impl BatExercise { + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = + memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + let bat_err = foo::bat( + &mut ctx, + &mut guest_memory, + self.input as i32, + self.return_loc.ptr as i32, + ); + + let return_val: GuestRef = guest_memory + .ptr(self.return_loc.ptr) + .expect("return loc ptr") + .as_ref() + .expect("return val ref"); + assert_eq!(bat_err, types::Errno::Ok.into(), "bat errno"); + assert_eq!(*return_val, (self.input as f32) * 2.0, "bat return val"); + } + + pub fn strat() -> BoxedStrategy { + (prop::num::u32::ANY, HostMemory::mem_area_strat(4)) + .prop_map(|(input, return_loc)| BatExercise { input, return_loc }) + .boxed() + } +} + +proptest! { + #[test] + fn bat(e in BatExercise::strat()) { + e.test() + } +} + +fn excuse_strat() -> impl Strategy { + prop_oneof![ + Just(types::Excuse::DogAte), + Just(types::Excuse::Traffic), + Just(types::Excuse::Sleeping), + ] + .boxed() +} + +#[derive(Debug)] +struct BazExercise { + pub input1: types::Excuse, + pub input2: types::Excuse, + pub input2_loc: MemArea, + pub input3: types::Excuse, + pub input3_loc: MemArea, + pub input4: types::Excuse, + pub input4_loc: MemArea, + pub input4_ptr_loc: MemArea, +} + +impl BazExercise { + pub fn strat() -> BoxedStrategy { + ( + excuse_strat(), + excuse_strat(), + HostMemory::mem_area_strat(4), + excuse_strat(), + HostMemory::mem_area_strat(4), + excuse_strat(), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + ) + .prop_map( + |( + input1, + input2, + input2_loc, + input3, + input3_loc, + input4, + input4_loc, + input4_ptr_loc, + )| BazExercise { + input1, + input2, + input2_loc, + input3, + input3_loc, + input4, + input4_loc, + input4_ptr_loc, + }, + ) + .prop_filter("non-overlapping pointers", |e| { + non_overlapping_set(&[ + &e.input2_loc, + &e.input3_loc, + &e.input4_loc, + &e.input4_ptr_loc, + ]) + }) + .boxed() + } + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = + memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + *guest_memory + .ptr_mut(self.input2_loc.ptr) + .expect("input2 ptr") + .as_ref_mut() + .expect("input2 ref_mut") = self.input2; + + *guest_memory + .ptr_mut(self.input3_loc.ptr) + .expect("input3 ptr") + .as_ref_mut() + .expect("input3 ref_mut") = self.input3; + + *guest_memory + .ptr_mut(self.input4_loc.ptr) + .expect("input4 ptr") + .as_ref_mut() + .expect("input4 ref_mut") = self.input4; + + *guest_memory + .ptr_mut(self.input4_ptr_loc.ptr) + .expect("input4 ptr ptr") + .as_ref_mut() + .expect("input4 ptr ref_mut") = self.input4_loc.ptr; + + let baz_err = foo::baz( + &mut ctx, + &mut guest_memory, + self.input1.into(), + self.input2_loc.ptr as i32, + self.input3_loc.ptr as i32, + self.input4_ptr_loc.ptr as i32, + ); + assert_eq!(baz_err, types::Errno::Ok.into(), "baz errno"); + + // Implementation of baz writes input3 to the input2_loc: + let written_to_input2_loc: i32 = *guest_memory + .ptr(self.input2_loc.ptr) + .expect("input2 ptr") + .as_ref() + .expect("input2 ref"); + + assert_eq!( + written_to_input2_loc, + self.input3.into(), + "baz written to input2" + ); + + // Implementation of baz writes input2_loc to input4_ptr_loc: + let written_to_input4_ptr: u32 = *guest_memory + .ptr(self.input4_ptr_loc.ptr) + .expect("input4_ptr_loc ptr") + .as_ref() + .expect("input4_ptr_loc ref"); + + assert_eq!( + written_to_input4_ptr, self.input2_loc.ptr, + "baz written to input4_ptr" + ); + } +} +proptest! { + #[test] + fn baz(e in BazExercise::strat()) { + e.test(); + } +} + +#[derive(Debug)] +struct SumOfPairExercise { + pub input: types::PairInts, + pub input_loc: MemArea, + pub return_loc: MemArea, +} + +impl SumOfPairExercise { + pub fn strat() -> BoxedStrategy { + ( + prop::num::i32::ANY, + prop::num::i32::ANY, + HostMemory::mem_area_strat(8), + HostMemory::mem_area_strat(8), ) - .is_ok()); - assert_eq!(*better.as_ref().unwrap(), types::Excuse::Sleeping); - let ptr = ptr_to_ptr.read_ptr_from_guest().unwrap(); - assert_eq!(*ptr.as_ref().unwrap(), types::Excuse::Sleeping); + .prop_map(|(first, second, input_loc, return_loc)| SumOfPairExercise { + input: types::PairInts { first, second }, + input_loc, + return_loc, + }) + .prop_filter("non-overlapping pointers", |e| { + non_overlapping_set(&[&e.input_loc, &e.return_loc]) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = + memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + *guest_memory + .ptr_mut(self.input_loc.ptr) + .expect("input ptr") + .as_ref_mut() + .expect("input ref_mut") = self.input.first; + *guest_memory + .ptr_mut(self.input_loc.ptr + 4) + .expect("input ptr") + .as_ref_mut() + .expect("input ref_mut") = self.input.second; + let sum_err = foo::sum_of_pair( + &mut ctx, + &mut guest_memory, + self.input_loc.ptr as i32, + self.return_loc.ptr as i32, + ); + + assert_eq!(sum_err, types::Errno::Ok.into(), "sum errno"); + + let return_val: i64 = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref"); + + assert_eq!( + return_val, + self.input.first as i64 + self.input.second as i64, + "sum return value" + ); + } } -#[test] -fn sum_of_pair() { - let mut ctx = WasiCtx::new(); - let pair = types::PairInts { - first: 1, - second: 2, - }; - assert_eq!(ctx.sum_of_pair(&pair), Ok(3)); +proptest! { + #[test] + fn sum_of_pair(e in SumOfPairExercise::strat()) { + e.test(); + } } -#[test] -fn sum_of_pair_of_ptrs() { - let mut ctx = WasiCtx::new(); - let mut host_memory = HostMemory::new(); - let guest_memory = memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); - { - let first_mut: memory::GuestPtrMut = guest_memory.ptr_mut(0).unwrap(); - let mut x = first_mut.as_ref_mut().unwrap(); - *x = 1; - let second_mut: memory::GuestPtrMut = guest_memory.ptr_mut(4).unwrap(); - let mut x = second_mut.as_ref_mut().unwrap(); - *x = 2; - } - let first: memory::GuestPtr = guest_memory - .ptr(0) - .expect("GuestPtr fits in the memory"); - let second: memory::GuestPtr = guest_memory - .ptr(4) - .expect("GuestPtr fits in the memory"); - let pair = types::PairIntPtrs { first, second }; - assert_eq!(ctx.sum_of_pair_of_ptrs(&pair), Ok(3)); +#[derive(Debug)] +struct SumPairPtrsExercise { + input_first: i32, + input_second: i32, + input_first_loc: MemArea, + input_second_loc: MemArea, + input_struct_loc: MemArea, + return_loc: MemArea, +} + +impl SumPairPtrsExercise { + pub fn strat() -> BoxedStrategy { + ( + prop::num::i32::ANY, + prop::num::i32::ANY, + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(4), + HostMemory::mem_area_strat(8), + HostMemory::mem_area_strat(8), + ) + .prop_map( + |( + input_first, + input_second, + input_first_loc, + input_second_loc, + input_struct_loc, + return_loc, + )| SumPairPtrsExercise { + input_first, + input_second, + input_first_loc, + input_second_loc, + input_struct_loc, + return_loc, + }, + ) + .prop_filter("non-overlapping pointers", |e| { + non_overlapping_set(&[ + &e.input_first_loc, + &e.input_second_loc, + &e.input_struct_loc, + &e.return_loc, + ]) + }) + .boxed() + } + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = + memory::GuestMemory::new(host_memory.as_mut_ptr(), host_memory.len() as u32); + + *guest_memory + .ptr_mut(self.input_first_loc.ptr) + .expect("input_first ptr") + .as_ref_mut() + .expect("input_first ref") = self.input_first; + *guest_memory + .ptr_mut(self.input_second_loc.ptr) + .expect("input_second ptr") + .as_ref_mut() + .expect("input_second ref") = self.input_second; + + *guest_memory + .ptr_mut(self.input_struct_loc.ptr) + .expect("input_struct ptr") + .as_ref_mut() + .expect("input_struct ref") = self.input_first_loc.ptr; + *guest_memory + .ptr_mut(self.input_struct_loc.ptr + 4) + .expect("input_struct ptr") + .as_ref_mut() + .expect("input_struct ref") = self.input_second_loc.ptr; + + let res = foo::sum_of_pair_of_ptrs( + &mut ctx, + &mut guest_memory, + self.input_struct_loc.ptr as i32, + self.return_loc.ptr as i32, + ); + + assert_eq!(res, types::Errno::Ok.into(), "sum of pair of ptrs errno"); + + let doubled: i64 = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref"); + + assert_eq!( + doubled, + (self.input_first as i64) + (self.input_second as i64), + "sum of pair of ptrs return val" + ); + } +} +proptest! { + #[test] + fn sum_of_pair_of_ptrs(e in SumPairPtrsExercise::strat()) { + e.test() + } }