#[macro_use]
extern crate proptest;
extern crate nbd;
extern crate pipe;
extern crate rand;
extern crate readwrite;
use rand::prng::XorShiftRng;
use rand::{RngCore, SeedableRng};
use proptest::prelude::{prop, ProptestConfig, Strategy};
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
use readwrite::ReadWrite;
#[derive(Debug, Eq, PartialEq)]
enum Action {
Seek(u64),
Write(usize),
ReadAndCheck(usize),
}
const SS: u64 = 1024 * 1024;
prop_compose! {
fn biased_size()(x in 0..65536usize, y in 0..3u8) -> usize {
if y == 0 {
x
} else {
x % 2048
}
}
}
fn gen_action() -> impl Strategy<Value = Action> {
prop_oneof! {
(0..SS).prop_map(Action::Seek),
biased_size().prop_map(Action::Write),
biased_size().prop_map(Action::ReadAndCheck),
}
}
proptest! {
#![proptest_config(ProptestConfig {
cases: 500,
.. ProptestConfig::default()
})]
#[test]
fn fuzz_roundtrip(script in prop::collection::vec(gen_action(),3..12)) {
let seed = [4u8;16];
let mut r = XorShiftRng::from_seed(seed);
let mut buf = vec![0;65536];
let mut buf2 = vec![0;65536];
let backing_storage1 = vec![0u8;SS as usize];
let backing_storage2 = vec![0u8;SS as usize];
let mut c1 = Cursor::new(backing_storage1);
let mut c2 = Cursor::new(backing_storage2);
let (r1,w1) = pipe::pipe();
let (r2,w2) = pipe::pipe();
let (s1,s2) = (ReadWrite::new(r1,w2), ReadWrite::new(r2,w1));
let h = std::thread::spawn(move || {
let _ = nbd::server::transmission(s2, &mut c2);
c2
});
let mut c2 = nbd::client::NbdClient::new(s1, &nbd::Export{size : SS,..Default::default()});
for i in script {
match i {
Action::Seek(pos) => {
c1.seek(SeekFrom::Start(pos)).unwrap();
c2.seek(SeekFrom::Start(pos)).unwrap();
},
Action::Write(mut sz) => {
if sz > (SS - c1.position()) as usize {
sz = (SS - c1.position()) as usize;
}
let bufview = &mut buf[0..sz];
r.fill_bytes(bufview);
c1.write_all(bufview).unwrap();
c2.write_all(bufview).unwrap();
},
Action::ReadAndCheck(sz) => {
let bufview = &mut buf[0..sz];
let bufview2 = &mut buf2[0..sz];
let ret1 = c1.read(bufview).unwrap();
let ret2 = c2.read(bufview2).unwrap();
assert!(ret1 == ret2);
assert!(bufview[0..ret1] == bufview2[0..ret2]);
},
}
}
drop(c2);
let backing_storage1 = c1.into_inner();
let backing_storage2 = h.join().unwrap().into_inner();
assert!(backing_storage1 == backing_storage2);
}
}