From 32a88299721393a5cbd3b369091de7cb77da349c Mon Sep 17 00:00:00 2001 From: Determinant Date: Wed, 10 Jun 2020 16:17:40 -0400 Subject: add failure generator in test --- tests/common/mod.rs | 77 ++++++++++++++++++++++++++++++++++++++++++----------- tests/rand_fail.rs | 9 ++++--- 2 files changed, 67 insertions(+), 19 deletions(-) (limited to 'tests') 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; +pub trait FailGen { + fn next_fail(&self) -> bool; +} /// Emulate the a virtual file handle. -pub struct WALFileEmul { +pub struct WALFileEmul { file: Rc, + fgen: Rc } -impl WALFile for WALFileEmul { +impl WALFile for WALFileEmul { 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, ()> { + 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>, } @@ -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 +} -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; fn open_file(&mut self, filename: &str, touch: bool) -> Result, ()> { - 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 { + 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, + 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, wal: &mut WALWriter) -> Box<[WALRingId]> { +fn test(records: Vec, wal: &mut WALWriter) -> Box<[WALRingId]> { let records: Vec = 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, wal: &mut WALWriter) -> 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::>(), &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(); } -- cgit v1.2.3-70-g09d2