use std::{
fs,
os::unix::{self, fs::PermissionsExt},
process::Command,
};
use anyhow::Result;
use tempfile::tempdir;
mod common;
use common::*;
#[test]
fn restore_src() -> Result<()> {
let backup_dir = tempdir()?;
let backup_path = backup_dir.path();
let working_dir = tempdir()?;
let working_path = working_dir.path();
cli_run(working_path, backup_path)?
.args(["init", "filesystem"])
.assert()
.success();
assert!(
Command::new("cp")
.args(&["-r", "src"])
.arg(working_path)
.status()?
.success()
);
cli_run(working_path, backup_path)?
.arg("backup")
.arg(working_path.join("src"))
.assert()
.success();
let restoreit = |args: &[&str]| {
let restore_run = cli_run(working_path, backup_path)
.unwrap()
.args(&["restore", "--delete", "--times", "--permissions", "LAST"])
.args(args)
.assert()
.success();
let restore_err = stderr(&restore_run).trim();
eprintln!("{}", restore_err);
let restore_output: Vec<_> = stdout(&restore_run)
.trim()
.lines()
.skip(3)
.filter(|l| !l.starts_with("A "))
.map(str::to_owned)
.collect();
restore_output
};
let prefix_working_path = |s: &str| -> String {
let (code, path) = s.split_at(2);
let prefixed = working_path.join(path);
format!("{}{}", code, prefixed.display())
};
let compare = |expected: &[&str], args: &[&str]| {
let expected = expected
.iter()
.map(|p| prefix_working_path(p))
.collect::<Vec<_>>();
let got = restoreit(args);
println!("Expected:\n{:#?}", expected);
println!("Got:\n{:#?}", got);
assert_eq!(expected, got);
};
compare(&[], &[]);
fs::remove_file(working_path.join("src/diff.rs"))?;
fs::File::create(working_path.join("src/aNewFile"))?;
fs::rename(
working_path.join("src/backend"),
working_path.join("src/wackend"),
)?;
fs::write(working_path.join("src/lib.rs"), "I want some butts!")?;
fs::set_permissions(
working_path.join("src/main.rs"),
fs::Permissions::from_mode(0o777),
)?;
compare(
&[
"- src/aNewFile",
"+ src/backend/",
"+ src/backend/backblaze.rs",
"+ src/backend/cache.rs",
"+ src/backend/filter.rs",
"+ src/backend/fs.rs",
"+ src/backend/memory.rs",
"+ src/backend/semaphored.rs",
"+ src/diff.rs",
"C src/lib.rs",
"P src/main.rs",
"- src/wackend/",
"- src/wackend/backblaze.rs",
"- src/wackend/cache.rs",
"- src/wackend/filter.rs",
"- src/wackend/fs.rs",
"- src/wackend/memory.rs",
"- src/wackend/semaphored.rs",
"T src/",
],
&[],
);
compare(&[], &[]);
fs::remove_file(working_path.join("src/ls.rs"))?;
unix::fs::symlink("/dev/null", working_path.join("src/ls.rs"))?;
compare(&["- src/ls.rs -> /dev/null", "+ src/ls.rs", "T src/"], &[]);
fs::remove_file(working_path.join("src/ls.rs"))?;
unix::fs::symlink("/dev/null", working_path.join("src/ls.rs"))?;
cli_run(working_path, backup_path)?
.arg("backup")
.arg(working_path.join("src"))
.assert()
.success();
fs::remove_file(working_path.join("src/ls.rs"))?;
unix::fs::symlink("/dev/urandom", working_path.join("src/ls.rs"))?;
compare(
&[
"- src/ls.rs -> /dev/urandom",
"+ src/ls.rs -> /dev/null",
"T src/",
],
&[],
);
let moved_to_path = &working_path.join("elsewhere");
let moved_to = moved_to_path.to_str().unwrap();
fs::rename(working_path.join("src"), moved_to)?;
compare(&["T elsewhere/ls.rs -> /dev/null"], &["--output", moved_to]);
fs::remove_dir_all(moved_to_path.join("backend"))?;
compare(
&[
"+ elsewhere/backend/",
"+ elsewhere/backend/backblaze.rs",
"+ elsewhere/backend/cache.rs",
"+ elsewhere/backend/filter.rs",
"+ elsewhere/backend/fs.rs",
"+ elsewhere/backend/memory.rs",
"+ elsewhere/backend/semaphored.rs",
"T elsewhere/",
],
&["-o", moved_to],
);
compare(&[], &["-o", moved_to]);
Ok(())
}
#[test]
fn restore_multipath() -> Result<()> {
let backup_dir = tempdir()?;
let backup_path = backup_dir.path();
let working_dir = tempdir()?;
let working_path = working_dir.path();
cli_run(working_path, backup_path)?
.args(["init", "filesystem"])
.assert()
.success();
assert!(
Command::new("cp")
.args(&["README.md", "LICENSE.txt"])
.arg(working_path)
.status()?
.success()
);
cli_run(working_path, backup_path)?
.arg("backup")
.arg(working_path.join("README.md"))
.arg(working_path.join("LICENSE.txt"))
.assert()
.success();
let restoreit = |args: &[&str]| {
let restore_run = cli_run(working_path, backup_path)
.unwrap()
.args(&["restore", "--delete", "--times", "--permissions", "LAST"])
.args(args)
.assert()
.success();
let restore_err = stderr(&restore_run).trim();
eprintln!("{}", restore_err);
let restore_output: Vec<_> = stdout(&restore_run)
.trim()
.lines()
.skip(3)
.filter(|l| !l.starts_with("A "))
.map(str::to_owned)
.collect();
restore_output
};
let prefix_working_path = |s: &str| -> String {
let (code, path) = s.split_at(2);
let prefixed = working_path.join(path);
format!("{}{}", code, prefixed.display())
};
let compare = |expected: &[&str], args: &[&str]| {
let expected = expected
.iter()
.map(|p| prefix_working_path(p))
.collect::<Vec<_>>();
let got = restoreit(args);
println!("Expected:\n{:#?}", expected);
println!("Got:\n{:#?}", got);
assert_eq!(expected, got);
};
compare(&[], &[]);
fs::write(working_path.join("LICENSE.txt"), "https://xkcd.com/225/")?;
fs::write(
working_path.join("README.md"),
"We spend eternity looking at pleasant moments—like today at the zoo. \
Isn't this a nice moment?",
)?;
compare(&["C LICENSE.txt", "C README.md"], &[]);
let out_path = &working_path.join("elsewhere");
let out = out_path.to_str().unwrap();
fs::create_dir(out_path)?;
compare(
&["+ elsewhere/LICENSE.txt", "+ elsewhere/README.md"],
&["--output", out],
);
Ok(())
}