mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-30 11:32:04 +03:00
copy tests from notify-debouncer-full
as well (verbatim)
While at it, remove the docs as they are referring to the original source code. Tests should help to understand how it is supposed to work. Note that the copy is verbatim and modifications will happen in a separate commit, along with the necessary license declarations.
This commit is contained in:
parent
86edb4627d
commit
edd8d54950
63
Cargo.lock
generated
63
Cargo.lock
generated
@ -1228,6 +1228,15 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deser-hjson"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30e1ab99fef4d11b2de312a0650bbf312fb48aa11a00084f35b27bf8c57d4cad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
@ -1847,6 +1856,12 @@ version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.30"
|
||||
@ -2211,13 +2226,20 @@ dependencies = [
|
||||
"anyhow",
|
||||
"backoff",
|
||||
"crossbeam-channel",
|
||||
"deser-hjson",
|
||||
"file-id",
|
||||
"futures",
|
||||
"gitbutler-core",
|
||||
"gitbutler-watcher",
|
||||
"gix",
|
||||
"itertools 0.12.1",
|
||||
"mock_instant",
|
||||
"notify",
|
||||
"parking_lot 0.12.1",
|
||||
"pretty_assertions",
|
||||
"rand 0.8.5",
|
||||
"rstest",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@ -3986,6 +4008,12 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mock_instant"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9366861eb2a2c436c20b12c8dbec5f798cea6b47ad99216be0282942e2c81ea0"
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
@ -5183,6 +5211,12 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||
|
||||
[[package]]
|
||||
name = "relative-path"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
||||
|
||||
[[package]]
|
||||
name = "rend"
|
||||
version = "0.4.2"
|
||||
@ -5369,6 +5403,35 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest"
|
||||
version = "0.18.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"futures-timer",
|
||||
"rstest_macros",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest_macros"
|
||||
version = "0.18.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"glob",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"relative-path",
|
||||
"rustc_version",
|
||||
"syn 2.0.58",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.29.0"
|
||||
|
@ -7,6 +7,9 @@ publish = false
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[features]
|
||||
mock_instant = ["dep:mock_instant"]
|
||||
|
||||
[dependencies]
|
||||
gitbutler-core.workspace = true
|
||||
thiserror.workspace = true
|
||||
@ -25,6 +28,16 @@ walkdir = "2.2.2"
|
||||
crossbeam-channel = "0.5.12"
|
||||
itertools = "0.12"
|
||||
|
||||
mock_instant = { version = "0.3.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
gitbutler-watcher = { path = ".", features = ["mock_instant"] }
|
||||
pretty_assertions = "1.3.0"
|
||||
rstest = "0.18"
|
||||
serde = { version = "1.0.89", features = ["derive"] }
|
||||
deser-hjson = "1.1.1"
|
||||
rand = "0.8.5"
|
||||
|
||||
[lints.clippy]
|
||||
all = "deny"
|
||||
perf = "deny"
|
||||
|
@ -29,6 +29,10 @@
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[cfg(feature = "mock_instant")]
|
||||
use mock_instant::Instant;
|
||||
|
||||
#[cfg(not(feature = "mock_instant"))]
|
||||
use std::time::Instant;
|
||||
|
||||
use notify::Event;
|
||||
|
@ -27,70 +27,6 @@
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//! A debouncer for [notify] that is optimized for ease of use.
|
||||
//!
|
||||
//! * Only emits a single `Rename` event if the rename `From` and `To` events can be matched
|
||||
//! * Merges multiple `Rename` events
|
||||
//! * Takes `Rename` events into account and updates paths for events that occurred before the rename event, but which haven't been emitted, yet
|
||||
//! * Optionally keeps track of the file system IDs all files and stiches rename events together (FSevents, Windows)
|
||||
//! * Emits only one `Remove` event when deleting a directory (inotify)
|
||||
//! * Doesn't emit duplicate create events
|
||||
//! * Doesn't emit `Modify` events after a `Create` event
|
||||
//!
|
||||
//! # Installation
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! notify-debouncer-full = "0.3.1"
|
||||
//! ```
|
||||
//!
|
||||
//! In case you want to select specific features of notify,
|
||||
//! specify notify as dependency explicitly in your dependencies.
|
||||
//! Otherwise you can just use the re-export of notify from debouncer-full.
|
||||
//!
|
||||
//! ```toml
|
||||
//! notify-debouncer-full = "0.3.1"
|
||||
//! notify = { version = "..", features = [".."] }
|
||||
//! ```
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! # use std::path::Path;
|
||||
//! # use std::time::Duration;
|
||||
//! use notify_debouncer_full::{notify::*, new_debouncer, DebounceEventResult};
|
||||
//!
|
||||
//! // Select recommended watcher for debouncer.
|
||||
//! // Using a callback here, could also be a channel.
|
||||
//! let mut debouncer = new_debouncer(Duration::from_secs(2), None, |result: DebounceEventResult| {
|
||||
//! match result {
|
||||
//! Ok(events) => events.iter().for_each(|event| println!("{event:?}")),
|
||||
//! Err(errors) => errors.iter().for_each(|error| println!("{error:?}")),
|
||||
//! }
|
||||
//! }).unwrap();
|
||||
//!
|
||||
//! // Add a path to be watched. All files and directories at that path and
|
||||
//! // below will be monitored for changes.
|
||||
//! debouncer.watcher().watch(Path::new("."), RecursiveMode::Recursive).unwrap();
|
||||
//!
|
||||
//! // Add the same path to the file ID cache. The cache uses unique file IDs
|
||||
//! // provided by the file system and is used to stich together rename events
|
||||
//! // in case the notification back-end doesn't emit rename cookies.
|
||||
//! debouncer.cache().add_root(Path::new("."), RecursiveMode::Recursive);
|
||||
//! ```
|
||||
//!
|
||||
//! # Features
|
||||
//!
|
||||
//! The following crate features can be turned on or off in your cargo dependency config:
|
||||
//!
|
||||
//! - `crossbeam` enabled by default, adds [`DebounceEventHandler`](DebounceEventHandler) support for crossbeam channels.
|
||||
//! Also enables crossbeam-channel in the re-exported notify. You may want to disable this when using the tokio async runtime.
|
||||
//! - `serde` enables serde support for events.
|
||||
//!
|
||||
//! # Caveats
|
||||
//!
|
||||
//! As all file events are sourced from notify, the [known problems](https://docs.rs/notify/latest/notify/#known-problems) section applies here too.
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
path::PathBuf,
|
||||
@ -98,9 +34,15 @@ use std::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[cfg(feature = "mock_instant")]
|
||||
use mock_instant::Instant;
|
||||
|
||||
#[cfg(not(feature = "mock_instant"))]
|
||||
use std::time::Instant;
|
||||
|
||||
use file_id::FileId;
|
||||
use notify::{
|
||||
event::{ModifyKind, RemoveKind, RenameMode},
|
||||
@ -114,26 +56,6 @@ mod event;
|
||||
use cache::{FileIdCache, FileIdMap};
|
||||
use event::DebouncedEvent;
|
||||
|
||||
/// The set of requirements for watcher debounce event handling functions.
|
||||
///
|
||||
/// # Example implementation
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use notify::{Event, Result, EventHandler};
|
||||
/// # use notify_debouncer_full::{DebounceEventHandler, DebounceEventResult};
|
||||
///
|
||||
/// /// Prints received events
|
||||
/// struct EventPrinter;
|
||||
///
|
||||
/// impl DebounceEventHandler for EventPrinter {
|
||||
/// fn handle_event(&mut self, result: DebounceEventResult) {
|
||||
/// match result {
|
||||
/// Ok(events) => events.iter().for_each(|event| println!("{event:?}")),
|
||||
/// Err(errors) => errors.iter().for_each(|error| println!("{error:?}")),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait DebounceEventHandler: Send + 'static {
|
||||
/// Handles an event.
|
||||
fn handle_event(&mut self, event: DebounceEventResult);
|
||||
|
451
crates/gitbutler-watcher/tests/debouncer.rs
Normal file
451
crates/gitbutler-watcher/tests/debouncer.rs
Normal file
@ -0,0 +1,451 @@
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use file_id::FileId;
|
||||
use mock_instant::Instant;
|
||||
use notify::{
|
||||
event::{
|
||||
AccessKind, AccessMode, CreateKind, DataChange, Flag, MetadataKind, ModifyKind, RemoveKind,
|
||||
RenameMode,
|
||||
},
|
||||
Error, ErrorKind, Event, EventKind, RecursiveMode,
|
||||
};
|
||||
|
||||
use crate::{DebounceDataInner, DebouncedEvent, FileIdCache, Queue};
|
||||
|
||||
pub(crate) use schema::TestCase;
|
||||
|
||||
mod schema {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub(crate) struct Error {
|
||||
/// The error kind is parsed by `into_notify_error`
|
||||
pub kind: String,
|
||||
|
||||
/// The error paths
|
||||
#[serde(default)]
|
||||
pub paths: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub(crate) struct Event {
|
||||
/// The timestamp the event occurred
|
||||
#[serde(default)]
|
||||
pub time: u64,
|
||||
|
||||
/// The event kind is parsed by `into_notify_event`
|
||||
pub kind: String,
|
||||
|
||||
/// The event paths
|
||||
#[serde(default)]
|
||||
pub paths: Vec<String>,
|
||||
|
||||
/// The event flags
|
||||
#[serde(default)]
|
||||
pub flags: Vec<String>,
|
||||
|
||||
/// The event tracker
|
||||
pub tracker: Option<usize>,
|
||||
|
||||
/// The event info
|
||||
pub info: Option<String>,
|
||||
|
||||
/// The file id for the file associated with the event
|
||||
///
|
||||
/// Only used for the rename event.
|
||||
pub file_id: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub(crate) struct Queue {
|
||||
pub events: Vec<Event>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub(crate) struct State {
|
||||
/// Timeout for the debouncer
|
||||
///
|
||||
/// Only used for the initial state.
|
||||
pub timeout: Option<u64>,
|
||||
|
||||
/// The event queues for each file
|
||||
#[serde(default)]
|
||||
pub queues: HashMap<String, Queue>,
|
||||
|
||||
/// Cached file ids
|
||||
#[serde(default)]
|
||||
pub cache: HashMap<String, u64>,
|
||||
|
||||
/// A map of file ids, used instead of accessing the file system
|
||||
#[serde(default)]
|
||||
pub file_system: HashMap<String, u64>,
|
||||
|
||||
/// Current rename event
|
||||
pub rename_event: Option<Event>,
|
||||
|
||||
/// Current rescan event
|
||||
pub rescan_event: Option<Event>,
|
||||
|
||||
/// Debounced events
|
||||
///
|
||||
/// Only used for the expected state.
|
||||
#[serde(default)]
|
||||
pub events: HashMap<String, Vec<Event>>,
|
||||
|
||||
/// Errors
|
||||
#[serde(default)]
|
||||
pub errors: Vec<Error>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub(crate) struct TestCase {
|
||||
/// Initial state
|
||||
pub state: State,
|
||||
|
||||
/// Events that are added during the test
|
||||
#[serde(default)]
|
||||
pub events: Vec<Event>,
|
||||
|
||||
/// Errors that are added during the test
|
||||
#[serde(default)]
|
||||
pub errors: Vec<Error>,
|
||||
|
||||
/// Expected state after the test
|
||||
pub expected: State,
|
||||
}
|
||||
}
|
||||
|
||||
impl schema::Error {
|
||||
pub fn into_notify_error(self) -> Error {
|
||||
let kind = match &*self.kind {
|
||||
"path-not-found" => ErrorKind::PathNotFound,
|
||||
"watch-not-found" => ErrorKind::WatchNotFound,
|
||||
"max-files-watch" => ErrorKind::MaxFilesWatch,
|
||||
_ => panic!("unknown error type `{}`", self.kind),
|
||||
};
|
||||
let mut error = Error::new(kind);
|
||||
|
||||
for p in self.paths {
|
||||
error = error.add_path(PathBuf::from(p));
|
||||
}
|
||||
|
||||
error
|
||||
}
|
||||
}
|
||||
|
||||
impl schema::Event {
|
||||
#[rustfmt::skip]
|
||||
pub fn into_debounced_event(self, time: Instant, path: Option<&str>) -> DebouncedEvent {
|
||||
let kind = match &*self.kind {
|
||||
"any" => EventKind::Any,
|
||||
"other" => EventKind::Other,
|
||||
"access-any" => EventKind::Access(AccessKind::Any),
|
||||
"access-read" => EventKind::Access(AccessKind::Read),
|
||||
"access-open-any" => EventKind::Access(AccessKind::Open(AccessMode::Any)),
|
||||
"access-open-execute" => EventKind::Access(AccessKind::Open(AccessMode::Execute)),
|
||||
"access-open-read" => EventKind::Access(AccessKind::Open(AccessMode::Read)),
|
||||
"access-open-write" => EventKind::Access(AccessKind::Open(AccessMode::Write)),
|
||||
"access-open-other" => EventKind::Access(AccessKind::Open(AccessMode::Other)),
|
||||
"access-close-any" => EventKind::Access(AccessKind::Close(AccessMode::Any)),
|
||||
"access-close-execute" => EventKind::Access(AccessKind::Close(AccessMode::Execute)),
|
||||
"access-close-read" => EventKind::Access(AccessKind::Close(AccessMode::Read)),
|
||||
"access-close-write" => EventKind::Access(AccessKind::Close(AccessMode::Write)),
|
||||
"access-close-other" => EventKind::Access(AccessKind::Close(AccessMode::Other)),
|
||||
"access-other" => EventKind::Access(AccessKind::Other),
|
||||
"create-any" => EventKind::Create(CreateKind::Any),
|
||||
"create-file" => EventKind::Create(CreateKind::File),
|
||||
"create-folder" => EventKind::Create(CreateKind::Folder),
|
||||
"create-other" => EventKind::Create(CreateKind::Other),
|
||||
"modify-any" => EventKind::Modify(ModifyKind::Any),
|
||||
"modify-other" => EventKind::Modify(ModifyKind::Other),
|
||||
"modify-data-any" => EventKind::Modify(ModifyKind::Data(DataChange::Any)),
|
||||
"modify-data-size" => EventKind::Modify(ModifyKind::Data(DataChange::Size)),
|
||||
"modify-data-content" => EventKind::Modify(ModifyKind::Data(DataChange::Content)),
|
||||
"modify-data-other" => EventKind::Modify(ModifyKind::Data(DataChange::Other)),
|
||||
"modify-metadata-any" => EventKind::Modify(ModifyKind::Metadata(MetadataKind::Any)),
|
||||
"modify-metadata-access-time" => EventKind::Modify(ModifyKind::Metadata(MetadataKind::AccessTime)),
|
||||
"modify-metadata-write-time" => EventKind::Modify(ModifyKind::Metadata(MetadataKind::WriteTime)),
|
||||
"modify-metadata-permissions" => EventKind::Modify(ModifyKind::Metadata(MetadataKind::Permissions)),
|
||||
"modify-metadata-ownership" => EventKind::Modify(ModifyKind::Metadata(MetadataKind::Ownership)),
|
||||
"modify-metadata-extended" => EventKind::Modify(ModifyKind::Metadata(MetadataKind::Extended)),
|
||||
"modify-metadata-other" => EventKind::Modify(ModifyKind::Metadata(MetadataKind::Other)),
|
||||
"rename-any" => EventKind::Modify(ModifyKind::Name(RenameMode::Any)),
|
||||
"rename-from" => EventKind::Modify(ModifyKind::Name(RenameMode::From)),
|
||||
"rename-to" => EventKind::Modify(ModifyKind::Name(RenameMode::To)),
|
||||
"rename-both" => EventKind::Modify(ModifyKind::Name(RenameMode::Both)),
|
||||
"rename-other" => EventKind::Modify(ModifyKind::Name(RenameMode::Other)),
|
||||
"remove-any" => EventKind::Remove(RemoveKind::Any),
|
||||
"remove-file" => EventKind::Remove(RemoveKind::File),
|
||||
"remove-folder" => EventKind::Remove(RemoveKind::Folder),
|
||||
"remove-other" => EventKind::Remove(RemoveKind::Other),
|
||||
_ => panic!("unknown event type `{}`", self.kind),
|
||||
};
|
||||
let mut event = Event::new(kind);
|
||||
|
||||
for p in self.paths {
|
||||
event = event.add_path(if p == "*" {
|
||||
PathBuf::from(path.expect("cannot replace `*`"))
|
||||
} else {
|
||||
PathBuf::from(p)
|
||||
});
|
||||
|
||||
if let Some(tracker) = self.tracker {
|
||||
event = event.set_tracker(tracker);
|
||||
}
|
||||
|
||||
if let Some(info) = &self.info {
|
||||
event = event.set_info(info.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
for f in self.flags {
|
||||
let flag = match &*f {
|
||||
"rescan" => Flag::Rescan,
|
||||
_ => panic!("unknown event flag `{f}`"),
|
||||
};
|
||||
|
||||
event = event.set_flag(flag);
|
||||
}
|
||||
|
||||
DebouncedEvent { event, time: time + Duration::from_millis(self.time) }
|
||||
}
|
||||
}
|
||||
|
||||
impl schema::State {
|
||||
pub(crate) fn into_debounce_data_inner(self, time: Instant) -> DebounceDataInner<TestCache> {
|
||||
let queues = self
|
||||
.queues
|
||||
.into_iter()
|
||||
.map(|(path, queue)| {
|
||||
let queue = Queue {
|
||||
events: queue
|
||||
.events
|
||||
.into_iter()
|
||||
.map(|event| event.into_debounced_event(time, Some(&path)))
|
||||
.collect::<VecDeque<_>>(),
|
||||
};
|
||||
(path.into(), queue)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let cache = self
|
||||
.cache
|
||||
.into_iter()
|
||||
.map(|(path, id)| {
|
||||
let path = PathBuf::from(path);
|
||||
let id = FileId::new_inode(id, id);
|
||||
(path, id)
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let file_system = self
|
||||
.file_system
|
||||
.into_iter()
|
||||
.map(|(path, id)| {
|
||||
let path = PathBuf::from(path);
|
||||
let id = FileId::new_inode(id, id);
|
||||
(path, id)
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let cache = TestCache::new(cache, file_system);
|
||||
|
||||
let rename_event = self.rename_event.map(|e| {
|
||||
let file_id = e.file_id.map(|id| FileId::new_inode(id, id));
|
||||
let event = e.into_debounced_event(time, None);
|
||||
(event, file_id)
|
||||
});
|
||||
|
||||
let rescan_event = self
|
||||
.rescan_event
|
||||
.map(|e| e.into_debounced_event(time, None));
|
||||
|
||||
DebounceDataInner {
|
||||
queues,
|
||||
roots: Vec::new(),
|
||||
cache,
|
||||
rename_event,
|
||||
rescan_event,
|
||||
errors: Vec::new(),
|
||||
timeout: Duration::from_millis(self.timeout.unwrap_or(50)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestCache {
|
||||
pub paths: HashMap<PathBuf, FileId>,
|
||||
pub file_system: HashMap<PathBuf, FileId>,
|
||||
}
|
||||
|
||||
impl TestCache {
|
||||
pub fn new(paths: HashMap<PathBuf, FileId>, file_system: HashMap<PathBuf, FileId>) -> Self {
|
||||
Self { paths, file_system }
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIdCache for TestCache {
|
||||
fn cached_file_id(&self, path: &Path) -> Option<&FileId> {
|
||||
self.paths.get(path)
|
||||
}
|
||||
|
||||
fn add_path(&mut self, path: &Path, recursive_mode: RecursiveMode) {
|
||||
for (file_path, file_id) in &self.file_system {
|
||||
if file_path == path
|
||||
|| (file_path.starts_with(path) && recursive_mode == RecursiveMode::Recursive)
|
||||
{
|
||||
self.paths.insert(file_path.clone(), *file_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_path(&mut self, path: &Path) {
|
||||
self.paths.remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use super::*;
|
||||
|
||||
use mock_instant::MockClock;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
use testing::TestCase;
|
||||
|
||||
#[rstest]
|
||||
fn state(
|
||||
#[values(
|
||||
"add_create_event",
|
||||
"add_create_event_after_remove_event",
|
||||
"add_create_dir_event_twice",
|
||||
"add_modify_content_event_after_create_event",
|
||||
"add_rename_from_event",
|
||||
"add_rename_from_event_after_create_event",
|
||||
"add_rename_from_event_after_modify_event",
|
||||
"add_rename_from_event_after_create_and_modify_event",
|
||||
"add_rename_from_event_after_rename_from_event",
|
||||
"add_rename_to_event",
|
||||
"add_rename_to_dir_event",
|
||||
"add_rename_from_and_to_event",
|
||||
"add_rename_from_and_to_event_after_create",
|
||||
"add_rename_from_and_to_event_after_rename",
|
||||
"add_rename_from_and_to_event_after_modify_content",
|
||||
"add_rename_from_and_to_event_override_created",
|
||||
"add_rename_from_and_to_event_override_modified",
|
||||
"add_rename_from_and_to_event_override_removed",
|
||||
"add_rename_from_and_to_event_with_file_ids",
|
||||
"add_rename_from_and_to_event_with_different_file_ids",
|
||||
"add_rename_from_and_to_event_with_different_tracker",
|
||||
"add_rename_both_event",
|
||||
"add_remove_event",
|
||||
"add_remove_event_after_create_event",
|
||||
"add_remove_event_after_modify_event",
|
||||
"add_remove_event_after_create_and_modify_event",
|
||||
"add_remove_parent_event_after_remove_child_event",
|
||||
"add_errors",
|
||||
"emit_continuous_modify_content_events",
|
||||
"emit_events_in_chronological_order",
|
||||
"emit_events_with_a_prepended_rename_event",
|
||||
"emit_close_events_only_once",
|
||||
"emit_modify_event_after_close_event",
|
||||
"emit_needs_rescan_event",
|
||||
"read_file_id_without_create_event"
|
||||
)]
|
||||
file_name: &str,
|
||||
) {
|
||||
let file_content =
|
||||
fs::read_to_string(Path::new(&format!("./test_cases/{file_name}.hjson"))).unwrap();
|
||||
let mut test_case = deser_hjson::from_str::<TestCase>(&file_content).unwrap();
|
||||
|
||||
MockClock::set_time(Duration::default());
|
||||
|
||||
let time = Instant::now();
|
||||
|
||||
let mut state = test_case.state.into_debounce_data_inner(time);
|
||||
state.roots = vec![(PathBuf::from("/"), RecursiveMode::Recursive)];
|
||||
|
||||
for event in test_case.events {
|
||||
let event = event.into_debounced_event(time, None);
|
||||
MockClock::set_time(event.time - time);
|
||||
state.add_event(event.event);
|
||||
}
|
||||
|
||||
for error in test_case.errors {
|
||||
let error = error.into_notify_error();
|
||||
state.add_error(error);
|
||||
}
|
||||
|
||||
let expected_errors = std::mem::take(&mut test_case.expected.errors);
|
||||
let expected_events = std::mem::take(&mut test_case.expected.events);
|
||||
let expected_state = test_case.expected.into_debounce_data_inner(time);
|
||||
assert_eq!(
|
||||
state.queues, expected_state.queues,
|
||||
"queues not as expected"
|
||||
);
|
||||
assert_eq!(
|
||||
state.rename_event, expected_state.rename_event,
|
||||
"rename event not as expected"
|
||||
);
|
||||
assert_eq!(
|
||||
state.rescan_event, expected_state.rescan_event,
|
||||
"rescan event not as expected"
|
||||
);
|
||||
assert_eq!(
|
||||
state.cache.paths, expected_state.cache.paths,
|
||||
"cache not as expected"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
state
|
||||
.errors
|
||||
.iter()
|
||||
.map(|e| format!("{:?}", e))
|
||||
.collect::<Vec<_>>(),
|
||||
expected_errors
|
||||
.iter()
|
||||
.map(|e| format!("{:?}", e.clone().into_notify_error()))
|
||||
.collect::<Vec<_>>(),
|
||||
"errors not as expected"
|
||||
);
|
||||
|
||||
let backup_time = Instant::now().duration_since(time);
|
||||
let backup_queues = state.queues.clone();
|
||||
|
||||
for (delay, events) in expected_events {
|
||||
MockClock::set_time(backup_time);
|
||||
state.queues = backup_queues.clone();
|
||||
|
||||
match delay.as_str() {
|
||||
"none" => {}
|
||||
"short" => MockClock::advance(Duration::from_millis(10)),
|
||||
"long" => MockClock::advance(Duration::from_millis(100)),
|
||||
_ => {
|
||||
if let Ok(ts) = delay.parse::<u64>() {
|
||||
let ts = time + Duration::from_millis(ts);
|
||||
MockClock::set_time(ts - time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let events = events
|
||||
.into_iter()
|
||||
.map(|event| event.into_debounced_event(time, None))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
state.debounced_events(),
|
||||
events,
|
||||
"debounced events after a `{delay}` delay"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
20
crates/gitbutler-watcher/tests/fixtures/add_create_dir_event_twice.hjson
vendored
Normal file
20
crates/gitbutler-watcher/tests/fixtures/add_create_dir_event_twice.hjson
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// https://github.com/spacedriveapp/spacedrive/blob/90a350946914be7f91ba692887ca03db659d530a/core/src/location/manager/watcher/macos.rs
|
||||
//
|
||||
// This is a MacOS specific event that happens when a folder is created trough Finder.
|
||||
// It creates a folder but 2 events are triggered in FSEvents.
|
||||
{
|
||||
state: {}
|
||||
events: [
|
||||
{ kind: "create-folder", paths: ["/watch/dir"] }
|
||||
{ kind: "create-folder", paths: ["/watch/dir"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/dir: {
|
||||
events: [
|
||||
{ kind: "create-folder", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
crates/gitbutler-watcher/tests/fixtures/add_create_event.hjson
vendored
Normal file
29
crates/gitbutler-watcher/tests/fixtures/add_create_event.hjson
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
state: {
|
||||
file_system: {
|
||||
/watch/file: 1
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
cache: {
|
||||
/watch/file: 1
|
||||
}
|
||||
events: {
|
||||
none: []
|
||||
short: []
|
||||
long: [
|
||||
{ kind: "create-any", paths: ["/watch/file"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
24
crates/gitbutler-watcher/tests/fixtures/add_create_event_after_remove_event.hjson
vendored
Normal file
24
crates/gitbutler-watcher/tests/fixtures/add_create_event_after_remove_event.hjson
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["*"] }
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
crates/gitbutler-watcher/tests/fixtures/add_errors.hjson
vendored
Normal file
13
crates/gitbutler-watcher/tests/fixtures/add_errors.hjson
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
state: {}
|
||||
errors: [
|
||||
{ kind: "max-files-watch" }
|
||||
{ kind: "path-not-found", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {
|
||||
errors: [
|
||||
{ kind: "max-files-watch" }
|
||||
{ kind: "path-not-found", paths: ["/watch/file"] }
|
||||
]
|
||||
}
|
||||
}
|
26
crates/gitbutler-watcher/tests/fixtures/add_modify_content_event_after_create_event.hjson
vendored
Normal file
26
crates/gitbutler-watcher/tests/fixtures/add_modify_content_event_after_create_event.hjson
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// https://github.com/spacedriveapp/spacedrive/blob/90a350946914be7f91ba692887ca03db659d530a/core/src/location/manager/watcher/macos.rs
|
||||
|
||||
// MacOS emits a Create File and then an Update Content event when a file is created.
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "create-file", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "create-file", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
crates/gitbutler-watcher/tests/fixtures/add_remove_event.hjson
vendored
Normal file
15
crates/gitbutler-watcher/tests/fixtures/add_remove_event.hjson
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
state: {}
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
crates/gitbutler-watcher/tests/fixtures/add_remove_event_after_create_and_modify_event.hjson
vendored
Normal file
16
crates/gitbutler-watcher/tests/fixtures/add_remove_event_after_create_and_modify_event.hjson
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
{ kind: "modify-data-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {}
|
||||
}
|
15
crates/gitbutler-watcher/tests/fixtures/add_remove_event_after_create_event.hjson
vendored
Normal file
15
crates/gitbutler-watcher/tests/fixtures/add_remove_event_after_create_event.hjson
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {}
|
||||
}
|
23
crates/gitbutler-watcher/tests/fixtures/add_remove_event_after_modify_event.hjson
vendored
Normal file
23
crates/gitbutler-watcher/tests/fixtures/add_remove_event_after_modify_event.hjson
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "modify-data-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
crates/gitbutler-watcher/tests/fixtures/add_remove_parent_event_after_remove_child_event.hjson
vendored
Normal file
25
crates/gitbutler-watcher/tests/fixtures/add_remove_parent_event_after_remove_child_event.hjson
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
state: {
|
||||
cache: {
|
||||
/watch: 1
|
||||
/watch/parent: 2
|
||||
/watch/parent/child: 3
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["/watch/parent/child"] }
|
||||
{ kind: "remove-any", paths: ["/watch/parent"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/parent: {
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
cache: {
|
||||
/watch: 1
|
||||
}
|
||||
}
|
||||
}
|
7
crates/gitbutler-watcher/tests/fixtures/add_rename_both_event.hjson
vendored
Normal file
7
crates/gitbutler-watcher/tests/fixtures/add_rename_both_event.hjson
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
state: {}
|
||||
events: [
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"] }
|
||||
]
|
||||
expected: {}
|
||||
}
|
16
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event.hjson
vendored
Normal file
16
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event.hjson
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
state: {}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/source"], tracker: 1 }
|
||||
{ kind: "rename-to", paths: ["/watch/target"], tracker: 1 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"], tracker: 1 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_after_create.hjson
vendored
Normal file
24
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_after_create.hjson
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/source: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/source"], tracker: 1 }
|
||||
{ kind: "rename-to", paths: ["/watch/target"], tracker: 1 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_after_modify_content.hjson
vendored
Normal file
25
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_after_modify_content.hjson
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/source: {
|
||||
events: [
|
||||
{ kind: "modify-data-content", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/source"], tracker: 1 }
|
||||
{ kind: "rename-to", paths: ["/watch/target"], tracker: 1 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"], tracker: 1 }
|
||||
{ kind: "modify-data-content", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_after_rename.hjson
vendored
Normal file
24
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_after_rename.hjson
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/temp: {
|
||||
events: [
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/temp"], tracker: 1, time: 1 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/temp"], tracker: 2, time: 2 }
|
||||
{ kind: "rename-to", paths: ["/watch/target"], tracker: 2, time: 3 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"], tracker: 2, time: 1 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_override_created.hjson
vendored
Normal file
24
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_override_created.hjson
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/source"], tracker: 1 }
|
||||
{ kind: "rename-to", paths: ["/watch/target"], tracker: 1 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"], tracker: 1 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_override_modified.hjson
vendored
Normal file
35
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_override_modified.hjson
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "modify-data-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
cache: {
|
||||
/watch/target: 1
|
||||
/watch/source: 2
|
||||
}
|
||||
file_system: {
|
||||
/watch/target: 2
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/source"], tracker: 1 }
|
||||
{ kind: "rename-to", paths: ["/watch/target"], tracker: 1 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["*"], info: "override" }
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"], tracker: 1 }
|
||||
]
|
||||
}
|
||||
}
|
||||
cache: {
|
||||
/watch/target: 2
|
||||
}
|
||||
}
|
||||
}
|
25
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_override_removed.hjson
vendored
Normal file
25
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_override_removed.hjson
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/source"], tracker: 1 }
|
||||
{ kind: "rename-to", paths: ["/watch/target"], tracker: 1 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "remove-any", paths: ["*"] }
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"], tracker: 1 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
state: {
|
||||
cache: {
|
||||
/watch/source: 1
|
||||
}
|
||||
file_system: {
|
||||
/watch/target: 2
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/source"] }
|
||||
{ kind: "rename-to", paths: ["/watch/target"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/source: {
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "rename-to", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
cache: {
|
||||
/watch/target: 2
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
{
|
||||
state: {}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/source"], tracker: 1 }
|
||||
{ kind: "rename-to", paths: ["/watch/target"], tracker: 2 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/source: {
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["*"], tracker: 1 }
|
||||
]
|
||||
}
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "rename-to", paths: ["*"], tracker: 2 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_with_file_ids.hjson
vendored
Normal file
26
crates/gitbutler-watcher/tests/fixtures/add_rename_from_and_to_event_with_file_ids.hjson
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
state: {
|
||||
cache: {
|
||||
/watch/source: 1
|
||||
}
|
||||
file_system: {
|
||||
/watch/target: 1
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/source"] }
|
||||
{ kind: "rename-to", paths: ["/watch/target"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
cache: {
|
||||
/watch/target: 1
|
||||
}
|
||||
}
|
||||
}
|
16
crates/gitbutler-watcher/tests/fixtures/add_rename_from_event.hjson
vendored
Normal file
16
crates/gitbutler-watcher/tests/fixtures/add_rename_from_event.hjson
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
state: {}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/source"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/source: {
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
rename_event: { kind: "rename-from", paths: ["/watch/source"] }
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
{ kind: "modify-data-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
{ kind: "modify-data-any", paths: ["*"] }
|
||||
{ kind: "rename-from", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
rename_event: { kind: "rename-from", paths: ["/watch/file"] }
|
||||
}
|
||||
}
|
25
crates/gitbutler-watcher/tests/fixtures/add_rename_from_event_after_create_event.hjson
vendored
Normal file
25
crates/gitbutler-watcher/tests/fixtures/add_rename_from_event_after_create_event.hjson
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"] }
|
||||
{ kind: "rename-from", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
rename_event: { kind: "rename-from", paths: ["/watch/file"] }
|
||||
}
|
||||
}
|
25
crates/gitbutler-watcher/tests/fixtures/add_rename_from_event_after_modify_event.hjson
vendored
Normal file
25
crates/gitbutler-watcher/tests/fixtures/add_rename_from_event_after_modify_event.hjson
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "modify-data-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "modify-data-any", paths: ["*"] }
|
||||
{ kind: "rename-from", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
rename_event: { kind: "rename-from", paths: ["/watch/file"] }
|
||||
}
|
||||
}
|
30
crates/gitbutler-watcher/tests/fixtures/add_rename_from_event_after_rename_from_event.hjson
vendored
Normal file
30
crates/gitbutler-watcher/tests/fixtures/add_rename_from_event_after_rename_from_event.hjson
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/file-a: {
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
rename_event: { kind: "rename-from", paths: ["/watch/file-a"] }
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["/watch/file-b"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file-a: {
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
/watch/file-b: {
|
||||
events: [
|
||||
{ kind: "rename-from", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
rename_event: { kind: "rename-from", paths: ["/watch/file-b"] }
|
||||
}
|
||||
}
|
24
crates/gitbutler-watcher/tests/fixtures/add_rename_to_dir_event.hjson
vendored
Normal file
24
crates/gitbutler-watcher/tests/fixtures/add_rename_to_dir_event.hjson
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
state: {
|
||||
file_system: {
|
||||
/watch/parent: 1
|
||||
/watch/parent/child: 2
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "rename-to", paths: ["/watch/parent"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/parent: {
|
||||
events: [
|
||||
{ kind: "rename-to", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
cache: {
|
||||
/watch/parent: 1
|
||||
/watch/parent/child: 2
|
||||
}
|
||||
}
|
||||
}
|
15
crates/gitbutler-watcher/tests/fixtures/add_rename_to_event.hjson
vendored
Normal file
15
crates/gitbutler-watcher/tests/fixtures/add_rename_to_event.hjson
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
state: {}
|
||||
events: [
|
||||
{ kind: "rename-to", paths: ["/watch/target"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "rename-to", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
crates/gitbutler-watcher/tests/fixtures/emit_close_events_only_once.hjson
vendored
Normal file
28
crates/gitbutler-watcher/tests/fixtures/emit_close_events_only_once.hjson
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
state: {}
|
||||
events: [
|
||||
{ kind: "modify-data-any", paths: ["/watch/file"], time: 1 }
|
||||
{ kind: "access-close-write", paths: ["/watch/file"], time: 2 }
|
||||
{ kind: "modify-data-any", paths: ["/watch/file"], time: 3 }
|
||||
{ kind: "access-close-write", paths: ["/watch/file"], time: 4 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "modify-data-any", paths: ["*"], time: 1 }
|
||||
{ kind: "access-close-write", paths: ["*"], time: 2 }
|
||||
{ kind: "modify-data-any", paths: ["*"], time: 3 }
|
||||
{ kind: "access-close-write", paths: ["*"], time: 4 }
|
||||
]
|
||||
}
|
||||
}
|
||||
events: {
|
||||
short: []
|
||||
long: [
|
||||
{ kind: "modify-data-any", paths: ["/watch/file"], time: 3 }
|
||||
{ kind: "access-close-write", paths: ["/watch/file"], time: 4 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
49
crates/gitbutler-watcher/tests/fixtures/emit_continuous_modify_content_events.hjson
vendored
Normal file
49
crates/gitbutler-watcher/tests/fixtures/emit_continuous_modify_content_events.hjson
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
state: {
|
||||
timeout: 5
|
||||
}
|
||||
events: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"], time: 1 }
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"], time: 2 }
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"], time: 3 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "modify-data-content", paths: ["*"], time: 1 }
|
||||
{ kind: "modify-data-content", paths: ["*"], time: 2 }
|
||||
{ kind: "modify-data-content", paths: ["*"], time: 3 }
|
||||
]
|
||||
}
|
||||
}
|
||||
events: {
|
||||
1: []
|
||||
2: []
|
||||
3: []
|
||||
4: []
|
||||
5: []
|
||||
6: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"], time: 1 }
|
||||
]
|
||||
7: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"], time: 2 }
|
||||
]
|
||||
8: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"], time: 3 }
|
||||
]
|
||||
9: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"], time: 3 }
|
||||
]
|
||||
10: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"], time: 3 }
|
||||
]
|
||||
100: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"], time: 3 }
|
||||
]
|
||||
1000: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file"], time: 3 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
45
crates/gitbutler-watcher/tests/fixtures/emit_events_in_chronological_order.hjson
vendored
Normal file
45
crates/gitbutler-watcher/tests/fixtures/emit_events_in_chronological_order.hjson
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
state: {
|
||||
timeout: 5
|
||||
}
|
||||
events: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file-a"], time: 1 }
|
||||
{ kind: "modify-data-content", paths: ["/watch/file-b"], time: 3 }
|
||||
{ kind: "modify-data-content", paths: ["/watch/file-c"], time: 4 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["/watch/file-b"], time: 7 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["/watch/file-c"], time: 8 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["/watch/file-a"], time: 9 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file-a: {
|
||||
events: [
|
||||
{ kind: "modify-data-content", paths: ["*"], time: 1 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["*"], time: 9 }
|
||||
]
|
||||
}
|
||||
/watch/file-b: {
|
||||
events: [
|
||||
{ kind: "modify-data-content", paths: ["*"], time: 3 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["*"], time: 7 }
|
||||
]
|
||||
}
|
||||
/watch/file-c: {
|
||||
events: [
|
||||
{ kind: "modify-data-content", paths: ["*"], time: 4 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["*"], time: 8 }
|
||||
]
|
||||
}
|
||||
}
|
||||
events: {
|
||||
long: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/file-a"], time: 1 }
|
||||
{ kind: "modify-data-content", paths: ["/watch/file-b"], time: 3 }
|
||||
{ kind: "modify-data-content", paths: ["/watch/file-c"], time: 4 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["/watch/file-b"], time: 7 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["/watch/file-c"], time: 8 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["/watch/file-a"], time: 9 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
36
crates/gitbutler-watcher/tests/fixtures/emit_events_with_a_prepended_rename_event.hjson
vendored
Normal file
36
crates/gitbutler-watcher/tests/fixtures/emit_events_with_a_prepended_rename_event.hjson
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
state: {
|
||||
timeout: 5
|
||||
}
|
||||
events: [
|
||||
{ kind: "modify-data-content", paths: ["/watch/source"], time: 1 }
|
||||
{ kind: "modify-data-content", paths: ["/watch/source"], time: 4 }
|
||||
{ kind: "rename-from", paths: ["/watch/source"], tracker: 1, time: 7 }
|
||||
{ kind: "rename-to", paths: ["/watch/target"], tracker: 1, time: 8 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["/watch/target"], time: 9 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/target: {
|
||||
events: [
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"], tracker: 1, time: 7 }
|
||||
{ kind: "modify-data-content", paths: ["*"], time: 1 }
|
||||
{ kind: "modify-data-content", paths: ["*"], time: 4 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["*"], time: 9 }
|
||||
]
|
||||
}
|
||||
}
|
||||
events: {
|
||||
11: []
|
||||
12: [
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"], tracker: 1, time: 7 }
|
||||
{ kind: "modify-data-content", paths: ["/watch/target"], time: 4 }
|
||||
]
|
||||
14: [
|
||||
{ kind: "rename-both", paths: ["/watch/source", "/watch/target"], tracker: 1, time: 7 }
|
||||
{ kind: "modify-data-content", paths: ["/watch/target"], time: 4 }
|
||||
{ kind: "modify-metadata-write-time", paths: ["/watch/target"], time: 9 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
30
crates/gitbutler-watcher/tests/fixtures/emit_modify_event_after_close_event.hjson
vendored
Normal file
30
crates/gitbutler-watcher/tests/fixtures/emit_modify_event_after_close_event.hjson
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
state: {}
|
||||
events: [
|
||||
{ kind: "modify-data-any", paths: ["/watch/file"], time: 1 }
|
||||
{ kind: "access-close-write", paths: ["/watch/file"], time: 2 }
|
||||
{ kind: "modify-data-any", paths: ["/watch/file"], time: 3 }
|
||||
{ kind: "access-close-write", paths: ["/watch/file"], time: 4 }
|
||||
{ kind: "modify-data-any", paths: ["/watch/file"], time: 5 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "modify-data-any", paths: ["*"], time: 1 }
|
||||
{ kind: "access-close-write", paths: ["*"], time: 2 }
|
||||
{ kind: "modify-data-any", paths: ["*"], time: 3 }
|
||||
{ kind: "access-close-write", paths: ["*"], time: 4 }
|
||||
{ kind: "modify-data-any", paths: ["*"], time: 5 }
|
||||
]
|
||||
}
|
||||
}
|
||||
events: {
|
||||
short: []
|
||||
long: [
|
||||
{ kind: "access-close-write", paths: ["/watch/file"], time: 4 }
|
||||
{ kind: "modify-data-any", paths: ["/watch/file"], time: 5 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
56
crates/gitbutler-watcher/tests/fixtures/emit_needs_rescan_event.hjson
vendored
Normal file
56
crates/gitbutler-watcher/tests/fixtures/emit_needs_rescan_event.hjson
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
state: {
|
||||
queues: {
|
||||
/watch/file-a: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"], time: 1 }
|
||||
]
|
||||
}
|
||||
/watch/file-b: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"], time: 2 }
|
||||
]
|
||||
}
|
||||
}
|
||||
cache: {
|
||||
/watch/file-a: 1
|
||||
/watch/file-b: 2
|
||||
}
|
||||
file_system: {
|
||||
/watch/file-a: 1
|
||||
/watch/file-b: 2
|
||||
/watch/file-c: 3
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "other", flags: ["rescan"], time: 3 }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file-a: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"], time: 1 }
|
||||
]
|
||||
}
|
||||
/watch/file-b: {
|
||||
events: [
|
||||
{ kind: "create-any", paths: ["*"], time: 2 }
|
||||
]
|
||||
}
|
||||
}
|
||||
rescan_event: { kind: "other", flags: ["rescan"], time: 3 }
|
||||
cache: {
|
||||
/watch/file-a: 1
|
||||
/watch/file-b: 2
|
||||
/watch/file-c: 3
|
||||
}
|
||||
events: {
|
||||
short: []
|
||||
long: [
|
||||
{ kind: "create-any", paths: ["/watch/file-a"], time: 1 }
|
||||
{ kind: "create-any", paths: ["/watch/file-b"], time: 2 }
|
||||
{ kind: "other", flags: ["rescan"], time: 3 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
22
crates/gitbutler-watcher/tests/fixtures/read_file_id_without_create_event.hjson
vendored
Normal file
22
crates/gitbutler-watcher/tests/fixtures/read_file_id_without_create_event.hjson
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
state: {
|
||||
file_system: {
|
||||
/watch/file: 1
|
||||
}
|
||||
}
|
||||
events: [
|
||||
{ kind: "modify-data-any", paths: ["/watch/file"] }
|
||||
]
|
||||
expected: {
|
||||
queues: {
|
||||
/watch/file: {
|
||||
events: [
|
||||
{ kind: "modify-data-any", paths: ["*"] }
|
||||
]
|
||||
}
|
||||
}
|
||||
cache: {
|
||||
/watch/file: 1
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user