diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/common/mod.rs | 77 | ||||
-rw-r--r-- | tests/rand_fail.rs | 9 |
2 files changed, 67 insertions, 19 deletions
diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 7a7e42a..2fd7bbb 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -32,15 +32,19 @@ impl std::ops::Deref for FileContentEmul { fn deref(&self) -> &Self::Target {&self.0} } -type FailGen = std::iter::Iterator<Item = bool>; +pub trait FailGen { + fn next_fail(&self) -> bool; +} /// Emulate the a virtual file handle. -pub struct WALFileEmul { +pub struct WALFileEmul<G: FailGen> { file: Rc<FileContentEmul>, + fgen: Rc<G> } -impl WALFile for WALFileEmul { +impl<G: FailGen> WALFile for WALFileEmul<G> { fn allocate(&self, offset: WALPos, length: usize) -> Result<(), ()> { + if self.fgen.next_fail() { return Err(()) } let offset = offset as usize; if offset + length > self.file.borrow().len() { self.file.borrow_mut().resize(offset + length, 0) @@ -50,17 +54,20 @@ impl WALFile for WALFileEmul { } fn truncate(&self, length: usize) -> Result<(), ()> { + if self.fgen.next_fail() { return Err(()) } self.file.borrow_mut().resize(length, 0); Ok(()) } fn write(&self, offset: WALPos, data: WALBytes) -> Result<(), ()> { + if self.fgen.next_fail() { return Err(()) } let offset = offset as usize; &self.file.borrow_mut()[offset..offset + data.len()].copy_from_slice(&data); Ok(()) } fn read(&self, offset: WALPos, length: usize) -> Result<Option<WALBytes>, ()> { + if self.fgen.next_fail() { return Err(()) } let offset = offset as usize; let file = self.file.borrow(); if offset + length > file.len() { Ok(None) } @@ -70,7 +77,7 @@ impl WALFile for WALFileEmul { } } -pub struct WALStoreEmulState{ +pub struct WALStoreEmulState { files: HashMap<String, Rc<FileContentEmul>>, } @@ -79,23 +86,32 @@ impl WALStoreEmulState { } /// Emulate the persistent storage state. -pub struct WALStoreEmul<'a>(&'a mut WALStoreEmulState); +pub struct WALStoreEmul<'a, G: FailGen> { + state: &'a mut WALStoreEmulState, + fgen: Rc<G> +} -impl<'a> WALStoreEmul<'a> { - pub fn new(state: &'a mut WALStoreEmulState) -> Self { - WALStoreEmul(state) +impl<'a, G: FailGen> WALStoreEmul<'a, G> { + pub fn new(state: &'a mut WALStoreEmulState, fail_gen: G) -> Self { + WALStoreEmul { state, fgen: Rc::new(fail_gen) } } } -impl<'a> WALStore for WALStoreEmul<'a> { +impl<'a, G: 'static + FailGen> WALStore for WALStoreEmul<'a, G> { type FileNameIter = std::vec::IntoIter<String>; fn open_file(&mut self, filename: &str, touch: bool) -> Result<Box<dyn WALFile>, ()> { - match self.0.files.entry(filename.to_string()) { - Entry::Occupied(e) => Ok(Box::new(WALFileEmul { file: e.get().clone() })), + if self.fgen.next_fail() { return Err(()) } + match self.state.files.entry(filename.to_string()) { + Entry::Occupied(e) => Ok(Box::new(WALFileEmul { + file: e.get().clone(), + fgen: self.fgen.clone() + })), Entry::Vacant(e) => if touch { - Ok(Box::new( - WALFileEmul { file: e.insert(Rc::new(FileContentEmul::new())).clone() })) + Ok(Box::new(WALFileEmul { + file: e.insert(Rc::new(FileContentEmul::new())).clone(), + fgen: self.fgen.clone() + })) } else { Err(()) } @@ -103,19 +119,50 @@ impl<'a> WALStore for WALStoreEmul<'a> { } fn remove_file(&mut self, filename: &str) -> Result<(), ()> { - self.0.files.remove(filename).ok_or(()).and_then(|_| Ok(())) + if self.fgen.next_fail() { return Err(()) } + self.state.files.remove(filename).ok_or(()).and_then(|_| Ok(())) } fn enumerate_files(&self) -> Result<Self::FileNameIter, ()> { + if self.fgen.next_fail() { return Err(()) } let mut logfiles = Vec::new(); - for (fname, _) in self.0.files.iter() { + for (fname, _) in self.state.files.iter() { logfiles.push(fname.clone()) } Ok(logfiles.into_iter()) } fn apply_payload(&mut self, payload: WALBytes) -> Result<(), ()> { + if self.fgen.next_fail() { return Err(()) } println!("apply_payload(payload={})", std::str::from_utf8(&payload).unwrap()); Ok(()) } } + +pub struct SingleFailGen { + cnt: std::cell::Cell<usize>, + fail_point: usize +} + +impl SingleFailGen { + pub fn new(fail_point: usize) -> Self { + SingleFailGen { + cnt: std::cell::Cell::new(0), + fail_point + } + } +} + +impl FailGen for SingleFailGen { + fn next_fail(&self) -> bool { + let c = self.cnt.get(); + self.cnt.set(c + 1); + c == self.fail_point + } +} + +pub struct ZeroFailGen; + +impl FailGen for ZeroFailGen { + fn next_fail(&self) -> bool { false } +} diff --git a/tests/rand_fail.rs b/tests/rand_fail.rs index 7499694..b4e30ac 100644 --- a/tests/rand_fail.rs +++ b/tests/rand_fail.rs @@ -1,11 +1,11 @@ #[cfg(test)] extern crate growthring; -use growthring::wal::{WALLoader, WALWriter, WALRingId, WALBytes}; +use growthring::wal::{WALLoader, WALWriter, WALStore, WALRingId, WALBytes}; mod common; -fn test(records: Vec<String>, wal: &mut WALWriter<common::WALStoreEmul>) -> Box<[WALRingId]> { +fn test<S: WALStore>(records: Vec<String>, wal: &mut WALWriter<S>) -> Box<[WALRingId]> { let records: Vec<WALBytes> = records.into_iter().map(|s| s.into_bytes().into_boxed_slice()).collect(); let ret = wal.grow(&records).unwrap(); for ring_id in ret.iter() { @@ -16,10 +16,11 @@ fn test(records: Vec<String>, wal: &mut WALWriter<common::WALStoreEmul>) -> Box< #[test] fn test_rand_fail() { + let fgen = common::SingleFailGen::new(100); let mut state = common::WALStoreEmulState::new(); - let mut wal = WALLoader::new(common::WALStoreEmul::new(&mut state), 9, 8, 1000).recover().unwrap(); + let mut wal = WALLoader::new(common::WALStoreEmul::new(&mut state, fgen), 9, 8, 1000).recover().unwrap(); for _ in 0..3 { test(["hi", "hello", "lol"].iter().map(|s| s.to_string()).collect::<Vec<String>>(), &mut wal); } - let mut wal = WALLoader::new(common::WALStoreEmul::new(&mut state), 9, 8, 1000).recover().unwrap(); + WALLoader::new(common::WALStoreEmul::new(&mut state, common::ZeroFailGen), 9, 8, 1000).recover().unwrap(); } |