Start testing parser and nonpaths

This commit is contained in:
Félix Saparelli 2021-12-15 07:24:13 +13:00
parent e4cd1d7379
commit b59acaa9cf
No known key found for this signature in database
GPG Key ID: B948C4BAE44FC474
4 changed files with 170 additions and 41 deletions

View File

@ -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);
}

View File

@ -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,
}
);
}

View File

@ -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;

View File

@ -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,