8000 test(fuzz): retry an interrupted crash copy and report why a copy failed (#564) by adityamukho · Pull Request #567 · project-minigraf/minigraf · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
49 changes: 49 additions & 0 deletions tests/model_based_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,52 @@ proptest! {
run(&steps, Checks::All)?;
}
}

/// The #564 nightly input: one transact, then a crash copy after every step.
/// Under libFuzzer one copy failed with an unreported error; `crash_copy` now
/// retries `Interrupted` and names the file and error kind.
#[test]
fn repeated_crash_copies_match_model() {
let probe = Probe {
as_of: 0,
at: 3,
e: 3,
a: 0,
v: V::Int(i64::MIN),
pick: 15,
};
let mut steps = vec![Step {
op: Op::Exec(Stmt::Transact {
window: Window {
vf: None,
vt: Some(3),
},
facts: vec![FactSpec {
e: 3,
a: 0,
v: V::Int(i64::MIN),
window: Window::default(),
}],
}),
probe: probe.clone(),
}];
for _ in 0..8 {
steps.push(Step {
op: Op::Crash(OpenMode::Default),
probe: probe.clone(),
});
}
run(&steps, Checks::All).expect("crash copies match the model");
}

/// A failed copy says which file and why.
#[test]
fn crash_copy_names_the_failing_file() {
let dir = tempfile::tempdir().unwrap();
let missing = dir.path().join("missing.graph");
let err = crash_copy(&missing, &dir.path().join("to.graph"), 7).unwrap_err();
let msg = err.to_string();
assert!(msg.contains("step 7"), "message names the step");
assert!(msg.contains("database file"), "message names the file");
assert!(msg.contains("NotFound"), "message carries the error kind");
}
23 changes: 17 additions & 6 deletions tests/reference_model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,20 +737,31 @@ pub fn open(path: &Path, mode: Open 8000 Mode) -> Result<Minigraf, TestCaseError> {
/// Copy the database file and its WAL as they are on disk right now. On
/// Windows, where locks are mandatory, the caller closes the handle first,
/// which turns the step into a plain reopen.
///
/// `fs::copy` can fail with `Interrupted` under libFuzzer, whose `-timeout`
/// alarm signal is installed without `SA_RESTART`, so the copy retries that.
/// Any other error names the file and the error kind (#564).
pub fn crash_copy(from: &Path, to: &Path, step: usize) -> Result<(), TestCaseError> {
let wal = |p: &Path| {
let mut s = p.as_os_str().to_owned();
s.push(".wal");
std::path::PathBuf::from(s)
};
let copy = |a: &Path, b: &Path| {
std::fs::copy(a, b)
.map(|_| ())
.map_err(|_| TestCaseError::fail(format!("step {step}: copy failed")))
let copy = |a: &Path, b: &Path, what: &str| loop {
match std::fs::copy(a, b) {
Ok(_) => return Ok(()),
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(e) => {
return Err(TestCaseError::fail(format!(
"step {step}: copy of the {what} failed ({:?})",
e.kind()
)));
}
}
};
copy(from, to)?;
copy(from, to, "database file")?;
if wal(from).exists() {
copy(&wal(from), &wal(to))?;
copy(&wal(from), &wal(to), "WAL")?;
}
Ok(())
}
Expand Down
Loading
0