use dev_utils::*;
use fcp::{self, filesystem as fs};
use std::ffi::OsStr;
use std::io::prelude::*;
use std::path::Path;
use std::process::{Command, ExitStatus};
use std::string::String;
const FILE_MODE: u32 = 0o644;
const DIR_MODE: u32 = 0o755;
fn diff(filename: &str) -> ExitStatus {
let filename = filename.strip_suffix(".json").unwrap();
Command::new("diff")
.args(&[
"-rq",
"--no-dereference",
HYDRATED_DIR.join(filename).to_str().unwrap(),
COPIES_DIR.join(filename).to_str().unwrap(),
])
.status()
.unwrap()
}
struct CommandResult {
stderr: String,
success: bool,
}
fn fcp_run<T: AsRef<OsStr>>(args: &[T]) -> CommandResult {
let result = Command::new(fcp_executable_path())
.args(args)
.output()
.unwrap();
CommandResult {
stderr: String::from_utf8(result.stderr).unwrap(),
success: result.status.success(),
}
}
fn copy_fixture(filename: &str) -> CommandResult {
let filename = filename.strip_suffix(".json").unwrap();
let destination = COPIES_DIR.join(filename);
remove(&destination);
fcp_run(&[HYDRATED_DIR.join(filename), destination])
}
macro_rules! make_test {
($(#[$attributes:meta])*
$test_name:ident) => {
#[test]
$(#[$attributes])*
fn $test_name() {
initialize();
let fixture_file = concat!(stringify!($test_name), ".json");
hydrate_fixture(fixture_file);
let result = copy_fixture(fixture_file);
assert!(result.success);
assert_eq!(result.stderr, "");
assert!(diff(fixture_file).success());
}
};
}
make_test!(regular_file);
make_test!(symlink);
make_test!(empty_directory);
make_test!(simple_directory);
make_test!(deep_directory);
make_test!(
#[ignore]
linux
);
make_test!(
#[ignore]
large_files
);
#[test]
fn socket() {
initialize();
let fixture_file = "socket.json";
hydrate_fixture(fixture_file);
let result = copy_fixture(fixture_file);
assert!(!result.success);
assert!(result.stderr.contains("sockets cannot be copied"));
}
#[test]
fn fifo() {
initialize();
let fixture_file = "fifo.json";
hydrate_fixture(fixture_file);
let result = copy_fixture(fixture_file);
assert!(result.success);
let file_type =
fs::file_type(&COPIES_DIR.join(fixture_file.strip_suffix(".json").unwrap())).unwrap();
assert!(matches!(file_type, fs::FileType::Fifo))
}
#[test]
fn character_device() {
initialize();
let destination = COPIES_DIR.join("character_device");
remove(&destination);
let contents = "Hello world\r";
let result = Command::new("tests/character_device.exp")
.args(&[
fcp_executable_path().to_str().unwrap(),
destination.to_str().unwrap(),
contents,
])
.output()
.unwrap();
assert!(result.status.success());
assert_eq!(String::from_utf8(result.stderr).unwrap(), "");
assert!(destination.exists());
let mut output = fs::open(destination).unwrap();
let mut output_contents = Vec::with_capacity(contents.len());
output.read_to_end(&mut output_contents).unwrap();
assert_eq!(
String::from_utf8(output_contents).unwrap(),
contents.replace('\r', "\n")
);
}
#[test]
fn too_few_arguments() {
initialize();
assert!(!fcp_run::<&str>(&[]).success);
assert!(!fcp_run(&["source"]).success);
}
#[test]
fn source_does_not_exist() {
initialize();
let destination = COPIES_DIR.join("source_does_not_exist");
let source = "source_does_not_exist";
remove(&destination);
let result = fcp_run(&[source, destination.to_str().unwrap()]);
assert!(!result.success);
assert!(result.stderr.contains(source));
assert!(!destination.exists());
}
#[test]
fn partial_directory() {
initialize();
let fixture_file = "partial_directory.json";
hydrate_fixture(fixture_file);
let result = copy_fixture(fixture_file);
assert!(!result.success);
assert!(result.stderr.contains("partial_directory/two.txt"));
for file in ["one.txt", "three.txt"] {
let result = Command::new("diff")
.args(&[
"-q",
HYDRATED_DIR
.join("partial_directory")
.join(file)
.to_str()
.unwrap(),
COPIES_DIR
.join("partial_directory")
.join(file)
.to_str()
.unwrap(),
])
.output()
.unwrap();
assert!(result.status.success());
}
}
#[test]
fn copy_into() {
initialize();
let source = COPIES_DIR.join("copy_into_empty");
let destination = COPIES_DIR.join("copy_into");
remove(&source);
remove(&destination);
fs::create(&source, FILE_MODE).unwrap();
fs::create_dir(&destination, DIR_MODE).unwrap();
let result = fcp_run(&[&source, &destination]);
assert!(result.success);
assert_eq!(result.stderr, "");
assert!(destination.join("copy_into_empty").exists());
}
#[test]
fn copy_into_symlink() {
initialize();
let fixture_file = "copy_into_symlink.json";
remove(&HYDRATED_DIR.join(fixture_file.strip_suffix(".json").unwrap()));
hydrate_fixture(fixture_file);
let fixture_path = HYDRATED_DIR.join(fixture_file.strip_suffix(".json").unwrap());
let source = fixture_path.join("source_directory");
let destination = fixture_path.join("symlink");
let result = fcp_run(&[&source, &destination]);
assert!(result.success);
assert_eq!(result.stderr, "");
let result = Command::new("diff")
.args(&[
"-rq",
source.to_str().unwrap(),
destination
.with_file_name("directory")
.join("source_directory")
.to_str()
.unwrap(),
])
.output()
.unwrap();
assert!(result.status.success());
assert_eq!(String::from_utf8(result.stderr).unwrap(), "");
}
fn copy_many_into(fixture_file: &str, create_destination: fn(&Path)) -> CommandResult {
initialize();
let fixture_name = fixture_file.strip_suffix(".json").unwrap();
hydrate_fixture(fixture_file);
let source = HYDRATED_DIR.join(fixture_name);
let destination = COPIES_DIR.join(fixture_name);
remove(&destination);
create_destination(&destination);
let mut file_paths = fs::read_dir(&source)
.unwrap()
.map(|entry| entry.unwrap().path())
.collect::<Vec<_>>();
file_paths.push(destination);
fcp_run(&file_paths)
}
#[test]
fn copy_many_into_success() {
let fixture_file = "copy_many_into_success.json";
let result = copy_many_into(fixture_file, |destination| {
fs::create_dir(destination, DIR_MODE).unwrap()
});
assert!(result.success);
assert_eq!(result.stderr, "");
assert!(diff(fixture_file).success());
}
#[test]
fn copy_many_into_destination_does_not_exist() {
let fixture_file = "copy_many_into_destination_does_not_exist.json";
let result = copy_many_into(fixture_file, |_| ());
assert!(!result.success);
assert_ne!(result.stderr, "");
}
#[test]
fn copy_many_into_destination_is_not_directory() {
let fixture_file = "copy_many_into_destination_is_not_directory.json";
let result = copy_many_into(fixture_file, |destination| {
fs::create(destination, FILE_MODE).unwrap();
});
assert!(!result.success);
assert!(result.stderr.contains("is not a directory"));
}
#[test]
fn copy_many_into_permissions_error() {
let fixture_file = "copy_many_into_permissions_error.json";
let fixture_name = fixture_file.strip_suffix(".json").unwrap();
let result = copy_many_into(fixture_file, |destination| {
fs::create_dir(destination, DIR_MODE).unwrap();
});
assert!(!result.success);
assert!(result.stderr.contains("two.txt"));
for file in ["one.txt", "three.txt"] {
let result = Command::new("diff")
.args(&[
"-q",
HYDRATED_DIR.join(fixture_name).join(file).to_str().unwrap(),
COPIES_DIR.join(fixture_name).join(file).to_str().unwrap(),
])
.status()
.unwrap();
assert!(result.success());
}
}
#[test]
fn prevent_copying_into_self() {
initialize();
fn assert_self_copy_failure(paths: &[&Path]) {
let result = fcp_run(paths);
assert!(!result.success);
assert!(result.stderr.contains("Cannot copy directory"));
}
let source = HYDRATED_DIR.join("prevent_copying_into_self");
remove(&source);
fs::create_dir(&source, DIR_MODE).unwrap();
assert_self_copy_failure(&[&source, &source]);
let other = HYDRATED_DIR.join("prevent_copying_into_self_other");
remove(&other);
fs::create_dir(&other, DIR_MODE).unwrap();
assert_self_copy_failure(&[&other, &source, &source]);
assert_self_copy_failure(&[&*HYDRATED_DIR, &source]);
fs::symlink("../", source.join("symlink")).unwrap();
assert_self_copy_failure(&[
&source.join("symlink").join(source.file_name().unwrap()),
&source,
]);
assert_self_copy_failure(&[Path::new(".."), Path::new(".")]);
let normal = source.join("normal");
fs::create(&normal, FILE_MODE).unwrap();
let result = fcp_run(&[&normal, &normal]);
assert!(!result.success);
assert!(result.stderr.contains("Cannot overwrite file"));
}
#[test]
fn prevent_duplicate_sources() {
initialize();
let source = HYDRATED_DIR.join("prevent_duplicate_sources");
remove(&source);
fs::create(&source, FILE_MODE).unwrap();
let destination = COPIES_DIR.join("prevent_duplicate_sources");
remove(&destination);
fs::create_dir(&destination, DIR_MODE).unwrap();
let mut result = fcp_run(&[&source, &source, &destination]);
assert!(!result.success);
assert!(result.stderr.contains("paths have the same file name"));
result = fcp_run(&[
&source,
&source
.with_file_name("..")
.join(HYDRATED_DIR.file_name().unwrap())
.join("prevent_duplicate_sources"),
&destination,
]);
assert!(!result.success);
assert!(result.stderr.contains("paths have the same file name"));
result = fcp_run(&[&source, &source.canonicalize().unwrap(), &destination]);
assert!(!result.success);
assert!(result.stderr.contains("paths have the same file name"));
}