diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/common/mod.rs | 156 |
1 files changed, 150 insertions, 6 deletions
diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 2fd7bbb..209e8af 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -3,11 +3,14 @@ extern crate growthring; use growthring::wal::{WALFile, WALStore, WALPos, WALBytes}; -use std::collections::{HashMap, hash_map::Entry}; +use indexmap::{IndexMap, map::Entry}; +use rand::Rng; +use std::collections::{HashMap, hash_map}; use std::cell::RefCell; use std::rc::Rc; +use std::convert::TryInto; +use std::collections::VecDeque; -/* thread_local! { //pub static RNG: RefCell<rand::rngs::StdRng> = RefCell::new(<rand::rngs::StdRng as rand::SeedableRng>::from_seed([0; 32])); pub static RNG: RefCell<rand::rngs::ThreadRng> = RefCell::new(rand::thread_rng()); @@ -16,10 +19,9 @@ thread_local! { pub fn gen_rand_letters(i: usize) -> String { //let mut rng = rand::thread_rng(); RNG.with(|rng| { - (0..i).map(|_| (rng.borrow_mut().gen::<u8>() % 26 + 'a' as u8) as char).collect() + (0..i).map(|_| (rng.borrow_mut().gen_range(0, 26) + 'a' as u8) as char).collect() }) } -*/ struct FileContentEmul(RefCell<Vec<u8>>); @@ -103,11 +105,11 @@ impl<'a, G: 'static + FailGen> WALStore for WALStoreEmul<'a, G> { fn open_file(&mut self, filename: &str, touch: bool) -> Result<Box<dyn WALFile>, ()> { if self.fgen.next_fail() { return Err(()) } match self.state.files.entry(filename.to_string()) { - Entry::Occupied(e) => Ok(Box::new(WALFileEmul { + hash_map::Entry::Occupied(e) => Ok(Box::new(WALFileEmul { file: e.get().clone(), fgen: self.fgen.clone() })), - Entry::Vacant(e) => if touch { + hash_map::Entry::Vacant(e) => if touch { Ok(Box::new(WALFileEmul { file: e.insert(Rc::new(FileContentEmul::new())).clone(), fgen: self.fgen.clone() @@ -166,3 +168,145 @@ pub struct ZeroFailGen; impl FailGen for ZeroFailGen { fn next_fail(&self) -> bool { false } } + +/// An ordered list of intervals: `(begin, end, color)*`. +pub struct PaintStrokes(Vec<(u32, u32, u32)>); + +impl PaintStrokes { + pub fn new() -> Self { PaintStrokes(Vec::new()) } + pub fn to_bytes(&self) -> WALBytes { + let mut res: Vec<u8> = Vec::new(); + let is = std::mem::size_of::<u32>(); + let len = self.0.len() as u32; + res.resize(is * (1 + 3 * self.0.len()), 0); + let mut rs = &mut res[..]; + &mut rs[..is].copy_from_slice(&len.to_le_bytes()); + rs = &mut rs[is..]; + for (s, e, c) in self.0.iter() { + &mut rs[..is].copy_from_slice(&s.to_le_bytes()); + &mut rs[is..is * 2].copy_from_slice(&e.to_le_bytes()); + &mut rs[is * 2..is * 3].copy_from_slice(&c.to_le_bytes()); + rs = &mut rs[is * 3..]; + } + res.into_boxed_slice() + } + + pub fn from_bytes(raw: &[u8]) -> Self { + assert!(raw.len() > 4); + assert!(raw.len() & 3 == 0); + let is = std::mem::size_of::<u32>(); + let (len_raw, mut rest) = raw.split_at(is); + let len = u32::from_le_bytes(len_raw.try_into().unwrap()); + let mut res = Vec::new(); + for _ in 0..len { + let (s_raw, rest1) = rest.split_at(is); + let (e_raw, rest2) = rest1.split_at(is); + let (c_raw, rest3) = rest2.split_at(is); + res.push((u32::from_le_bytes(s_raw.try_into().unwrap()), + u32::from_le_bytes(e_raw.try_into().unwrap()), + u32::from_le_bytes(c_raw.try_into().unwrap()))); + rest = rest3 + } + PaintStrokes(res) + } + + pub fn stroke(&mut self, start: u32, end: u32, color: u32) { + self.0.push((start, end, color)) + } + + pub fn into_vec(self) -> Vec<(u32, u32, u32)> { self.0 } +} + +#[test] +fn test_paint_strokes() { + let mut p = PaintStrokes::new(); + for i in 0..3 { + p.stroke(i, i + 3, i + 10) + } + let pr = p.to_bytes(); + for ((s, e, c), i) in PaintStrokes::from_bytes(&pr) + .into_vec().into_iter().zip(0..) { + assert_eq!(s, i); + assert_eq!(e, i + 3); + assert_eq!(c, i + 10); + } +} + +type CanvasTrace = Vec<(u32, u32)>; + +pub struct Canvas { + queue: IndexMap<u32, VecDeque<u32>>, + canvas: Box<[u32]> +} + +impl Canvas { + pub fn new(size: usize) -> Self { + let mut canvas = Vec::new(); + // fill the backgroudn color 0 + canvas.resize(size, 0); + let canvas = canvas.into_boxed_slice(); + Canvas { + queue: IndexMap::new(), + canvas + } + } + + fn get_queued(&mut self, pos: u32) -> &mut VecDeque<u32> { + match self.queue.entry(pos) { + Entry::Occupied(e) => e.into_mut(), + Entry::Vacant(e) => e.insert(VecDeque::new()) + } + } + + pub fn prepaint(&mut self, strokes: &PaintStrokes) { + for (s, e, c) in strokes.0.iter() { + for i in *s..*e { + self.get_queued(i).push_back(*c) + } + } + } + + // TODO: allow customized scheduler + pub fn rand_paint<R: rand::Rng>(&mut self, rng: &mut R) -> u32 { + println!("{}", self.queue.len()); + let idx = rng.gen_range(0, self.queue.len()); + let (pos, _) = self.queue.get_index_mut(idx).unwrap(); + let pos = *pos; + self.paint(pos); + pos + } + + pub fn paint(&mut self, pos: u32) { + let q = self.queue.get_mut(&pos).unwrap(); + self.canvas[pos as usize] = q.pop_front().unwrap(); + if q.is_empty() { self.queue.remove(&pos); } + } + + pub fn is_same(&self, other: &Canvas) -> bool { + self.canvas.cmp(&other.canvas) == std::cmp::Ordering::Equal + } +} + +#[test] +fn test_canvas() { + let mut canvas1 = Canvas::new(10); + let mut canvas2 = Canvas::new(10); + let canvas3 = Canvas::new(11); + let mut s1 = PaintStrokes::new(); + s1.stroke(0, 1, 1); + let mut s2 = PaintStrokes::new(); + s2.stroke(1, 2, 2); + assert!(canvas1.is_same(&canvas2)); + assert!(!canvas2.is_same(&canvas3)); + canvas1.prepaint(&s1); + canvas1.prepaint(&s2); + canvas2.prepaint(&s1); + canvas2.prepaint(&s2); + assert!(canvas1.is_same(&canvas2)); + RNG.with(|rng| canvas1.rand_paint(&mut *rng.borrow_mut())); + assert!(!canvas1.is_same(&canvas2)); + RNG.with(|rng| canvas1.rand_paint(&mut *rng.borrow_mut())); + RNG.with(|rng| canvas2.rand_paint(&mut *rng.borrow_mut())); + RNG.with(|rng| canvas2.rand_paint(&mut *rng.borrow_mut())); + assert!(canvas1.is_same(&canvas2)); +} |