Add tests to `path` and `copy` modules.
parent
4256a79a87
commit
794ed592c9
|
@ -0,0 +1 @@
|
|||
Hello, world!
|
|
@ -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"
|
||||
|
|
194
src/copy.rs
194
src/copy.rs
|
@ -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"),
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
110
src/path.rs
110
src/path.rs
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue