watchexec/crates/ignore-files/tests/helpers/mod.rs

160 lines
3.5 KiB
Rust

use std::path::{Path, PathBuf};
use ignore::{gitignore::Glob, Match};
use ignore_files::{IgnoreFile, IgnoreFilter};
pub mod ignore_tests {
pub use super::ig_file as file;
pub use super::ignore_filt as filt;
pub use super::Applies;
pub use super::PathHarness;
}
/// Get the drive letter of the current working directory.
#[cfg(windows)]
fn drive_root() -> String {
let path = std::fs::canonicalize(".").unwrap();
let Some(prefix) = path.components().next() else {
return r"C:\".into();
};
match prefix {
std::path::Component::Prefix(prefix_component) => prefix_component
.as_os_str()
.to_str()
.map(|p| p.to_owned() + r"\")
.unwrap_or(r"C:\".into()),
_ => r"C:\".into(),
}
}
fn normalize_path(path: &str) -> PathBuf {
#[cfg(windows)]
let path: &str = &String::from(path)
.strip_prefix("/")
.map_or(path.into(), |p| drive_root() + p);
let path: PathBuf = if Path::new(path).has_root() {
path.into()
} else {
std::fs::canonicalize(".").unwrap().join("tests").join(path)
};
dunce::simplified(&path).into()
}
pub trait PathHarness {
fn check_path(&self, path: &Path, is_dir: bool) -> Match<&Glob>;
fn path_pass(&self, path: &str, is_dir: bool, pass: bool) {
let full_path = &normalize_path(path);
tracing::info!(?path, ?is_dir, ?pass, "check");
let result = self.check_path(full_path, is_dir);
assert_eq!(
match result {
Match::None => true,
Match::Ignore(glob) => !glob.from().map_or(true, |f| full_path.starts_with(f)),
Match::Whitelist(_glob) => true,
},
pass,
"{} {:?} (expected {}) [result: {}]",
if is_dir { "dir" } else { "file" },
full_path,
if pass { "pass" } else { "fail" },
match result {
Match::None => String::from("None"),
Match::Ignore(glob) => format!(
"Ignore({})",
glob.from()
.map_or(String::from(""), |f| f.display().to_string())
),
Match::Whitelist(glob) => format!(
"Whitelist({})",
glob.from()
.map_or(String::from(""), |f| f.display().to_string())
),
},
);
}
fn file_does_pass(&self, path: &str) {
self.path_pass(path, false, true);
}
fn file_doesnt_pass(&self, path: &str) {
self.path_pass(path, false, false);
}
fn dir_does_pass(&self, path: &str) {
self.path_pass(path, true, true);
}
fn dir_doesnt_pass(&self, path: &str) {
self.path_pass(path, true, false);
}
fn agnostic_pass(&self, path: &str) {
self.file_does_pass(path);
self.dir_does_pass(path);
}
fn agnostic_fail(&self, path: &str) {
self.file_doesnt_pass(path);
self.dir_doesnt_pass(path);
}
}
impl PathHarness for IgnoreFilter {
fn check_path(&self, path: &Path, is_dir: bool) -> Match<&Glob> {
self.match_path(&path, is_dir)
}
}
fn tracing_init() {
use tracing_subscriber::{
fmt::{format::FmtSpan, Subscriber},
util::SubscriberInitExt,
EnvFilter,
};
Subscriber::builder()
.pretty()
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
.with_env_filter(EnvFilter::from_default_env())
.finish()
.try_init()
.ok();
}
pub async fn ignore_filt(origin: &str, ignore_files: &[IgnoreFile]) -> IgnoreFilter {
tracing_init();
let origin = normalize_path(origin);
IgnoreFilter::new(origin, ignore_files)
.await
.expect("making filterer")
}
pub fn ig_file(name: &str) -> IgnoreFile {
let path = normalize_path(name);
let parent: PathBuf = path.parent().unwrap_or(&path).into();
IgnoreFile {
path,
applies_in: Some(parent),
applies_to: None,
}
}
pub trait Applies {
fn applies_globally(self) -> Self;
}
impl Applies for IgnoreFile {
fn applies_globally(mut self) -> Self {
self.applies_in = None;
self
}
}