Path: blob/main/crates/wiggle/tests/strings.rs
1692 views
use proptest::prelude::*;1use wiggle::{GuestMemory, GuestPtr};2use wiggle_test::{HostMemory, MemArea, MemAreas, WasiCtx, impl_errno};34wiggle::from_witx!({5witx: ["tests/strings.witx"],6});78impl_errno!(types::Errno);910impl<'a> strings::Strings for WasiCtx<'a> {11fn hello_string(12&mut self,13memory: &mut GuestMemory<'_>,14a_string: GuestPtr<str>,15) -> Result<u32, types::Errno> {16let s = memory17.as_str(a_string)18.expect("should be valid string")19.expect("expected non-shared memory");20println!("a_string='{}'", &*s);21Ok(s.len() as u32)22}2324fn multi_string(25&mut self,26memory: &mut GuestMemory<'_>,27a: GuestPtr<str>,28b: GuestPtr<str>,29c: GuestPtr<str>,30) -> Result<u32, types::Errno> {31let sa = memory32.as_str(a)33.expect("A should be valid string")34.expect("expected non-shared memory");35let sb = memory36.as_str(b)37.expect("B should be valid string")38.expect("expected non-shared memory");39let sc = memory40.as_str(c)41.expect("C should be valid string")42.expect("expected non-shared memory");43let total_len = sa.len() + sb.len() + sc.len();44println!(45"len={}, a='{}', b='{}', c='{}'",46total_len, &*sa, &*sb, &*sc47);48Ok(total_len as u32)49}50}5152fn unicode_string_strategy() -> impl Strategy<Value = String> {53"\\p{Greek}{1,256}"54}55fn ascii_string_strategy() -> impl Strategy<Value = String> {56"[a-zA-Z0..9]{1,256}"57}5859#[derive(Debug)]60struct HelloStringExercise {61test_word: String,62string_ptr_loc: MemArea,63return_ptr_loc: MemArea,64}6566impl HelloStringExercise {67pub fn strat() -> BoxedStrategy<Self> {68(unicode_string_strategy(),)69.prop_flat_map(|(test_word,)| {70(71Just(test_word.clone()),72HostMemory::mem_area_strat(test_word.len() as u32),73HostMemory::mem_area_strat(4),74)75})76.prop_map(|(test_word, string_ptr_loc, return_ptr_loc)| Self {77test_word,78string_ptr_loc,79return_ptr_loc,80})81.prop_filter("non-overlapping pointers", |e| {82MemArea::non_overlapping_set(&[e.string_ptr_loc, e.return_ptr_loc])83})84.boxed()85}8687pub fn test(&self) {88let mut ctx = WasiCtx::new();89let mut host_memory = HostMemory::new();90let mut memory = host_memory.guest_memory();9192// Populate string in guest's memory93let ptr = GuestPtr::<str>::new((self.string_ptr_loc.ptr, self.test_word.len() as u32));94for (slot, byte) in ptr.as_bytes().iter().zip(self.test_word.bytes()) {95memory96.write(slot.expect("should be valid pointer"), byte)97.expect("failed to write");98}99100let res = strings::hello_string(101&mut ctx,102&mut memory,103self.string_ptr_loc.ptr as i32,104self.test_word.len() as i32,105self.return_ptr_loc.ptr as i32,106)107.unwrap();108assert_eq!(res, types::Errno::Ok as i32, "hello string errno");109110let given = memory111.read(GuestPtr::<u32>::new(self.return_ptr_loc.ptr))112.expect("deref ptr to return value");113assert_eq!(self.test_word.len() as u32, given);114}115}116proptest! {117#[test]118fn hello_string(e in HelloStringExercise::strat()) {119e.test()120}121}122123#[derive(Debug)]124struct MultiStringExercise {125a: String,126b: String,127c: String,128sa_ptr_loc: MemArea,129sb_ptr_loc: MemArea,130sc_ptr_loc: MemArea,131return_ptr_loc: MemArea,132}133134impl MultiStringExercise {135pub fn strat() -> BoxedStrategy<Self> {136(137unicode_string_strategy(),138unicode_string_strategy(),139unicode_string_strategy(),140HostMemory::mem_area_strat(4),141)142.prop_flat_map(|(a, b, c, return_ptr_loc)| {143(144Just(a.clone()),145Just(b.clone()),146Just(c.clone()),147HostMemory::byte_slice_strat(148a.len() as u32,1491,150&MemAreas::from([return_ptr_loc]),151),152Just(return_ptr_loc),153)154})155.prop_flat_map(|(a, b, c, sa_ptr_loc, return_ptr_loc)| {156(157Just(a.clone()),158Just(b.clone()),159Just(c.clone()),160Just(sa_ptr_loc),161HostMemory::byte_slice_strat(162b.len() as u32,1631,164&MemAreas::from([sa_ptr_loc, return_ptr_loc]),165),166Just(return_ptr_loc),167)168})169.prop_flat_map(|(a, b, c, sa_ptr_loc, sb_ptr_loc, return_ptr_loc)| {170(171Just(a.clone()),172Just(b.clone()),173Just(c.clone()),174Just(sa_ptr_loc),175Just(sb_ptr_loc),176HostMemory::byte_slice_strat(177c.len() as u32,1781,179&MemAreas::from([sa_ptr_loc, sb_ptr_loc, return_ptr_loc]),180),181Just(return_ptr_loc),182)183})184.prop_map(185|(a, b, c, sa_ptr_loc, sb_ptr_loc, sc_ptr_loc, return_ptr_loc)| {186MultiStringExercise {187a,188b,189c,190sa_ptr_loc,191sb_ptr_loc,192sc_ptr_loc,193return_ptr_loc,194}195},196)197.boxed()198}199200pub fn test(&self) {201let mut ctx = WasiCtx::new();202let mut host_memory = HostMemory::new();203let mut memory = host_memory.guest_memory();204205let mut write_string = |val: &str, loc: MemArea| {206let ptr = GuestPtr::<str>::new((loc.ptr, val.len() as u32));207for (slot, byte) in ptr.as_bytes().iter().zip(val.bytes()) {208memory209.write(slot.expect("should be valid pointer"), byte)210.expect("failed to write");211}212};213214write_string(&self.a, self.sa_ptr_loc);215write_string(&self.b, self.sb_ptr_loc);216write_string(&self.c, self.sc_ptr_loc);217218let res = strings::multi_string(219&mut ctx,220&mut memory,221self.sa_ptr_loc.ptr as i32,222self.a.len() as i32,223self.sb_ptr_loc.ptr as i32,224self.b.len() as i32,225self.sc_ptr_loc.ptr as i32,226self.c.len() as i32,227self.return_ptr_loc.ptr as i32,228)229.unwrap();230assert_eq!(res, types::Errno::Ok as i32, "multi string errno");231232let given = memory233.read(GuestPtr::<u32>::new(self.return_ptr_loc.ptr))234.expect("deref ptr to return value");235assert_eq!((self.a.len() + self.b.len() + self.c.len()) as u32, given);236}237}238proptest! {239#[test]240fn multi_string(e in MultiStringExercise::strat()) {241e.test()242}243}244245#[derive(Debug)]246struct OverlappingStringExercise {247a: String,248sa_ptr_loc: MemArea,249offset_b: u32,250offset_c: u32,251return_ptr_loc: MemArea,252}253254impl OverlappingStringExercise {255pub fn strat() -> BoxedStrategy<Self> {256// using ascii so we can window into it without worrying about codepoints257(ascii_string_strategy(), HostMemory::mem_area_strat(4))258.prop_flat_map(|(a, return_ptr_loc)| {259(260Just(a.clone()),261HostMemory::mem_area_strat(a.len() as u32),2620..(a.len() as u32),2630..(a.len() as u32),264Just(return_ptr_loc),265)266})267.prop_map(|(a, sa_ptr_loc, offset_b, offset_c, return_ptr_loc)| Self {268a,269sa_ptr_loc,270offset_b,271offset_c,272return_ptr_loc,273})274.prop_filter("non-overlapping pointers", |e| {275MemArea::non_overlapping_set(&[e.sa_ptr_loc, e.return_ptr_loc])276})277.boxed()278}279280pub fn test(&self) {281let mut ctx = WasiCtx::new();282let mut host_memory = HostMemory::new();283let mut memory = host_memory.guest_memory();284285let mut write_string = |val: &str, loc: MemArea| {286let ptr = GuestPtr::<str>::new((loc.ptr, val.len() as u32));287for (slot, byte) in ptr.as_bytes().iter().zip(val.bytes()) {288memory289.write(slot.expect("should be valid pointer"), byte)290.expect("failed to write");291}292};293294write_string(&self.a, self.sa_ptr_loc);295296let a_len = self.a.as_bytes().len() as i32;297let res = strings::multi_string(298&mut ctx,299&mut memory,300self.sa_ptr_loc.ptr as i32,301a_len,302(self.sa_ptr_loc.ptr + self.offset_b) as i32,303a_len - self.offset_b as i32,304(self.sa_ptr_loc.ptr + self.offset_c) as i32,305a_len - self.offset_c as i32,306self.return_ptr_loc.ptr as i32,307)308.unwrap();309assert_eq!(res, types::Errno::Ok as i32, "multi string errno");310311let given = memory312.read(GuestPtr::<u32>::new(self.return_ptr_loc.ptr))313.expect("deref ptr to return value");314assert_eq!(315((3 * a_len) - (self.offset_b as i32 + self.offset_c as i32)) as u32,316given317);318}319}320321proptest! {322#[test]323fn overlapping_string(e in OverlappingStringExercise::strat()) {324e.test()325}326}327328329