use std::collections::BTreeMap;
use std::fs;
use std::path::Path;
use conserve::monitor::test::TestMonitor;
use proptest::prelude::*;
use proptest_derive::Arbitrary;
use tempfile::TempDir;
use conserve::test_fixtures::*;
use conserve::*;
#[derive(Debug, Clone, Arbitrary)]
enum TreeChange {
AddFile,
Backup,
RemoveFile(usize),
ChangeFile(usize),
Restore(usize),
}
fn backup_sequential_changes(changes: &[TreeChange]) {
use TreeChange::*;
let tf = TreeFixture::new();
let archive = ScratchArchive::new();
let mut live_files: Vec<String> = Vec::new();
let mut live_contents: BTreeMap<String, Vec<u8>> = BTreeMap::new();
let mut next_file = 0;
let mut backup_contents: Vec<TempDir> = Vec::new();
for (i, c) in changes.iter().enumerate() {
println!("{i}: {c:?}");
match c {
AddFile => {
let content = format!("initial content of {next_file}").into_bytes();
let name = next_file.to_string();
tf.create_file_with_contents(&name, &content);
live_files.push(name.clone());
live_contents.insert(name, content);
next_file += 1;
}
ChangeFile(j) => {
if !live_files.is_empty() {
let j = j % live_files.len();
let name = &live_files[j];
let content = format!("changed content of {j} from step {i}").into_bytes();
fs::write(tf.path().join(name), content).unwrap();
}
}
RemoveFile(j) => {
if !live_files.is_empty() {
let j = j % live_files.len();
let name = live_files.remove(j);
live_contents.remove(&name);
fs::remove_file(tf.path().join(&name)).unwrap();
}
}
Backup => {
std::thread::sleep(std::time::Duration::from_millis(10));
let options = BackupOptions {
max_entries_per_hunk: 3,
..BackupOptions::default()
};
backup(&archive, tf.path(), &options, TestMonitor::arc()).unwrap();
let snapshot = TempDir::new().unwrap();
cp_r::CopyOptions::default()
.copy_tree(tf.path(), snapshot.path())
.unwrap();
backup_contents.push(snapshot);
}
Restore(i_version) => {
if !backup_contents.is_empty() {
let version = i_version % backup_contents.len();
check_restore_against_snapshot(
&archive,
BandId::new(&[version as u32]),
backup_contents[version].path(),
);
}
}
}
}
for (i, snapshot) in backup_contents.iter().enumerate() {
check_restore_against_snapshot(&archive, BandId::new(&[i as u32]), snapshot.path())
}
println!(">> done!");
}
fn check_restore_against_snapshot(archive: &Archive, band_id: BandId, snapshot: &Path) {
let restore_dir = tempfile::tempdir().unwrap();
let options = RestoreOptions {
band_selection: BandSelectionPolicy::Specified(band_id),
..RestoreOptions::default()
};
restore(archive, restore_dir.path(), &options, TestMonitor::arc()).unwrap();
dir_assert::assert_paths(restore_dir.path(), snapshot).unwrap();
}
proptest! {
#[test]
#[ignore] fn changes(changes: Vec<TreeChange>) {
backup_sequential_changes(&changes);
}
}