Start testing parser and nonpaths
This commit is contained in:
parent
e4cd1d7379
commit
b59acaa9cf
|
@ -0,0 +1,27 @@
|
|||
use watchexec::{
|
||||
event::{filekind::*, ProcessEnd, Source},
|
||||
signal::source::MainSignal,
|
||||
};
|
||||
|
||||
mod helpers;
|
||||
use helpers::tagged::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn empty_filter_passes_everything() {
|
||||
let filterer = filt(&[]).await;
|
||||
|
||||
filterer.fek_does_pass(FileEventKind::Create(CreateKind::File));
|
||||
filterer.source_does_pass(Source::Keyboard);
|
||||
filterer.pid_does_pass(1234);
|
||||
filterer.signal_does_pass(MainSignal::User1);
|
||||
filterer.complete_does_pass(None);
|
||||
filterer.complete_does_pass(Some(ProcessEnd::Success));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn source_exact() {
|
||||
let filterer = filt(&[filter("source=keyboard")]).await;
|
||||
|
||||
filterer.source_does_pass(Source::Keyboard);
|
||||
filterer.source_doesnt_pass(Source::Mouse);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use watchexec::filter::tagged::{error::TaggedFiltererError, Filter, Matcher, Op, Pattern};
|
||||
|
||||
mod helpers;
|
||||
use helpers::tagged::*;
|
||||
|
||||
#[test]
|
||||
fn empty_filter() {
|
||||
assert!(matches!(
|
||||
Filter::from_str(""),
|
||||
Err(TaggedFiltererError::Parse { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_auto_op() {
|
||||
assert_eq!(
|
||||
filter("path=foo"),
|
||||
Filter {
|
||||
in_path: None,
|
||||
on: Matcher::Path,
|
||||
op: Op::Glob,
|
||||
pat: Pattern::Glob("foo".to_string()),
|
||||
negate: false,
|
||||
}
|
||||
);
|
||||
}
|
|
@ -27,7 +27,7 @@ async fn empty_filter_passes_everything() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn exact_filename() {
|
||||
let filterer = filt(&[filter("Cargo.toml")]).await;
|
||||
let filterer = filt(&[glob_filter("Cargo.toml")]).await;
|
||||
|
||||
filterer.file_does_pass("Cargo.toml");
|
||||
filterer.file_does_pass("/test/foo/bar/Cargo.toml");
|
||||
|
@ -40,7 +40,7 @@ async fn exact_filename() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn exact_filenames_multiple() {
|
||||
let filterer = filt(&[filter("Cargo.toml"), filter("package.json")]).await;
|
||||
let filterer = filt(&[glob_filter("Cargo.toml"), glob_filter("package.json")]).await;
|
||||
|
||||
filterer.file_does_pass("Cargo.toml");
|
||||
filterer.file_does_pass("/test/foo/bar/Cargo.toml");
|
||||
|
@ -57,7 +57,7 @@ async fn exact_filenames_multiple() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn glob_single_final_ext_star() {
|
||||
let filterer = filt(&[filter("Cargo.*")]).await;
|
||||
let filterer = filt(&[glob_filter("Cargo.*")]).await;
|
||||
|
||||
filterer.file_does_pass("Cargo.toml");
|
||||
filterer.file_does_pass("Cargo.json");
|
||||
|
@ -69,7 +69,7 @@ async fn glob_single_final_ext_star() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn glob_star_trailing_slash() {
|
||||
let filterer = filt(&[filter("Cargo.*/")]).await;
|
||||
let filterer = filt(&[glob_filter("Cargo.*/")]).await;
|
||||
|
||||
filterer.file_doesnt_pass("Cargo.toml");
|
||||
filterer.file_doesnt_pass("Cargo.json");
|
||||
|
@ -82,7 +82,7 @@ async fn glob_star_trailing_slash() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn glob_star_leading_slash() {
|
||||
let filterer = filt(&[filter("/Cargo.*")]).await;
|
||||
let filterer = filt(&[glob_filter("/Cargo.*")]).await;
|
||||
|
||||
filterer.file_does_pass("Cargo.toml");
|
||||
filterer.file_does_pass("Cargo.json");
|
||||
|
@ -94,7 +94,7 @@ async fn glob_star_leading_slash() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn glob_leading_double_star() {
|
||||
let filterer = filt(&[filter("**/possum")]).await;
|
||||
let filterer = filt(&[glob_filter("**/possum")]).await;
|
||||
|
||||
filterer.file_does_pass("possum");
|
||||
filterer.file_does_pass("foo/bar/possum");
|
||||
|
@ -109,7 +109,7 @@ async fn glob_leading_double_star() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn glob_trailing_double_star() {
|
||||
let filterer = filt(&[filter("possum/**")]).await;
|
||||
let filterer = filt(&[glob_filter("possum/**")]).await;
|
||||
|
||||
filterer.file_doesnt_pass("possum");
|
||||
filterer.file_does_pass("possum/foo/bar");
|
||||
|
@ -128,7 +128,7 @@ async fn glob_trailing_double_star() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn glob_middle_double_star() {
|
||||
let filterer = filt(&[filter("apples/**/oranges")]).await;
|
||||
let filterer = filt(&[glob_filter("apples/**/oranges")]).await;
|
||||
|
||||
filterer.dir_doesnt_pass("/a/folder");
|
||||
filterer.file_does_pass("apples/carrots/oranges");
|
||||
|
@ -145,7 +145,7 @@ async fn glob_middle_double_star() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn glob_double_star_trailing_slash() {
|
||||
let filterer = filt(&[filter("apples/**/oranges/")]).await;
|
||||
let filterer = filt(&[glob_filter("apples/**/oranges/")]).await;
|
||||
|
||||
filterer.dir_doesnt_pass("/a/folder");
|
||||
filterer.file_doesnt_pass("apples/carrots/oranges");
|
||||
|
@ -165,7 +165,7 @@ async fn glob_double_star_trailing_slash() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_exact_filename() {
|
||||
let filterer = filt(&[not_filter("Cargo.toml")]).await;
|
||||
let filterer = filt(&[notglob_filter("Cargo.toml")]).await;
|
||||
|
||||
filterer.file_doesnt_pass("Cargo.toml");
|
||||
filterer.file_doesnt_pass("/test/foo/bar/Cargo.toml");
|
||||
|
@ -178,7 +178,7 @@ async fn ignore_exact_filename() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_exact_filenames_multiple() {
|
||||
let filterer = filt(&[not_filter("Cargo.toml"), not_filter("package.json")]).await;
|
||||
let filterer = filt(&[notglob_filter("Cargo.toml"), notglob_filter("package.json")]).await;
|
||||
|
||||
filterer.file_doesnt_pass("Cargo.toml");
|
||||
filterer.file_doesnt_pass("/test/foo/bar/Cargo.toml");
|
||||
|
@ -195,7 +195,7 @@ async fn ignore_exact_filenames_multiple() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_glob_single_final_ext_star() {
|
||||
let filterer = filt(&[not_filter("Cargo.*")]).await;
|
||||
let filterer = filt(&[notglob_filter("Cargo.*")]).await;
|
||||
|
||||
filterer.file_doesnt_pass("Cargo.toml");
|
||||
filterer.file_doesnt_pass("Cargo.json");
|
||||
|
@ -207,7 +207,7 @@ async fn ignore_glob_single_final_ext_star() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_glob_star_trailing_slash() {
|
||||
let filterer = filt(&[not_filter("Cargo.*/")]).await;
|
||||
let filterer = filt(&[notglob_filter("Cargo.*/")]).await;
|
||||
|
||||
filterer.file_does_pass("Cargo.toml");
|
||||
filterer.file_does_pass("Cargo.json");
|
||||
|
@ -220,7 +220,7 @@ async fn ignore_glob_star_trailing_slash() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_glob_star_leading_slash() {
|
||||
let filterer = filt(&[not_filter("/Cargo.*")]).await;
|
||||
let filterer = filt(&[notglob_filter("/Cargo.*")]).await;
|
||||
|
||||
filterer.file_doesnt_pass("Cargo.toml");
|
||||
filterer.file_doesnt_pass("Cargo.json");
|
||||
|
@ -232,7 +232,7 @@ async fn ignore_glob_star_leading_slash() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_glob_leading_double_star() {
|
||||
let filterer = filt(&[not_filter("**/possum")]).await;
|
||||
let filterer = filt(&[notglob_filter("**/possum")]).await;
|
||||
|
||||
filterer.file_doesnt_pass("possum");
|
||||
filterer.file_doesnt_pass("foo/bar/possum");
|
||||
|
@ -247,7 +247,7 @@ async fn ignore_glob_leading_double_star() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_glob_trailing_double_star() {
|
||||
let filterer = filt(&[not_filter("possum/**")]).await;
|
||||
let filterer = filt(&[notglob_filter("possum/**")]).await;
|
||||
|
||||
filterer.file_does_pass("possum");
|
||||
filterer.file_doesnt_pass("possum/foo/bar");
|
||||
|
@ -266,7 +266,7 @@ async fn ignore_glob_trailing_double_star() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_glob_middle_double_star() {
|
||||
let filterer = filt(&[not_filter("apples/**/oranges")]).await;
|
||||
let filterer = filt(&[notglob_filter("apples/**/oranges")]).await;
|
||||
|
||||
filterer.dir_does_pass("/a/folder");
|
||||
filterer.file_doesnt_pass("apples/carrots/oranges");
|
||||
|
@ -283,7 +283,7 @@ async fn ignore_glob_middle_double_star() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_glob_double_star_trailing_slash() {
|
||||
let filterer = filt(&[not_filter("apples/**/oranges/")]).await;
|
||||
let filterer = filt(&[notglob_filter("apples/**/oranges/")]).await;
|
||||
|
||||
filterer.dir_does_pass("/a/folder");
|
||||
filterer.file_does_pass("apples/carrots/oranges");
|
||||
|
@ -304,11 +304,11 @@ async fn ignore_glob_double_star_trailing_slash() {
|
|||
#[tokio::test]
|
||||
async fn ignores_take_precedence() {
|
||||
let filterer = filt(&[
|
||||
filter("*.docx"),
|
||||
filter("*.toml"),
|
||||
filter("*.json"),
|
||||
not_filter("*.toml"),
|
||||
not_filter("*.json"),
|
||||
glob_filter("*.docx"),
|
||||
glob_filter("*.toml"),
|
||||
glob_filter("*.json"),
|
||||
notglob_filter("*.toml"),
|
||||
notglob_filter("*.json"),
|
||||
])
|
||||
.await;
|
||||
|
||||
|
@ -323,7 +323,7 @@ async fn ignores_take_precedence() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn scopes_global() {
|
||||
let filterer = filt(&[not_filter("*.toml")]).await;
|
||||
let filterer = filt(&[notglob_filter("*.toml")]).await;
|
||||
|
||||
filterer.file_doesnt_pass("Cargo.toml");
|
||||
filterer.dir_doesnt_pass("Cargo.toml");
|
||||
|
@ -340,7 +340,7 @@ async fn scopes_global() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn scopes_local() {
|
||||
let filterer = filt(&[not_filter("*.toml").in_path()]).await;
|
||||
let filterer = filt(&[notglob_filter("*.toml").in_path()]).await;
|
||||
|
||||
filterer.file_doesnt_pass("/test/Cargo.toml");
|
||||
filterer.dir_doesnt_pass("/test/Cargo.toml");
|
||||
|
@ -357,7 +357,7 @@ async fn scopes_local() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn scopes_sublocal() {
|
||||
let filterer = filt(&[not_filter("*.toml").in_subpath("src")]).await;
|
||||
let filterer = filt(&[notglob_filter("*.toml").in_subpath("src")]).await;
|
||||
|
||||
filterer.file_doesnt_pass("/test/src/Cargo.toml");
|
||||
filterer.dir_doesnt_pass("/test/src/Cargo.toml");
|
||||
|
@ -406,7 +406,7 @@ fn watchexec_v1_confusing_suite(filterer: Arc<TaggedFilterer>) {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_folder_with_bare_match() {
|
||||
let filterer = filt(&[not_filter("prunes").in_path()]).await;
|
||||
let filterer = filt(&[notglob_filter("prunes").in_path()]).await;
|
||||
|
||||
filterer.file_doesnt_pass("prunes");
|
||||
filterer.dir_doesnt_pass("prunes");
|
||||
|
@ -415,7 +415,7 @@ async fn ignore_folder_with_bare_match() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_folder_with_bare_and_leading_slash() {
|
||||
let filterer = filt(&[not_filter("/prunes").in_path()]).await;
|
||||
let filterer = filt(&[notglob_filter("/prunes").in_path()]).await;
|
||||
|
||||
filterer.file_doesnt_pass("prunes");
|
||||
filterer.dir_doesnt_pass("prunes");
|
||||
|
@ -424,7 +424,7 @@ async fn ignore_folder_with_bare_and_leading_slash() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_folder_with_bare_and_trailing_slash() {
|
||||
let filterer = filt(&[not_filter("prunes/").in_path()]).await;
|
||||
let filterer = filt(&[notglob_filter("prunes/").in_path()]).await;
|
||||
|
||||
filterer.file_does_pass("prunes");
|
||||
filterer.dir_doesnt_pass("prunes");
|
||||
|
@ -433,7 +433,7 @@ async fn ignore_folder_with_bare_and_trailing_slash() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn ignore_folder_with_only_double_double_glob() {
|
||||
let filterer = filt(&[not_filter("**/prunes/**").in_path()]).await;
|
||||
let filterer = filt(&[notglob_filter("**/prunes/**").in_path()]).await;
|
||||
|
||||
filterer.file_does_pass("prunes");
|
||||
filterer.dir_does_pass("prunes");
|
||||
|
@ -443,8 +443,8 @@ async fn ignore_folder_with_only_double_double_glob() {
|
|||
#[tokio::test]
|
||||
async fn ignore_folder_with_double_and_double_double_globs() {
|
||||
let filterer = filt(&[
|
||||
not_filter("**/prunes").in_path(),
|
||||
not_filter("**/prunes/**").in_path(),
|
||||
notglob_filter("**/prunes").in_path(),
|
||||
notglob_filter("**/prunes/**").in_path(),
|
||||
])
|
||||
.await;
|
||||
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
use std::{
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use watchexec::{
|
||||
error::RuntimeError,
|
||||
event::{Event, FileType, Tag},
|
||||
event::{filekind::FileEventKind, Event, FileType, ProcessEnd, Source, Tag},
|
||||
filter::{
|
||||
globset::GlobsetFilterer,
|
||||
tagged::{Filter, Matcher, Op, Pattern, TaggedFilterer},
|
||||
|
@ -16,13 +17,14 @@ use watchexec::{
|
|||
},
|
||||
ignore_files::IgnoreFile,
|
||||
project::ProjectType,
|
||||
signal::source::MainSignal,
|
||||
};
|
||||
|
||||
pub mod globset {
|
||||
pub use super::file;
|
||||
pub use super::globset_filt as filt;
|
||||
pub use super::Applies;
|
||||
pub use super::Harness;
|
||||
pub use super::PathHarness;
|
||||
}
|
||||
|
||||
pub mod globset_ig {
|
||||
|
@ -35,8 +37,9 @@ pub mod tagged {
|
|||
pub use super::tagged_filt as filt;
|
||||
pub use super::Applies;
|
||||
pub use super::FilterExt;
|
||||
pub use super::Harness;
|
||||
pub use super::{filter, not_filter};
|
||||
pub use super::PathHarness;
|
||||
pub use super::TaggedHarness;
|
||||
pub use super::{filter, glob_filter, notglob_filter};
|
||||
}
|
||||
|
||||
pub mod tagged_ig {
|
||||
|
@ -44,7 +47,7 @@ pub mod tagged_ig {
|
|||
pub use super::tagged_igfilt as filt;
|
||||
}
|
||||
|
||||
pub trait Harness {
|
||||
pub trait PathHarness {
|
||||
fn check_path(
|
||||
&self,
|
||||
path: PathBuf,
|
||||
|
@ -104,7 +107,7 @@ pub trait Harness {
|
|||
}
|
||||
}
|
||||
|
||||
impl Harness for GlobsetFilterer {
|
||||
impl PathHarness for GlobsetFilterer {
|
||||
fn check_path(
|
||||
&self,
|
||||
path: PathBuf,
|
||||
|
@ -119,7 +122,7 @@ impl Harness for GlobsetFilterer {
|
|||
}
|
||||
}
|
||||
|
||||
impl Harness for TaggedFilterer {
|
||||
impl PathHarness for TaggedFilterer {
|
||||
fn check_path(
|
||||
&self,
|
||||
path: PathBuf,
|
||||
|
@ -134,6 +137,73 @@ impl Harness for TaggedFilterer {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait TaggedHarness {
|
||||
fn check_tag(&self, tag: Tag) -> std::result::Result<bool, RuntimeError>;
|
||||
|
||||
fn tag_pass(&self, tag: Tag, pass: bool) {
|
||||
tracing::info!(?tag, ?pass, "check");
|
||||
|
||||
assert_eq!(
|
||||
self.check_tag(tag.clone()).unwrap(),
|
||||
pass,
|
||||
"{:?} (expected {})",
|
||||
tag,
|
||||
if pass { "pass" } else { "fail" }
|
||||
);
|
||||
}
|
||||
|
||||
fn fek_does_pass(&self, fek: FileEventKind) {
|
||||
self.tag_pass(Tag::FileEventKind(fek), true);
|
||||
}
|
||||
|
||||
fn fek_doesnt_pass(&self, fek: FileEventKind) {
|
||||
self.tag_pass(Tag::FileEventKind(fek), false);
|
||||
}
|
||||
|
||||
fn source_does_pass(&self, source: Source) {
|
||||
self.tag_pass(Tag::Source(source), true);
|
||||
}
|
||||
|
||||
fn source_doesnt_pass(&self, source: Source) {
|
||||
self.tag_pass(Tag::Source(source), false);
|
||||
}
|
||||
|
||||
fn pid_does_pass(&self, pid: u32) {
|
||||
self.tag_pass(Tag::Process(pid), true);
|
||||
}
|
||||
|
||||
fn pid_doesnt_pass(&self, pid: u32) {
|
||||
self.tag_pass(Tag::Process(pid), false);
|
||||
}
|
||||
|
||||
fn signal_does_pass(&self, sig: MainSignal) {
|
||||
self.tag_pass(Tag::Signal(sig), true);
|
||||
}
|
||||
|
||||
fn signal_doesnt_pass(&self, sig: MainSignal) {
|
||||
self.tag_pass(Tag::Signal(sig), false);
|
||||
}
|
||||
|
||||
fn complete_does_pass(&self, exit: Option<ProcessEnd>) {
|
||||
self.tag_pass(Tag::ProcessCompletion(exit), true);
|
||||
}
|
||||
|
||||
fn complete_doesnt_pass(&self, exit: Option<ProcessEnd>) {
|
||||
self.tag_pass(Tag::ProcessCompletion(exit), false);
|
||||
}
|
||||
}
|
||||
|
||||
impl TaggedHarness for TaggedFilterer {
|
||||
fn check_tag(&self, tag: Tag) -> std::result::Result<bool, RuntimeError> {
|
||||
let event = Event {
|
||||
tags: vec![tag],
|
||||
metadata: Default::default(),
|
||||
};
|
||||
|
||||
self.check_event(&event)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn globset_filt(filters: &[&str], ignores: &[&str], extensions: &[&str]) -> GlobsetFilterer {
|
||||
let origin = dunce::canonicalize(".").unwrap();
|
||||
GlobsetFilterer::new(
|
||||
|
@ -213,7 +283,11 @@ impl Applies for IgnoreFile {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn filter(pat: &str) -> Filter {
|
||||
pub fn filter(expr: &str) -> Filter {
|
||||
Filter::from_str(expr).expect("parse filter")
|
||||
}
|
||||
|
||||
pub fn glob_filter(pat: &str) -> Filter {
|
||||
Filter {
|
||||
in_path: None,
|
||||
on: Matcher::Path,
|
||||
|
@ -223,7 +297,7 @@ pub fn filter(pat: &str) -> Filter {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn not_filter(pat: &str) -> Filter {
|
||||
pub fn notglob_filter(pat: &str) -> Filter {
|
||||
Filter {
|
||||
in_path: None,
|
||||
on: Matcher::Path,
|
||||
|
|
Loading…
Reference in New Issue