mirror of
https://github.com/watchexec/watchexec.git
synced 2024-09-19 18:01:29 +02:00
429 lines
14 KiB
Rust
429 lines
14 KiB
Rust
use std::num::{NonZeroI32, NonZeroI64};
|
|
|
|
use watchexec::{
|
|
event::{filekind::*, ProcessEnd, Source},
|
|
filter::tagged::TaggedFilterer,
|
|
signal::{process::SubSignal, source::MainSignal},
|
|
};
|
|
|
|
mod helpers;
|
|
use helpers::tagged::*;
|
|
|
|
#[tokio::test]
|
|
async fn empty_filter_passes_everything() {
|
|
let filterer = filt(&[]).await;
|
|
|
|
filterer.source_does_pass(Source::Keyboard);
|
|
filterer.fek_does_pass(FileEventKind::Create(CreateKind::File));
|
|
filterer.pid_does_pass(1234);
|
|
filterer.signal_does_pass(MainSignal::User1);
|
|
filterer.complete_does_pass(None);
|
|
filterer.complete_does_pass(Some(ProcessEnd::Success));
|
|
}
|
|
|
|
// Source is used as a relatively simple test case for common text-based ops, so
|
|
// these aren't repeated for the other tags, which instead focus on their own
|
|
// special characteristics.
|
|
|
|
#[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);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn source_glob() {
|
|
let filterer = filt(&[filter("source*=*i*m*")]).await;
|
|
|
|
filterer.source_does_pass(Source::Filesystem);
|
|
filterer.source_does_pass(Source::Time);
|
|
filterer.source_doesnt_pass(Source::Internal);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn source_regex() {
|
|
let filterer = filt(&[filter("source~=(keyboard|mouse)")]).await;
|
|
|
|
filterer.source_does_pass(Source::Keyboard);
|
|
filterer.source_does_pass(Source::Mouse);
|
|
filterer.source_doesnt_pass(Source::Internal);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn source_two_filters() {
|
|
let filterer = filt(&[filter("source*=*s*"), filter("source!=mouse")]).await;
|
|
|
|
filterer.source_doesnt_pass(Source::Mouse);
|
|
filterer.source_does_pass(Source::Filesystem);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn source_allowlisting() {
|
|
// allowlisting is vastly easier to achieve with e.g. `source==mouse`
|
|
// but this pattern is nonetheless useful for more complex cases.
|
|
let filterer = filt(&[filter("source*!*"), filter("!source==mouse")]).await;
|
|
|
|
filterer.source_does_pass(Source::Mouse);
|
|
filterer.source_doesnt_pass(Source::Filesystem);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn source_set() {
|
|
let f = filter("source:=keyboard,mouse");
|
|
assert_eq!(f, filter("source=keyboard,mouse"));
|
|
|
|
let filterer = filt(&[f]).await;
|
|
filterer.source_does_pass(Source::Keyboard);
|
|
filterer.source_does_pass(Source::Mouse);
|
|
filterer.source_doesnt_pass(Source::Internal);
|
|
|
|
let filterer = filt(&[filter("source:!keyboard,mouse")]).await;
|
|
filterer.source_doesnt_pass(Source::Keyboard);
|
|
filterer.source_doesnt_pass(Source::Mouse);
|
|
filterer.source_does_pass(Source::Internal);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn fek_glob_level_one() {
|
|
let f = filter("kind*=Create(*)");
|
|
assert_eq!(f, filter("fek*=Create(*)"));
|
|
assert_eq!(f, filter("kind=Create(*)"));
|
|
assert_eq!(f, filter("fek=Create(*)"));
|
|
|
|
let filterer = filt(&[f]).await;
|
|
|
|
filterer.fek_does_pass(FileEventKind::Create(CreateKind::Any));
|
|
filterer.fek_does_pass(FileEventKind::Create(CreateKind::File));
|
|
filterer.fek_doesnt_pass(FileEventKind::Modify(ModifyKind::Data(DataChange::Content)));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn fek_glob_level_two() {
|
|
let filterer = filt(&[filter("fek=Modify(Data(*))")]).await;
|
|
|
|
filterer.fek_does_pass(FileEventKind::Modify(ModifyKind::Data(DataChange::Content)));
|
|
filterer.fek_doesnt_pass(FileEventKind::Modify(ModifyKind::Other));
|
|
filterer.fek_doesnt_pass(FileEventKind::Modify(ModifyKind::Metadata(
|
|
MetadataKind::Permissions,
|
|
)));
|
|
filterer.fek_doesnt_pass(FileEventKind::Create(CreateKind::Any));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn fek_level_three() {
|
|
fn suite(filterer: &TaggedFilterer) {
|
|
filterer.fek_does_pass(FileEventKind::Modify(ModifyKind::Data(DataChange::Content)));
|
|
filterer.fek_doesnt_pass(FileEventKind::Modify(ModifyKind::Data(DataChange::Size)));
|
|
filterer.fek_doesnt_pass(FileEventKind::Modify(ModifyKind::Other));
|
|
filterer.fek_doesnt_pass(FileEventKind::Modify(ModifyKind::Metadata(
|
|
MetadataKind::Permissions,
|
|
)));
|
|
filterer.fek_doesnt_pass(FileEventKind::Create(CreateKind::Any));
|
|
}
|
|
|
|
suite(filt(&[filter("fek=Modify(Data(Content))")]).await.as_ref());
|
|
suite(filt(&[filter("fek==Modify(Data(Content))")]).await.as_ref());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn pid_set_single() {
|
|
let f = filter("process:=1234");
|
|
assert_eq!(f, filter("pid:=1234"));
|
|
assert_eq!(f, filter("process=1234"));
|
|
assert_eq!(f, filter("pid=1234"));
|
|
|
|
let filterer = filt(&[f]).await;
|
|
|
|
filterer.pid_does_pass(1234);
|
|
filterer.pid_doesnt_pass(5678);
|
|
filterer.pid_doesnt_pass(12345);
|
|
filterer.pid_doesnt_pass(123);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn pid_set_multiple() {
|
|
let filterer = filt(&[filter("pid=123,456")]).await;
|
|
|
|
filterer.pid_does_pass(123);
|
|
filterer.pid_does_pass(456);
|
|
filterer.pid_doesnt_pass(123456);
|
|
filterer.pid_doesnt_pass(12);
|
|
filterer.pid_doesnt_pass(23);
|
|
filterer.pid_doesnt_pass(45);
|
|
filterer.pid_doesnt_pass(56);
|
|
filterer.pid_doesnt_pass(1234);
|
|
filterer.pid_doesnt_pass(3456);
|
|
filterer.pid_doesnt_pass(4567);
|
|
filterer.pid_doesnt_pass(34567);
|
|
filterer.pid_doesnt_pass(0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn pid_equals() {
|
|
let f = filter("process==1234");
|
|
assert_eq!(f, filter("pid==1234"));
|
|
|
|
let filterer = filt(&[f]).await;
|
|
|
|
filterer.pid_does_pass(1234);
|
|
filterer.pid_doesnt_pass(5678);
|
|
filterer.pid_doesnt_pass(12345);
|
|
filterer.pid_doesnt_pass(123);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_set_single_without_sig() {
|
|
let f = filter("signal=INT");
|
|
assert_eq!(f, filter("sig=INT"));
|
|
assert_eq!(f, filter("signal:=INT"));
|
|
assert_eq!(f, filter("sig:=INT"));
|
|
|
|
let filterer = filt(&[f]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_doesnt_pass(MainSignal::Hangup);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_set_single_with_sig() {
|
|
let filterer = filt(&[filter("signal:=SIGINT")]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_doesnt_pass(MainSignal::Hangup);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_set_multiple_without_sig() {
|
|
let filterer = filt(&[filter("sig:=INT,TERM")]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_does_pass(MainSignal::Terminate);
|
|
filterer.signal_doesnt_pass(MainSignal::Hangup);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_set_multiple_with_sig() {
|
|
let filterer = filt(&[filter("signal:=SIGINT,SIGTERM")]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_does_pass(MainSignal::Terminate);
|
|
filterer.signal_doesnt_pass(MainSignal::Hangup);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_set_multiple_mixed_sig() {
|
|
let filterer = filt(&[filter("sig:=SIGINT,TERM")]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_does_pass(MainSignal::Terminate);
|
|
filterer.signal_doesnt_pass(MainSignal::Hangup);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_equals_without_sig() {
|
|
let filterer = filt(&[filter("sig==INT")]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_doesnt_pass(MainSignal::Hangup);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_equals_with_sig() {
|
|
let filterer = filt(&[filter("signal==SIGINT")]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_doesnt_pass(MainSignal::Hangup);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_set_single_numbers() {
|
|
let filterer = filt(&[filter("signal:=2")]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_doesnt_pass(MainSignal::Hangup);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_set_multiple_numbers() {
|
|
let filterer = filt(&[filter("sig:=2,15")]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_does_pass(MainSignal::Terminate);
|
|
filterer.signal_doesnt_pass(MainSignal::Hangup);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_equals_numbers() {
|
|
let filterer = filt(&[filter("sig==2")]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_doesnt_pass(MainSignal::Hangup);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn signal_set_all_mixed() {
|
|
let filterer = filt(&[filter("signal:=SIGHUP,INT,15")]).await;
|
|
|
|
filterer.signal_does_pass(MainSignal::Hangup);
|
|
filterer.signal_does_pass(MainSignal::Interrupt);
|
|
filterer.signal_does_pass(MainSignal::Terminate);
|
|
filterer.signal_doesnt_pass(MainSignal::User1);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_empty() {
|
|
let f = filter("complete=_");
|
|
assert_eq!(f, filter("complete*=_"));
|
|
assert_eq!(f, filter("exit=_"));
|
|
assert_eq!(f, filter("exit*=_"));
|
|
|
|
let filterer = filt(&[f]).await;
|
|
|
|
filterer.complete_does_pass(None);
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitError(NonZeroI64::new(1).unwrap())));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_any() {
|
|
let filterer = filt(&[filter("complete=*")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitError(NonZeroI64::new(1).unwrap())));
|
|
filterer.complete_does_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_success() {
|
|
let filterer = filt(&[filter("complete*=success")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitError(NonZeroI64::new(1).unwrap())));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_continued() {
|
|
let filterer = filt(&[filter("complete*=continued")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::Continued));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitError(NonZeroI64::new(1).unwrap())));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_specific_exit_error() {
|
|
let filterer = filt(&[filter("complete*=error(1)")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitError(NonZeroI64::new(1).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_any_exit_error() {
|
|
let filterer = filt(&[filter("complete*=error(*)")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitError(NonZeroI64::new(1).unwrap())));
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitError(NonZeroI64::new(63).unwrap())));
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitError(
|
|
NonZeroI64::new(-12823912738).unwrap(),
|
|
)));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitStop(NonZeroI32::new(63).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_specific_stop() {
|
|
let filterer = filt(&[filter("complete*=stop(19)")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitStop(NonZeroI32::new(19).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_any_stop() {
|
|
let filterer = filt(&[filter("complete*=stop(*)")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitStop(NonZeroI32::new(1).unwrap())));
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitStop(NonZeroI32::new(63).unwrap())));
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitStop(
|
|
NonZeroI32::new(-128239127).unwrap(),
|
|
)));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitError(NonZeroI64::new(63).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_specific_exception() {
|
|
let filterer = filt(&[filter("complete*=exception(4B53)")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::Exception(NonZeroI32::new(19283).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_any_exception() {
|
|
let filterer = filt(&[filter("complete*=exception(*)")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::Exception(NonZeroI32::new(1).unwrap())));
|
|
filterer.complete_does_pass(Some(ProcessEnd::Exception(NonZeroI32::new(63).unwrap())));
|
|
filterer.complete_does_pass(Some(ProcessEnd::Exception(
|
|
NonZeroI32::new(-128239127).unwrap(),
|
|
)));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitStop(NonZeroI32::new(63).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitError(NonZeroI64::new(63).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_specific_signal_with_sig() {
|
|
let filterer = filt(&[filter("complete*=signal(SIGINT)")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitSignal(SubSignal::Interrupt)));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitStop(NonZeroI32::new(19).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_specific_signal_without_sig() {
|
|
let filterer = filt(&[filter("complete*=signal(INT)")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitSignal(SubSignal::Interrupt)));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitStop(NonZeroI32::new(19).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_specific_signal_number() {
|
|
let filterer = filt(&[filter("complete*=signal(2)")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitSignal(SubSignal::Interrupt)));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitStop(NonZeroI32::new(19).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn complete_with_any_signal() {
|
|
let filterer = filt(&[filter("complete*=signal(*)")]).await;
|
|
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitSignal(SubSignal::Interrupt)));
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitSignal(SubSignal::Terminate)));
|
|
filterer.complete_does_pass(Some(ProcessEnd::ExitSignal(SubSignal::Custom(123))));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitStop(NonZeroI32::new(63).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::ExitError(NonZeroI64::new(63).unwrap())));
|
|
filterer.complete_doesnt_pass(Some(ProcessEnd::Success));
|
|
filterer.complete_doesnt_pass(None);
|
|
}
|