From 02f311cbb1190292218bacc03d5748d6c8a7152d Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Wed, 26 Feb 2020 14:24:09 -0800 Subject: [PATCH] finish union testing --- tests/union.rs | 244 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 237 insertions(+), 7 deletions(-) diff --git a/tests/union.rs b/tests/union.rs index d5da1bff96..91b50c033f 100644 --- a/tests/union.rs +++ b/tests/union.rs @@ -1,7 +1,5 @@ use proptest::prelude::*; -use wiggle_runtime::{ - GuestError, GuestErrorType, GuestMemory, GuestPtr, GuestPtrMut, GuestRef, GuestRefMut, -}; +use wiggle_runtime::{GuestError, GuestType}; use wiggle_test::{impl_errno, HostMemory, MemArea, WasiCtx}; wiggle_generate::from_witx!({ @@ -11,10 +9,36 @@ wiggle_generate::from_witx!({ impl_errno!(types::Errno); +// Avoid panics on overflow +fn mult_lose_overflow(a: i32, b: u32) -> i32 { + let a_64: i64 = a as i64; + let b_64: i64 = b as i64; + let product = a_64 * b_64; + product as i32 +} + +// Avoid assert_eq(NaN, NaN) failures +fn mult_zero_nan(a: f32, b: u32) -> f32 { + if a.is_nan() { + 0.0 + } else { + let product = a * b as f32; + if product.is_nan() { + 0.0 + } else { + product + } + } +} + impl union_example::UnionExample for WasiCtx { fn get_tag(&mut self, u: &types::Reason) -> Result { println!("GET TAG: {:?}", u); - Ok(types::Excuse::DogAte) + match u { + types::Reason::DogAte { .. } => Ok(types::Excuse::DogAte), + types::Reason::Traffic { .. } => Ok(types::Excuse::Traffic), + types::Reason::Sleeping { .. } => Ok(types::Excuse::Sleeping), + } } fn reason_mult( &mut self, @@ -26,13 +50,13 @@ impl union_example::UnionExample for WasiCtx { let mut f = fptr.as_ref_mut().expect("valid pointer"); let val = *f; println!("REASON MULT DogAte({})", val); - *f = val * multiply_by as f32; + *f = mult_zero_nan(val, multiply_by); } types::ReasonMut::Traffic(iptr) => { let mut i = iptr.as_ref_mut().expect("valid pointer"); - let val = *i; + let val: i32 = *i; println!("REASON MULT Traffic({})", val); - *i = val * multiply_by as i32; + *i = mult_lose_overflow(val, multiply_by); } types::ReasonMut::Sleeping => { println!("REASON MULT Sleeping"); @@ -41,3 +65,209 @@ impl union_example::UnionExample for WasiCtx { Ok(()) } } + +fn reason_strat() -> impl Strategy { + prop_oneof![ + prop::num::f32::ANY.prop_map(|v| types::Reason::DogAte(v)), + prop::num::i32::ANY.prop_map(|v| types::Reason::Traffic(v)), + Just(types::Reason::Sleeping), + ] + .boxed() +} + +fn reason_tag(r: &types::Reason) -> types::Excuse { + match r { + types::Reason::DogAte { .. } => types::Excuse::DogAte, + types::Reason::Traffic { .. } => types::Excuse::Traffic, + types::Reason::Sleeping { .. } => types::Excuse::Sleeping, + } +} + +#[derive(Debug)] +struct GetTagExercise { + pub input: types::Reason, + pub input_loc: MemArea, + pub return_loc: MemArea, +} + +impl GetTagExercise { + pub fn strat() -> BoxedStrategy { + ( + reason_strat(), + HostMemory::mem_area_strat(types::Reason::size()), + HostMemory::mem_area_strat(types::Excuse::size()), + ) + .prop_map(|(input, input_loc, return_loc)| GetTagExercise { + input, + input_loc, + return_loc, + }) + .prop_filter("non-overlapping pointers", |e| { + MemArea::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 = host_memory.guest_memory(); + + let discriminant: u8 = reason_tag(&self.input).into(); + *guest_memory + .ptr_mut(self.input_loc.ptr) + .expect("input discriminant ptr") + .as_ref_mut() + .expect("input discriminant ref_mut") = discriminant; + match self.input { + types::Reason::DogAte(f) => { + *guest_memory + .ptr_mut(self.input_loc.ptr + 4) + .expect("input contents ptr") + .as_ref_mut() + .expect("input contents ref_mut") = f; + } + types::Reason::Traffic(v) => { + *guest_memory + .ptr_mut(self.input_loc.ptr + 4) + .expect("input contents ptr") + .as_ref_mut() + .expect("input contents ref_mut") = v; + } + types::Reason::Sleeping => {} // Do nothing + } + let e = union_example::get_tag( + &mut ctx, + &mut guest_memory, + self.input_loc.ptr as i32, + self.return_loc.ptr as i32, + ); + + assert_eq!(e, types::Errno::Ok.into(), "get_tag errno"); + + let return_val: types::Excuse = *guest_memory + .ptr(self.return_loc.ptr) + .expect("return ptr") + .as_ref() + .expect("return ref"); + + assert_eq!(return_val, reason_tag(&self.input), "get_tag return value"); + } +} + +proptest! { + #[test] + fn get_tag(e in GetTagExercise::strat()) { + e.test(); + } +} + +#[derive(Debug)] +struct ReasonMultExercise { + pub input: types::Reason, + pub input_loc: MemArea, + pub input_pointee_loc: MemArea, + pub multiply_by: u32, +} + +impl ReasonMultExercise { + pub fn strat() -> BoxedStrategy { + ( + reason_strat(), + HostMemory::mem_area_strat(types::Reason::size()), + HostMemory::mem_area_strat(4), + prop::num::u32::ANY, + ) + .prop_map( + |(input, input_loc, input_pointee_loc, multiply_by)| ReasonMultExercise { + input, + input_loc, + input_pointee_loc, + multiply_by, + }, + ) + .prop_filter("non-overlapping pointers", |e| { + MemArea::non_overlapping_set(&[&e.input_loc, &e.input_pointee_loc]) + }) + .boxed() + } + + pub fn test(&self) { + let mut ctx = WasiCtx::new(); + let mut host_memory = HostMemory::new(); + let mut guest_memory = host_memory.guest_memory(); + + let discriminant: u8 = reason_tag(&self.input).into(); + *guest_memory + .ptr_mut(self.input_loc.ptr) + .expect("input discriminant ptr") + .as_ref_mut() + .expect("input discriminant ref_mut") = discriminant; + *guest_memory + .ptr_mut(self.input_loc.ptr + 4) + .expect("input pointer ptr") + .as_ref_mut() + .expect("input pointer ref_mut") = self.input_pointee_loc.ptr; + + match self.input { + types::Reason::DogAte(f) => { + *guest_memory + .ptr_mut(self.input_pointee_loc.ptr) + .expect("input contents ptr") + .as_ref_mut() + .expect("input contents ref_mut") = f; + } + types::Reason::Traffic(v) => { + *guest_memory + .ptr_mut(self.input_pointee_loc.ptr) + .expect("input contents ptr") + .as_ref_mut() + .expect("input contents ref_mut") = v; + } + types::Reason::Sleeping => {} // Do nothing + } + let e = union_example::reason_mult( + &mut ctx, + &mut guest_memory, + self.input_loc.ptr as i32, + self.multiply_by as i32, + ); + + assert_eq!(e, types::Errno::Ok.into(), "reason_mult errno"); + + match self.input { + types::Reason::DogAte(f) => { + let f_result: f32 = *guest_memory + .ptr(self.input_pointee_loc.ptr) + .expect("input contents ptr") + .as_ref() + .expect("input contents ref_mut"); + assert_eq!( + mult_zero_nan(f, self.multiply_by), + f_result, + "DogAte result" + ) + } + types::Reason::Traffic(v) => { + let v_result: i32 = *guest_memory + .ptr(self.input_pointee_loc.ptr) + .expect("input contents ptr") + .as_ref() + .expect("input contents ref_mut"); + assert_eq!( + mult_lose_overflow(v, self.multiply_by), + v_result, + "Traffic result" + ) + } + types::Reason::Sleeping => {} // Do nothing + } + } +} + +proptest! { + #[test] + fn reason_mult(e in ReasonMultExercise::strat()) { + e.test(); + } +}