Skip to content

Commit 89b0cf8

Browse files
authored
Merge pull request #630 from dfaust/remove-mock-instant
Remove mock instant
2 parents 7c26617 + 3bb640f commit 89b0cf8

File tree

11 files changed

+178
-65
lines changed

11 files changed

+178
-65
lines changed

CHANGELOG.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,20 @@ v4 commits split out to branch `v4_maintenance` starting with `4.0.16`
1414

1515
## notify 7.0.0 (unreleased)
1616

17-
- CHANGE: raise MSRV to 1.72 [#569] [#610]
17+
- CHANGE: raise MSRV to 1.72 [#569] [#610] **breaking**
1818
- CHANGE: move event type to notify-types crate [#559]
1919
- CHANGE: flatten serialization of events and use camelCase [#558]
20-
- CHANGE: remove internal use of crossbeam-channels [#569] [#610]
20+
- CHANGE: remove internal use of crossbeam channels [#569] [#610]
21+
- CHANGE: rename feature `crossbeam` to `crossbeam-channel` and disable it by default [#610] **breaking**
2122
- CHANGE: upgrade mio to 1.0 [#623]
23+
- CHANGE: add log statements [#499]
2224
- FIX: prevent UB with illegal instruction for the windows backend [#604] [#607]
2325
- FIX: on Linux report deleted directories correctly [#545]
2426
- FEATURE: enable kqueue on iOS [#533]
2527
- MISC: various minor doc updates and fixes [#535] [#536] [#543] [#565] [#592] [#595]
2628
- MISC: update inotify to 0.10 [#547]
2729

30+
[#499]: https://github.com/notify-rs/notify/pull/499
2831
[#533]: https://github.com/notify-rs/notify/pull/533
2932
[#535]: https://github.com/notify-rs/notify/pull/535
3033
[#536]: https://github.com/notify-rs/notify/pull/536
@@ -43,13 +46,16 @@ v4 commits split out to branch `v4_maintenance` starting with `4.0.16`
4346

4447
## notify-types 1.0.0 (unreleased)
4548

46-
New crate containing public type definitions for the notify and debouncer crates.
49+
New crate containing public type definitions for the notify and debouncer crates. [#559]
4750

4851
- CHANGE: the serialization format for events has been changed to be easier to use in environments like JavaScript;
49-
the old behavior can be restored using the new feature flag `serialization-compat-6` [#558] [#568]
52+
the old behavior can be restored using the new feature flag **breaking**`serialization-compat-6` [#558] [#568]
53+
- CHANGE: use instant crate (which provides an `Instant` type that works in Wasm environments) [#570]
5054

5155
[#558]: https://github.com/notify-rs/notify/pull/558
56+
[#559]: https://github.com/notify-rs/notify/pull/559
5257
[#568]: https://github.com/notify-rs/notify/pull/568
58+
[#570]: https://github.com/notify-rs/notify/pull/570
5359

5460
## debouncer-full 0.4.0 (unreleased)
5561

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ kqueue = "1.0.8"
3333
libc = "0.2.4"
3434
log = "0.4.17"
3535
mio = { version = "1.0", features = ["os-ext"] }
36-
mock_instant = "0.3.0"
3736
instant = "0.1.12"
3837
nix = "0.27.0"
3938
notify = { path = "notify" }

notify-debouncer-full/Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ repository.workspace = true
1515
[features]
1616
default = ["macos_fsevent"]
1717
serde = ["notify-types/serde"]
18-
mock_instant = ["dep:mock_instant", "notify-types/mock_instant"]
1918
crossbeam-channel = ["dep:crossbeam-channel", "notify/crossbeam-channel"]
2019
macos_fsevent = ["notify/macos_fsevent"]
2120
macos_kqueue = ["notify/macos_kqueue"]
@@ -28,12 +27,11 @@ crossbeam-channel = { workspace = true, optional = true }
2827
file-id.workspace = true
2928
walkdir.workspace = true
3029
log.workspace = true
31-
mock_instant = { workspace = true, optional = true }
3230

3331
[dev-dependencies]
34-
notify-debouncer-full = { workspace = true, features = ["mock_instant"] }
3532
pretty_assertions.workspace = true
3633
rstest.workspace = true
3734
serde.workspace = true
3835
deser-hjson.workspace = true
3936
rand.workspace = true
37+
tempfile.workspace = true

notify-debouncer-full/src/lib.rs

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,15 @@
4949
//!
5050
//! The following crate features can be turned on or off in your cargo dependency config:
5151
//!
52-
//! - `crossbeam` passed down to notify, off by default
52+
//! - `crossbeam-channel` passed down to notify, off by default
5353
//! - `serialization-compat-6` passed down to notify, off by default
5454
//!
5555
//! # Caveats
5656
//!
5757
//! As all file events are sourced from notify, the [known problems](https://docs.rs/notify/latest/notify/#known-problems) section applies here too.
5858
5959
mod cache;
60+
mod time;
6061

6162
#[cfg(test)]
6263
mod testing;
@@ -69,9 +70,11 @@ use std::{
6970
atomic::{AtomicBool, Ordering},
7071
Arc, Mutex,
7172
},
72-
time::Duration,
73+
time::{Duration, Instant},
7374
};
7475

76+
use time::now;
77+
7578
pub use cache::{FileIdCache, FileIdMap, NoCache, RecommendedCache};
7679

7780
pub use file_id;
@@ -84,12 +87,6 @@ use notify::{
8487
Error, ErrorKind, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher, WatcherKind,
8588
};
8689

87-
#[cfg(feature = "mock_instant")]
88-
use mock_instant::Instant;
89-
90-
#[cfg(not(feature = "mock_instant"))]
91-
use std::time::Instant;
92-
9390
/// The set of requirements for watcher debounce event handling functions.
9491
///
9592
/// # Example implementation
@@ -124,7 +121,7 @@ where
124121
}
125122
}
126123

127-
#[cfg(feature = "crossbeam")]
124+
#[cfg(feature = "crossbeam-channel")]
128125
impl DebounceEventHandler for crossbeam_channel::Sender<DebounceEventResult> {
129126
fn handle_event(&mut self, event: DebounceEventResult) {
130127
let _ = self.send(event);
@@ -198,7 +195,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
198195

199196
/// Retrieve a vec of debounced events, removing them if not continuous
200197
pub fn debounced_events(&mut self) -> Vec<DebouncedEvent> {
201-
let now = Instant::now();
198+
let now = now();
202199
let mut events_expired = Vec::with_capacity(self.queues.len());
203200
let mut queues_remaining = HashMap::with_capacity(self.queues.len());
204201

@@ -211,6 +208,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
211208
}
212209
}
213210

211+
// drain the entire queue, then process the expired events and re-add the rest
214212
// TODO: perfect fit for drain_filter https://github.com/rust-lang/rust/issues/59618
215213
for (path, mut queue) in self.queues.drain() {
216214
let mut kind_index = HashMap::new();
@@ -249,13 +247,13 @@ impl<T: FileIdCache> DebounceDataInner<T> {
249247

250248
/// Returns all currently stored errors
251249
pub fn errors(&mut self) -> Vec<Error> {
252-
let mut v = Vec::new();
253-
std::mem::swap(&mut v, &mut self.errors);
254-
v
250+
std::mem::take(&mut self.errors)
255251
}
256252

257253
/// Add an error entry to re-send later on
258254
pub fn add_error(&mut self, error: Error) {
255+
log::trace!("raw error: {error:?}");
256+
259257
self.errors.push(error);
260258
}
261259

@@ -265,7 +263,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
265263

266264
if event.need_rescan() {
267265
self.cache.rescan(&self.roots);
268-
self.rescan_event = Some(event.into());
266+
self.rescan_event = Some(DebouncedEvent { event, time: now() });
269267
return;
270268
}
271269

@@ -277,7 +275,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
277275

278276
self.cache.add_path(path, recursive_mode);
279277

280-
self.push_event(event, Instant::now());
278+
self.push_event(event, now());
281279
}
282280
EventKind::Modify(ModifyKind::Name(rename_mode)) => {
283281
match rename_mode {
@@ -303,7 +301,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
303301
}
304302
}
305303
EventKind::Remove(_) => {
306-
self.push_remove_event(event, Instant::now());
304+
self.push_remove_event(event, now());
307305
}
308306
EventKind::Other => {
309307
// ignore meta events
@@ -315,7 +313,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
315313
self.cache.add_path(path, recursive_mode);
316314
}
317315

318-
self.push_event(event, Instant::now());
316+
self.push_event(event, now());
319317
}
320318
}
321319
}
@@ -334,7 +332,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
334332
}
335333

336334
fn handle_rename_from(&mut self, event: Event) {
337-
let time = Instant::now();
335+
let time = now();
338336
let path = &event.paths[0];
339337

340338
// store event
@@ -382,7 +380,7 @@ impl<T: FileIdCache> DebounceDataInner<T> {
382380
self.push_rename_event(path, event, time);
383381
} else {
384382
// move in
385-
self.push_event(event, Instant::now());
383+
self.push_event(event, now());
386384
}
387385

388386
self.rename_event = None;
@@ -753,10 +751,11 @@ mod tests {
753751

754752
use super::*;
755753

756-
use mock_instant::MockClock;
757754
use pretty_assertions::assert_eq;
758755
use rstest::rstest;
756+
use tempfile::tempdir;
759757
use testing::TestCase;
758+
use time::MockTime;
760759

761760
#[rstest]
762761
fn state(
@@ -805,16 +804,19 @@ mod tests {
805804
fs::read_to_string(Path::new(&format!("./test_cases/{file_name}.hjson"))).unwrap();
806805
let mut test_case = deser_hjson::from_str::<TestCase>(&file_content).unwrap();
807806

808-
MockClock::set_time(Duration::default());
809-
810-
let time = Instant::now();
807+
let time = now();
808+
MockTime::set_time(time);
811809

812810
let mut state = test_case.state.into_debounce_data_inner(time);
813811
state.roots = vec![(PathBuf::from("/"), RecursiveMode::Recursive)];
814812

813+
let mut prev_event_time = Duration::default();
814+
815815
for event in test_case.events {
816+
let event_time = Duration::from_millis(event.time);
816817
let event = event.into_debounced_event(time, None);
817-
MockClock::set_time(event.time - time);
818+
MockTime::advance(event_time - prev_event_time);
819+
prev_event_time = event_time;
818820
state.add_event(event.event);
819821
}
820822

@@ -856,21 +858,20 @@ mod tests {
856858
"errors not as expected"
857859
);
858860

859-
let backup_time = Instant::now().duration_since(time);
861+
let backup_time = now();
860862
let backup_queues = state.queues.clone();
861863

862864
for (delay, events) in expected_events {
863-
MockClock::set_time(backup_time);
865+
MockTime::set_time(backup_time);
864866
state.queues = backup_queues.clone();
865867

866868
match delay.as_str() {
867869
"none" => {}
868-
"short" => MockClock::advance(Duration::from_millis(10)),
869-
"long" => MockClock::advance(Duration::from_millis(100)),
870+
"short" => MockTime::advance(Duration::from_millis(10)),
871+
"long" => MockTime::advance(Duration::from_millis(100)),
870872
_ => {
871873
if let Ok(ts) = delay.parse::<u64>() {
872-
let ts = time + Duration::from_millis(ts);
873-
MockClock::set_time(ts - time);
874+
MockTime::set_time(time + Duration::from_millis(ts));
874875
}
875876
}
876877
}
@@ -887,4 +888,26 @@ mod tests {
887888
);
888889
}
889890
}
891+
892+
#[test]
893+
fn integration() -> Result<(), Box<dyn std::error::Error>> {
894+
let dir = tempdir()?;
895+
896+
let (tx, rx) = std::sync::mpsc::channel();
897+
898+
let mut debouncer = new_debouncer(Duration::from_millis(10), None, tx)?;
899+
900+
debouncer.watch(dir.path(), RecursiveMode::Recursive)?;
901+
902+
fs::write(dir.path().join("file.txt"), b"Lorem ipsum")?;
903+
904+
let events = rx
905+
.recv_timeout(Duration::from_secs(10))
906+
.expect("no events received")
907+
.expect("received an error");
908+
909+
assert!(!events.is_empty(), "received empty event list");
910+
911+
Ok(())
912+
}
890913
}

notify-debouncer-full/src/testing.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use std::{
22
collections::{HashMap, VecDeque},
33
path::{Path, PathBuf},
4-
time::Duration,
4+
time::{Duration, Instant},
55
};
66

77
use file_id::FileId;
8-
use mock_instant::Instant;
98
use notify::{
109
event::{
1110
AccessKind, AccessMode, CreateKind, DataChange, Flag, MetadataKind, ModifyKind, RemoveKind,

notify-debouncer-full/src/time.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#[cfg(not(test))]
2+
mod build {
3+
use std::time::Instant;
4+
5+
pub fn now() -> Instant {
6+
Instant::now()
7+
}
8+
}
9+
10+
#[cfg(not(test))]
11+
pub use build::*;
12+
13+
#[cfg(test)]
14+
mod test {
15+
use std::{
16+
sync::Mutex,
17+
time::{Duration, Instant},
18+
};
19+
20+
thread_local! {
21+
static NOW: Mutex<Option<Instant>> = Mutex::new(None);
22+
}
23+
24+
pub fn now() -> Instant {
25+
let time = NOW.with(|now| *now.lock().unwrap());
26+
time.unwrap_or_else(|| Instant::now())
27+
}
28+
29+
pub struct MockTime;
30+
31+
impl MockTime {
32+
pub fn set_time(time: Instant) {
33+
NOW.with(|now| *now.lock().unwrap() = Some(time));
34+
}
35+
36+
pub fn advance(delta: Duration) {
37+
NOW.with(|now| {
38+
if let Some(n) = &mut *now.lock().unwrap() {
39+
*n += delta;
40+
}
41+
});
42+
}
43+
}
44+
}
45+
46+
#[cfg(test)]
47+
pub use test::*;

notify-debouncer-mini/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ notify.workspace = true
2525
notify-types.workspace = true
2626
crossbeam-channel = { workspace = true, optional = true }
2727
log.workspace = true
28+
tempfile.workspace = true

0 commit comments

Comments
 (0)