use scoped_pool::Pool;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::io::Write;
use util::test::gen_random_block;
use {Storage, Cache, ContentId, VolumeMetadata, VolumeName, Snapshot, BlockIndex};
#[derive(Debug, Clone)]
pub struct MockStorage {
inner: Arc<RwLock<InnerMockStorage>>
}
#[derive(Default, Clone, Debug)]
struct InnerMockStorage {
volumes: HashMap<VolumeName, Snapshot>,
chunks: HashMap<ContentId, Vec<u8>>
}
impl MockStorage {
pub fn new() -> Self {
MockStorage {
inner: Arc::new(RwLock::new(InnerMockStorage::default()))
}
}
}
impl Storage for MockStorage {
fn snapshot(&self, volume: &VolumeName, snapshot: Snapshot) -> ::Result<()> {
self.inner.write().unwrap().snapshot(volume, snapshot)
}
fn get_snapshot(&self, name: &VolumeName) -> ::Result<Snapshot> {
self.inner.read().unwrap().get_snapshot(name)
}
fn get_metadata(&self, volume: &VolumeName) -> ::Result<VolumeMetadata> {
self.inner.read().unwrap().get_metadata(volume)
}
fn create(&self, id: ContentId, data: &[u8]) -> ::Result<()> {
self.inner.write().unwrap().create(id, data)
}
fn delete(&self, id: ContentId) -> ::Result<()> {
self.inner.write().unwrap().delete(id)
}
}
impl Cache for MockStorage {
fn read(&self, id: ContentId, buf: &mut [u8]) -> ::Result<()> {
self.inner.read().unwrap().read(id, buf)
}
}
impl InnerMockStorage {
fn snapshot(&mut self, volume: &VolumeName, snapshot: Snapshot) -> ::Result<()> {
self.volumes.entry(volume.clone())
.or_insert(snapshot);
Ok(())
}
fn get_snapshot(&self, name: &VolumeName) -> ::Result<Snapshot> {
self.volumes.get(name).cloned().ok_or(::Error::NotFound)
}
fn get_metadata(&self, volume: &VolumeName) -> ::Result<VolumeMetadata> {
self.volumes.get(&volume).map(|snap| &snap.metadata).cloned().ok_or(::Error::NotFound)
}
fn read(&self, id: ContentId, mut buf: &mut [u8]) -> ::Result<()> {
let data = try!(self.chunks.get(&id).ok_or(::Error::NotFound));
assert_eq!(data.len(), buf.len());
buf.write(data).unwrap();
Ok(())
}
fn create(&mut self, id: ContentId, data: &[u8]) -> ::Result<()> {
self.chunks.entry(id).or_insert_with(|| data.into());
Ok(())
}
fn delete(&mut self, id: ContentId) -> ::Result<()> {
self.chunks.remove(&id)
.ok_or(::Error::NotFound)
.map(|_| ())
}
}
pub struct StorageFuzzer<S> {
storage: S
}
impl<S: Storage> StorageFuzzer<S> {
pub fn new(storage: S) -> StorageFuzzer<S> {
StorageFuzzer {
storage: storage
}
}
pub fn run(&self, magnitude: usize) {
self.check_properties(magnitude);
self.check_against_mock(magnitude);
}
pub fn check_properties(&self, magnitude: usize) {
let num_objects = magnitude * 100;
let chunk_size = magnitude * 100;
let objects = (0..num_objects)
.map(|_| gen_random_block(chunk_size))
.collect::<HashMap<_, _>>();
self.storage.get_metadata(&VolumeName("random".to_string()))
.unwrap_err().assert_not_found();
self.storage.get_snapshot(&VolumeName("random".to_string()))
.unwrap_err().assert_not_found();
let pool = Pool::new(objects.len());
pool.scoped(|scope| {
for (id, data) in objects.clone() {
scope.execute(move || {
let mut buffer = vec![0; chunk_size];
self.storage.create(id, &data).unwrap();
self.storage.read(id, &mut buffer).unwrap();
assert_eq!(data, buffer);
})
}
});
pool.shutdown();
let snapshot = Snapshot {
metadata: VolumeMetadata { size: num_objects },
blocks: objects.keys().enumerate()
.map(|(index, &id)| (BlockIndex(index), id)).collect()
};
let name = VolumeName("some_volume".to_string());
self.storage.snapshot(&name, snapshot.clone()).unwrap();
let metadata = self.storage.get_metadata(&name).unwrap();
assert_eq!(metadata, snapshot.metadata);
}
pub fn check_against_mock(&self, _magnitude: usize) {
}
}
#[cfg(test)]
mod test {
use mock::{MockStorage, StorageFuzzer};
use {Storage, Cache, ContentId};
#[test]
fn fuzz_mock_storage() {
let storage = MockStorage::new();
let fuzzer = StorageFuzzer { storage: storage };
fuzzer.run(2);
}
#[test]
fn test_mock_storage_create_read() {
let data = &[1, 2, 3, 4, 5, 6, 7, 8];
let id = ContentId::hash(data);
let storage = MockStorage::new();
let mut buf = vec![0; data.len()];
storage.create(id, data).unwrap();
storage.read(id, &mut buf).unwrap();
assert_eq!(&buf, data);
let data = &[8, 7, 6, 5, 5, 6, 7, 8];
let id = ContentId::hash(data);
let mut buf = vec![0; data.len()];
storage.create(id, data).unwrap();
storage.read(id, &mut buf).unwrap();
assert_eq!(&buf, data);
storage.create(id, &[5, 5, 5, 5, 5, 5, 5, 5]).unwrap();
storage.read(id, &mut buf).unwrap();
assert_eq!(&buf, data);
}
}