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"] }
|
simplelog = { version = "^0.11.1", features = ["paris"] }
|
||||||
url = { version = "2.2.2", features = ["serde"] }
|
url = { version = "2.2.2", features = ["serde"] }
|
||||||
yaml-rust = "0.4.4"
|
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> {
|
fn get_workdir(pc: &PathConfig) -> Result<ResPathBuf> {
|
||||||
let workdir = path::resolve(&pc.config.repos.local)?;
|
let workdir = path::resolve(&pc.config.repos.local)?;
|
||||||
match Repository::open(workdir.resolved()) {
|
if let Ok(repo) = Repository::open(workdir.resolved()) {
|
||||||
Ok(_) => Ok(workdir),
|
if repo.workdir().is_some() {
|
||||||
Err(_) => Err(io::Error::new(
|
Ok(workdir)
|
||||||
|
} else {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::NotFound,
|
||||||
|
"Local repository is --bare.",
|
||||||
|
))?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(io::Error::new(
|
||||||
io::ErrorKind::NotFound,
|
io::ErrorKind::NotFound,
|
||||||
"Local repository not found.",
|
"Local repository not found.",
|
||||||
))?,
|
))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,3 +235,181 @@ fn get_package_lookup(pc: &PathConfig) -> HashMap<PathBuf, Option<ResPathBuf>> {
|
||||||
}
|
}
|
||||||
seen
|
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,
|
env::VarError,
|
||||||
error,
|
error,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
fmt, fs,
|
fmt,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
io,
|
io,
|
||||||
path::{Component, Path, PathBuf},
|
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
|
// Resolution
|
||||||
// ========================================
|
// ========================================
|
||||||
|
@ -236,7 +208,7 @@ pub fn expand(path: &Path) -> Result<PathBuf> {
|
||||||
match comp {
|
match comp {
|
||||||
Component::Prefix(_) => Err(io::Error::new(
|
Component::Prefix(_) => Err(io::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
"We do not currently support Windows.",
|
"We do not support Windows.",
|
||||||
))?,
|
))?,
|
||||||
Component::RootDir => {
|
Component::RootDir => {
|
||||||
expanded.clear();
|
expanded.clear();
|
||||||
|
@ -292,3 +264,81 @@ pub fn soft_resolve(path: &Path) -> Result<Option<ResPathBuf>> {
|
||||||
Err(Error::VarError(_)) => Ok(None),
|
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