#![deny(warnings)]
#![allow(dead_code)]
#[macro_use]
extern crate lazy_static;
extern crate partial_io;
extern crate quickcheck;
use std::io::{self, Write};
pub struct BuggyWrite<W> {
inner: W,
buf: Vec<u8>,
offset: usize,
}
impl<W: Write> BuggyWrite<W> {
pub fn new(inner: W) -> Self {
BuggyWrite {
inner,
buf: Vec::with_capacity(256),
offset: 0,
}
}
fn write_from_offset(&mut self) -> io::Result<()> {
while self.offset < self.buf.len() {
self.offset += self.inner.write(&self.buf[self.offset..])?;
}
Ok(())
}
fn reset_buffer(&mut self) {
unsafe {
self.buf.set_len(0);
}
self.offset = 0;
}
pub fn into_inner(self) -> W {
self.inner
}
}
impl<W: Write> Write for BuggyWrite<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if self.offset < self.buf.len() {
self.write_from_offset()?;
}
self.reset_buffer();
self.buf.extend_from_slice(buf);
self.write_from_offset()?;
Ok(self.buf.len())
}
fn flush(&mut self) -> io::Result<()> {
while self.offset < self.buf.len() {
self.write_from_offset()?;
}
self.reset_buffer();
self.inner.flush()
}
}
fn main() {
test::buggy_write();
}
mod test {
use std::io::{self, Write};
use quickcheck::{quickcheck, TestResult};
use partial_io::{GenInterrupted, PartialOp, PartialWithErrors, PartialWrite};
use super::*;
lazy_static! {
static ref HELLO_STR: Vec<u8> = "Hello".repeat(50).into_bytes();
static ref WORLD_STR: Vec<u8> = "World".repeat(40).into_bytes();
}
#[test]
fn test_buggy_write() {
buggy_write();
}
pub fn buggy_write() {
let partial = vec![
PartialOp::Err(io::ErrorKind::Interrupted),
PartialOp::Unlimited,
];
let (hello_res, world_res, flush_res, inner) = buggy_write_internal(partial);
assert_eq!(hello_res.unwrap_err().kind(), io::ErrorKind::Interrupted);
assert_eq!(world_res.unwrap(), 5 * 40);
assert_eq!(flush_res.unwrap(), ());
let mut expected = Vec::new();
expected.extend_from_slice(&HELLO_STR);
expected.extend_from_slice(&WORLD_STR);
assert_eq!(inner, expected);
}
#[test]
#[ignore]
fn test_quickcheck_buggy_write() {
quickcheck_buggy_write();
}
pub fn quickcheck_buggy_write() {
quickcheck(quickcheck_buggy_write2 as fn(PartialWithErrors<GenInterrupted>) -> TestResult);
}
fn quickcheck_buggy_write2(partial: PartialWithErrors<GenInterrupted>) -> TestResult {
let (hello_res, world_res, flush_res, inner) = buggy_write_internal(partial);
if flush_res.is_err() {
return TestResult::discard();
}
let mut expected = Vec::new();
if hello_res.is_ok() {
expected.extend_from_slice(&HELLO_STR);
}
if world_res.is_ok() {
expected.extend_from_slice(&WORLD_STR);
}
assert_eq!(inner, expected);
TestResult::passed()
}
fn buggy_write_internal<I>(
partial_iter: I,
) -> (
io::Result<usize>,
io::Result<usize>,
io::Result<()>,
Vec<u8>,
)
where
I: IntoIterator<Item = PartialOp> + 'static,
I::IntoIter: Send,
{
let inner = Vec::new();
let partial_writer = PartialWrite::new(inner, partial_iter);
let mut buggy_write = BuggyWrite::new(partial_writer);
let hello_res = buggy_write.write(&HELLO_STR);
let world_res = buggy_write.write(&WORLD_STR);
let flush_res = buggy_write.flush();
let inner = buggy_write.into_inner().into_inner();
(hello_res, world_res, flush_res, inner)
}
}