Add tests to `path` and `copy` modules.

pull/3/head
Joshua Potter 2022-01-09 05:28:38 -05:00
parent 4256a79a87
commit 794ed592c9
4 changed files with 275 additions and 34 deletions

1
.homesync.yml Normal file
View File

@ -0,0 +1 @@
Hello, world!

View File

@ -20,3 +20,7 @@ serde_yaml = "0.8"
simplelog = { version = "^0.11.1", features = ["paris"] }
url = { version = "2.2.2", features = ["serde"] }
yaml-rust = "0.4.4"
[dev-dependencies]
serial_test = "0.5.1"
tempfile = "3.3.0"

View File

@ -179,12 +179,20 @@ pub fn stage(pc: &PathConfig) -> Result<()> {
fn get_workdir(pc: &PathConfig) -> Result<ResPathBuf> {
let workdir = path::resolve(&pc.config.repos.local)?;
match Repository::open(workdir.resolved()) {
Ok(_) => Ok(workdir),
Err(_) => Err(io::Error::new(
if let Ok(repo) = Repository::open(workdir.resolved()) {
if repo.workdir().is_some() {
Ok(workdir)
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"Local repository is --bare.",
))?
}
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"Local repository not found.",
))?,
))?
}
}
@ -227,3 +235,181 @@ fn get_package_lookup(pc: &PathConfig) -> HashMap<PathBuf, Option<ResPathBuf>> {
}
seen
}
// ========================================
// Tests
// ========================================
#[cfg(test)]
mod tests {
use super::*;
use crate::{config, path};
use git2::Repository;
use std::{env, fs::File, io::Write};
use tempfile::TempDir;
// Tests must be serial since we are updating our environment variables.
use serial_test::serial;
// Wrap functionality around this method to ensure the temporary directory
// does not go out of scope before we are ready.
fn build_home<T: Fn(&config::PathConfig, &Path)>(func: T) {
let temp_dir = TempDir::new().unwrap();
let mut home_dir = temp_dir.path().to_path_buf();
home_dir.push("home/owner");
fs::create_dir_all(&home_dir).unwrap();
let mut homesync_yml = home_dir.to_path_buf();
homesync_yml.push(".homesync.yml");
File::create(&homesync_yml).unwrap();
path::resolve(&homesync_yml).unwrap();
let mut config_homesync_yml = home_dir.to_path_buf();
config_homesync_yml.push(".config/homesync");
fs::create_dir_all(&config_homesync_yml).unwrap();
config_homesync_yml.push("homesync.yml");
File::create(&config_homesync_yml).unwrap();
env::set_var("HOME", &home_dir);
env::set_var("XDG_CONFIG_HOME", "");
let template = path::resolve(Path::new("examples/template.yaml")).unwrap();
let config = config::load(&vec![template]).unwrap();
func(&config, &home_dir);
}
fn build_repo(pc: &PathConfig) -> PathBuf {
let repo_dir = path::expand(&pc.config.repos.local).unwrap();
Repository::init(&repo_dir).unwrap();
let mut path = repo_dir.to_path_buf();
path.push("b");
fs::create_dir(&path).unwrap();
path.pop();
path.push("a");
File::create(&path).unwrap();
path.pop();
path.push("b/c");
File::create(&path).unwrap();
repo_dir
}
#[test]
#[serial]
fn walk_repo() {
build_home(|pc, _home_dir| {
let repo_dir = build_repo(pc);
let walked = super::walk_repo(&repo_dir).unwrap();
let mut walked: Vec<PathBuf> = walked
.iter()
.map(|w| w.unresolved().to_path_buf())
.collect();
walked.sort();
assert_eq!(walked, vec![PathBuf::from("a"), PathBuf::from("b/c")]);
});
}
#[test]
#[serial]
fn package_lookup() {
build_home(|pc, _home_dir| {
let lookup = super::get_package_lookup(pc);
assert_eq!(lookup.len(), 4);
assert_eq!(
lookup
.iter()
.filter(|(_, v)| v.is_some())
.collect::<HashMap<_, _>>()
.len(),
2
);
assert_eq!(
lookup
.iter()
.filter(|(_, v)| v.is_none())
.collect::<HashMap<_, _>>()
.len(),
2
);
});
}
#[test]
#[serial]
fn apply_all() {
build_home(|pc, home_dir| {
let repo_dir = build_repo(pc);
let targets = [".homesync.yml", ".config/homesync/homesync.yml"];
for target in &targets {
let mut repo_path = repo_dir.to_path_buf();
repo_path.push(&format!("$HOME/{}", target));
fs::create_dir_all(repo_path.parent().unwrap()).unwrap();
let mut file = File::create(&repo_path).unwrap();
file.write_all(b"Hello, world!").unwrap();
}
super::apply_all(pc).expect("Could not apply packages");
for target in &targets {
let mut home_path = home_dir.to_path_buf();
home_path.push(target);
let contents = fs::read_to_string(&home_path).unwrap();
assert_eq!(contents, "Hello, world!");
}
});
}
#[test]
#[serial]
fn apply_one() {
build_home(|pc, home_dir| {
let repo_dir = build_repo(pc);
let targets = [".homesync.yml", ".config/homesync/homesync.yml"];
for target in &targets {
let mut repo_path = repo_dir.to_path_buf();
repo_path.push(&format!("$HOME/{}", target));
fs::create_dir_all(repo_path.parent().unwrap()).unwrap();
let mut file = File::create(&repo_path).unwrap();
file.write_all(b"Hello, world!").unwrap();
}
super::apply_one(pc, "homesync").expect("Could not apply `homesync`");
for target in &targets {
let mut home_path = home_dir.to_path_buf();
home_path.push(target);
let contents = fs::read_to_string(&home_path).unwrap();
assert_eq!(contents, "Hello, world!");
}
});
}
#[test]
#[serial]
fn stage() {
build_home(|pc, _home_dir| {
let repo_dir = build_repo(pc);
super::stage(pc).expect("Could not stage files.");
// Copied over the files in $HOME that exist, and deleted files that
// were previously defined but not referenced in the config.
let walked = super::walk_repo(&repo_dir).unwrap();
let mut walked: Vec<PathBuf> = walked
.iter()
.map(|w| w.unresolved().to_path_buf())
.collect();
walked.sort();
assert_eq!(
walked,
vec![
PathBuf::from("$HOME/.config/homesync/homesync.yml"),
PathBuf::from("$HOME/.homesync.yml"),
]
);
});
}
}

View File

@ -8,7 +8,7 @@ use std::{
env::VarError,
error,
ffi::OsString,
fmt, fs,
fmt,
hash::{Hash, Hasher},
io,
path::{Component, Path, PathBuf},
@ -195,34 +195,6 @@ impl<'de> Deserialize<'de> for ResPathBuf {
}
}
// ========================================
// Validation
// ========================================
pub fn validate_is_file(path: &Path) -> Result<()> {
let metadata = fs::metadata(path)?;
if !metadata.is_file() {
// TODO(jrpotter): Use `IsADirectory` when stable.
Err(io::Error::new(
io::ErrorKind::Other,
format!("'{}' is not a file.", path.display()),
))?;
}
Ok(())
}
pub fn validate_is_dir(path: &Path) -> Result<()> {
let metadata = fs::metadata(path)?;
if !metadata.is_dir() {
// TODO(jrpotter): Use `NotADirectory` when stable.
Err(io::Error::new(
io::ErrorKind::Other,
format!("'{}' is not a directory.", path.display()),
))?;
}
Ok(())
}
// ========================================
// Resolution
// ========================================
@ -236,7 +208,7 @@ pub fn expand(path: &Path) -> Result<PathBuf> {
match comp {
Component::Prefix(_) => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"We do not currently support Windows.",
"We do not support Windows.",
))?,
Component::RootDir => {
expanded.clear();
@ -292,3 +264,81 @@ pub fn soft_resolve(path: &Path) -> Result<Option<ResPathBuf>> {
Err(Error::VarError(_)) => Ok(None),
}
}
// ========================================
// Tests
// ========================================
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
use tempfile::NamedTempFile;
#[test]
fn respath_absolute() {
let abs = Path::new("/home/jrpotter/example");
let rel = Path::new("home/jrpotter/example");
assert!(ResPathBuf::new(abs, rel).is_ok());
assert!(ResPathBuf::new(rel, rel).is_err());
}
#[test]
fn respath_equality() {
let path = Path::new("/home/jrpotter/example");
let res1 = ResPathBuf::new(path, Path::new("rel1")).unwrap();
let res2 = ResPathBuf::new(path, Path::new("rel2")).unwrap();
assert_eq!(res1, res2);
}
#[test]
fn respath_hash() {
let mut set = HashSet::new();
let path = Path::new("/home/jrpotter/example");
let res1 = ResPathBuf::new(path, Path::new("rel1")).unwrap();
let res2 = ResPathBuf::new(path, Path::new("rel2")).unwrap();
set.insert(res1);
set.insert(res2);
assert_eq!(set.len(), 1);
}
#[test]
fn expand_root_dir() {
let current = env::current_dir().unwrap();
let expanded = expand(Path::new("")).unwrap();
assert_eq!(current, expanded);
let expanded = expand(Path::new("/")).unwrap();
assert_eq!(Path::new("/"), expanded);
}
#[test]
fn expand_component() {
env::set_var("EXAMPLE", "example");
let expanded = expand(Path::new("/a/b/$EXAMPLE/c")).unwrap();
assert_eq!(Path::new("/a/b/example/c"), expanded);
let expanded = expand(Path::new("/a/b/pre$EXAMPLE/c")).unwrap();
assert_eq!(Path::new("/a/b/pre$EXAMPLE/c"), expanded);
}
#[test]
fn resolve() {
let path: PathBuf;
{
let temp = NamedTempFile::new().unwrap();
path = temp.path().to_path_buf();
assert!(super::resolve(&path).is_ok());
}
assert!(super::resolve(&path).is_err());
}
#[test]
fn soft_resolve() {
let path: PathBuf;
{
let temp = NamedTempFile::new().unwrap();
path = temp.path().to_path_buf();
assert!(super::soft_resolve(&path).unwrap().is_some());
}
assert!(super::soft_resolve(&path).unwrap().is_none());
}
}