use std::path::PathBuf;
use std::process::Command;
use assert_cmd::prelude::*;
use assert_fs::prelude::*;
use assert_fs::TempDir;
use predicates::prelude::*;
use url::Url;
use conserve::test_fixtures::{ScratchArchive, TreeFixture};
mod backup;
mod delete;
mod diff;
mod exclude;
#[cfg(unix)]
mod unix_permissions;
mod validate;
mod versions;
fn run_conserve() -> Command {
Command::cargo_bin("conserve").expect("locate conserve binary")
}
#[test]
fn no_args() {
run_conserve()
.assert()
.failure()
.stdout(predicate::str::is_empty())
.stderr(predicate::str::contains("Usage:"));
}
#[test]
fn help() {
run_conserve()
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("A robust backup tool"))
.stdout(predicate::str::contains(
"Copy source directory into an archive",
))
.stderr(predicate::str::is_empty());
}
#[test]
fn clean_error_on_non_archive() {
let testdir = TempDir::new().unwrap();
run_conserve()
.arg("backup")
.arg(testdir.path())
.arg(".")
.assert()
.failure()
.stdout(predicate::str::contains("Not a Conserve archive"));
}
#[test]
fn basic_backup() {
let testdir = TempDir::new().unwrap();
let arch_dir = testdir.path().join("a");
run_conserve()
.arg("init")
.arg(&arch_dir)
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(predicate::str::starts_with("Created new archive"));
run_conserve()
.arg("versions")
.arg(&arch_dir)
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(predicate::str::is_empty());
let src: PathBuf = "./testdata/tree/minimal".into();
assert!(src.is_dir());
run_conserve()
.args(["ls", "--source"])
.arg(&src)
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(
"/\n\
/hello\n\
/subdir\n\
/subdir/subfile\n",
);
run_conserve()
.args(["size", "-s"])
.arg(&src)
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout("0 MB\n");
run_conserve()
.arg("backup")
.arg(&arch_dir)
.arg(&src)
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(predicate::str::starts_with("Backup complete.\n"));
run_conserve()
.args(["size"])
.arg(&arch_dir)
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout("0 MB\n");
run_conserve()
.args(["versions", "--short"])
.arg(&arch_dir)
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout("b0000\n");
let expected_blocks = [
"ea50e43840e5f310490bba1b641db82480a05e16e9ae220c1e5113c79b59541fa5a6ddb13db20d4df53dfcecb3ed9969e41a329e07afe0fbb597251a789c3575",
];
let is_expected_blocks = |output: &[u8]| {
let output_str = std::str::from_utf8(output).unwrap();
let mut blocks: Vec<&str> = output_str.lines().collect();
blocks.sort_unstable();
blocks == expected_blocks
};
run_conserve()
.args(["debug", "blocks"])
.arg(&arch_dir)
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(predicate::function(is_expected_blocks));
run_conserve()
.args(["debug", "referenced"])
.arg(&arch_dir)
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(predicate::function(is_expected_blocks));
run_conserve()
.args(["debug", "unreferenced"])
.arg(&arch_dir)
.assert()
.success()
.stderr("")
.stdout("");
run_conserve()
.args(["debug", "index"])
.arg(&arch_dir)
.assert()
.success()
.stderr(predicate::str::is_empty());
run_conserve().arg("gc").arg(&arch_dir).assert().success();
let file_url = Url::from_directory_path(&arch_dir).unwrap();
run_conserve()
.arg("ls")
.arg(file_url.as_str())
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(
"/\n\
/hello\n\
/subdir\n\
/subdir/subfile\n",
);
let restore_dir = TempDir::new().unwrap();
run_conserve()
.arg("restore")
.arg("-v")
.arg("--no-progress")
.arg(&arch_dir)
.arg(restore_dir.path())
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(predicate::str::starts_with(
"/\n\
/hello\n\
/subdir\n\
/subdir/subfile\n\
Restore complete.\n",
));
restore_dir
.child("subdir")
.assert(predicate::path::is_dir());
restore_dir
.child("hello")
.assert(predicate::path::is_file())
.assert("hello world\n");
restore_dir
.child("subdir")
.child("subfile")
.assert("I like Rust\n");
run_conserve()
.arg("restore")
.arg("-v")
.arg(&arch_dir)
.arg(restore_dir.path())
.assert()
.failure()
.stdout(predicate::str::contains("Destination directory not empty"));
{
let restore_dir2 = TempDir::new().unwrap();
run_conserve()
.args(["restore", "-b", "b0"])
.arg(&arch_dir)
.arg(restore_dir2.path())
.assert()
.success();
}
run_conserve()
.arg("validate")
.arg(arch_dir)
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(predicate::str::contains("Archive is OK.\n"));
}
#[test]
fn empty_archive() {
let tempdir = TempDir::new().unwrap();
let adir = tempdir.path().join("archive");
let restore_dir = TempDir::new().unwrap();
run_conserve().arg("init").arg(&adir).assert().success();
run_conserve()
.arg("restore")
.arg(&adir)
.arg(restore_dir.path())
.assert()
.failure()
.stdout(predicate::str::contains("Archive has no bands"));
run_conserve()
.arg("ls")
.arg(&adir)
.assert()
.failure()
.stdout(predicate::str::contains("Archive has no bands"));
run_conserve()
.arg("versions")
.arg(&adir)
.assert()
.success()
.stdout(predicate::str::is_empty());
run_conserve().arg("gc").arg(adir).assert().success();
}
#[test]
fn incomplete_version() {
let af = ScratchArchive::new();
af.setup_incomplete_empty_band();
run_conserve()
.arg("versions")
.arg(af.path())
.assert()
.success()
.stderr(predicate::str::is_empty())
.stdout(predicate::str::contains("b0000"))
.stdout(predicate::str::contains("incomplete"));
run_conserve().arg("ls").arg(af.path()).assert().success();
run_conserve()
.arg("gc")
.arg(af.path())
.assert()
.failure()
.stdout(predicate::str::contains("incomplete and may be in use"));
}
#[test]
fn validate_non_fatal_problems_nonzero_result() {
run_conserve()
.args(["validate", "testdata/damaged/missing-block/"])
.assert()
.stdout(predicate::str::contains("Archive has some problems."))
.code(2);
}
#[test]
fn restore_only_subtree() {
let dest = TempDir::new().unwrap();
run_conserve()
.args([
"restore",
"testdata/archive/minimal/v0.6.3/",
"--only",
"/subdir",
])
.arg(dest.path())
.assert()
.success();
dest.child("hello").assert(predicate::path::missing());
dest.child("subdir").assert(predicate::path::is_dir());
dest.child("subdir")
.child("subfile")
.assert("I like Rust\n");
dest.close().unwrap();
}
#[test]
fn size_exclude() {
let source = TreeFixture::new();
source.create_file_with_contents("small", b"0123456789");
source.create_file_with_contents("junk", b"01234567890123456789");
run_conserve()
.args(["size", "--bytes", "--source"])
.arg(source.path())
.args(["--exclude=/junk"])
.assert()
.success()
.stdout("10\n");
}