#![allow(dead_code)]
use std::fs;
use std::io::{self, BufRead, Read, Seek, Write};
use std::path::Path;
use std::string::String;
use std::vec::Vec;
use crate::test_runner::{TestCaseError, TestCaseResult, Seed};
const SENTINEL: &'static str = "proptest-forkfile";
#[derive(Clone, Debug)]
pub(crate) struct Replay {
pub(crate) seed: Seed,
pub(crate) steps: Vec<TestCaseResult>,
}
impl Replay {
pub fn merge(&mut self, other: &Replay) {
if other.steps.len() > self.steps.len() {
let sl = self.steps.len();
self.steps.extend_from_slice(&other.steps[sl..]);
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum ReplayFileStatus {
InProgress(Replay),
Terminated(Replay),
Corrupt,
}
pub(crate) fn open_file(path: impl AsRef<Path>) -> io::Result<fs::File> {
fs::OpenOptions::new()
.read(true)
.append(true)
.create(true)
.truncate(false)
.open(path)
}
fn step_to_char(step: &TestCaseResult) -> char {
match *step {
Ok(_) => '+',
Err(TestCaseError::Reject(_)) => '!',
Err(TestCaseError::Fail(_)) => '-',
}
}
pub(crate) fn append(mut file: impl Write, step: &TestCaseResult)
-> io::Result<()> {
write!(file, "{}", step_to_char(step))
}
pub(crate) fn ping(mut file: impl Write) -> io::Result<()> {
write!(file, " ")
}
pub(crate) fn terminate(mut file: impl Write) -> io::Result<()> {
write!(file, ".")
}
impl Replay {
pub fn init_file(&self, mut file: impl Write) -> io::Result<()> {
writeln!(file, "{}", SENTINEL)?;
writeln!(file, "{}", self.seed.to_persistence())?;
let mut step_data = Vec::<u8>::new();
for step in &self.steps {
step_data.push(step_to_char(step) as u8);
}
file.write_all(&step_data)?;
Ok(())
}
pub fn complete(mut file: impl Write) -> io::Result<()> {
write!(file, ".")
}
pub fn parse_from(mut file: impl Read + Seek)
-> io::Result<ReplayFileStatus> {
file.seek(io::SeekFrom::Start(0))?;
let mut reader = io::BufReader::new(&mut file);
let mut line = String::new();
reader.read_line(&mut line)?;
if SENTINEL != line.trim() {
return Ok(ReplayFileStatus::Corrupt);
}
line.clear();
reader.read_line(&mut line)?;
let seed = match Seed::from_persistence(&line) {
Some(seed) => seed,
None => return Ok(ReplayFileStatus::Corrupt),
};
line.clear();
reader.read_line(&mut line)?;
let mut steps = Vec::new();
for ch in line.chars() {
match ch {
'+' => steps.push(Ok(())),
'-' => steps.push(Err(TestCaseError::fail(
"failed in other process"))),
'!' => steps.push(Err(TestCaseError::reject(
"rejected in other process"))),
'.' => return Ok(ReplayFileStatus::Terminated(
Replay { seed, steps })),
' ' => (),
_ => return Ok(ReplayFileStatus::Corrupt),
}
}
Ok(ReplayFileStatus::InProgress(Replay { seed, steps }))
}
}