History

parametrize on recursiveness

Cole Miller committed 5 months ago

Commit 2df437e1 parent 374f17b1 file · +302 −52Browse files

Unified · Split

crates/fs/src/fs_watcher.rs+302−52
@@ -76,7 +76,7 @@ impl Watcher for FsWatcher { · 75 unmodified lines
7676 paths.get(ancestor).is_some_and(|entry| match entry {
7777 FsWatcherRegistration::Pending { .. } => false,
7878 FsWatcherRegistration::Registered { mode, .. } => {
79− recursive_mode(*mode) == notify::RecursiveMode::Recursive
79+ platform_recursive_mode(*mode) == notify::RecursiveMode::Recursive
8080 }
8181 })
8282 });
@@ -551,7 +551,7 @@ impl<T: notify::Watcher + Send> WatchBackend for T { · 468 unmodified lines
551551 }
552552 }
553553
554−/// `GlobalWatcher` is the low-level manager of filesystem watching. There is one per `RealFs`.
554+/// `GlobalWatcher` is the low-level platform-agnostic manager of filesystem watching. There is one per `RealFs`.
555555 ///
556556 /// `GlobalWatcher` talks to a `WatchBackend`, which handles the OS-specific details of filesystem watching.
557557 /// In production, we use the `notify` crate for this. The job of `GlobalWatcher` is to convey watch and unwatch
@@ -648,13 +648,17 @@ impl GlobalWatcher { · 90 unmodified lines
648648 }
649649
650650 pub(crate) fn new(executor: &BackgroundExecutor) -> Self {
651− Self::with_backends(executor, None, None)
651+ Self::with_backends(
652+ executor,
653+ (None, platform_recursive_mode(WatcherMode::Native)),
654+ (None, platform_recursive_mode(WatcherMode::Poll)),
655+ )
652656 }
653657
654658 fn with_backends(
655659 executor: &BackgroundExecutor,
656− native_backend: Option<Box<dyn WatchBackend>>,
657− poll_backend: Option<Box<dyn WatchBackend>>,
660+ native: (Option<Box<dyn WatchBackend>>, notify::RecursiveMode),
661+ poll: (Option<Box<dyn WatchBackend>>, notify::RecursiveMode),
658662 ) -> Self {
659663 let state = Arc::new(Mutex::new(WatcherState {
660664 watchers: HashMap::new(),
@@ -672,10 +676,11 @@ impl GlobalWatcher { · 11 unmodified lines
672676 executor: executor.clone(),
673677 native: BackendState::new(
674678 WatcherMode::Native,
679+ native.1,
675680 executor.clone(),
676− native_backend,
681+ native.0,
677682 ),
678− poll: BackendState::new(WatcherMode::Poll, executor.clone(), poll_backend),
683+ poll: BackendState::new(WatcherMode::Poll, poll.1, executor.clone(), poll.0),
679684 }
680685 .run(wake_rx),
681686 )
@@ -690,6 +695,10 @@ impl GlobalWatcher { · 8 unmodified lines
690695
691696 struct BackendState {
692697 mode: WatcherMode,
698+ // Whether this backend watches recursively. Determined once at construction
699+ // (from the platform for the real backend, or chosen explicitly in tests) so
700+ // that coverage reasoning works even before the backend has been created.
701+ recursive_mode: notify::RecursiveMode,
693702 executor: BackgroundExecutor,
694703 backend: Option<Box<dyn WatchBackend>>,
695704 /// Paths that we have successfully asked the backend to watch.
@@ -753,11 +762,11 @@ impl Reconciler { · 57 unmodified lines
753762 health_checks = std::mem::take(&mut state.pending_health_checks);
754763 let dirty = std::mem::take(&mut state.dirty_paths);
755764 for (mode, path) in dirty {
756− let affected_paths = match mode {
757− WatcherMode::Native => &mut native_affected_paths,
758− WatcherMode::Poll => &mut poll_affected_paths,
765+ let (affected_paths, is_recursive) = match mode {
766+ WatcherMode::Native => (&mut native_affected_paths, self.native.is_recursive()),
767+ WatcherMode::Poll => (&mut poll_affected_paths, self.poll.is_recursive()),
759768 };
760− if recursive_mode(mode) == notify::RecursiveMode::Recursive {
769+ if is_recursive {
761770 for (descendant, _) in state.path_registrations(mode).range::<Path, _>((
762771 std::ops::Bound::Excluded(path.as_ref()),
763772 std::ops::Bound::Unbounded,
@@ -797,11 +806,13 @@ impl Reconciler { · 33 unmodified lines
797806 impl BackendState {
798807 fn new(
799808 mode: WatcherMode,
809+ recursive_mode: notify::RecursiveMode,
800810 executor: BackgroundExecutor,
801811 backend: Option<Box<dyn WatchBackend>>,
802812 ) -> Self {
803813 Self {
804814 mode,
815+ recursive_mode,
805816 executor,
806817 backend,
807818 applied_paths: HashSet::new(),
@@ -811,6 +822,10 @@ impl BackendState { · 3 unmodified lines
811822 }
812823 }
813824
825+ fn is_recursive(&self) -> bool {
826+ self.recursive_mode == notify::RecursiveMode::Recursive
827+ }
828+
814829 fn reconcile(
815830 &mut self,
816831 affected_paths: HashSet<Arc<Path>>,
@@ -829,7 +844,7 @@ impl BackendState { · 12 unmodified lines
829844 let registrations = watcher_state.path_registrations(self.mode);
830845 for path in affected_paths {
831846 let is_desired = registrations.contains_key(&path);
832− let is_covered = recursive_mode(self.mode) == notify::RecursiveMode::Recursive
847+ let is_covered = self.is_recursive()
833848 && path
834849 .ancestors()
835850 .skip(1)
@@ -938,7 +953,7 @@ impl BackendState { · 102 unmodified lines
938953
939954 // Check that no path in the applied set is a strict child of another,
940955 // if the backend is recursive, since this would be redundant.
941− if recursive_mode(self.mode) == notify::RecursiveMode::Recursive {
956+ if self.is_recursive() {
942957 let mut applied_paths = self
943958 .applied_paths
944959 .iter()
@@ -1017,7 +1032,7 @@ impl BackendState { · 72 unmodified lines
10171032 paths: Vec<Arc<Path>>,
10181033 watcher_state: &Arc<Mutex<WatcherState>>,
10191034 ) -> Option<Instant> {
1020− let recursive_mode = recursive_mode(self.mode);
1035+ let recursive_mode = self.recursive_mode;
10211036 let mut queue = paths;
10221037 let mut wake_at = None;
10231038 while !queue.is_empty() {
@@ -1195,7 +1210,7 @@ fn create_backend( · 171 unmodified lines
11951210 }
11961211 }
11971212
1198−fn recursive_mode(mode: WatcherMode) -> notify::RecursiveMode {
1213+fn platform_recursive_mode(mode: WatcherMode) -> notify::RecursiveMode {
11991214 match mode {
12001215 WatcherMode::Native => {
12011216 if cfg!(any(target_os = "windows", target_os = "macos")) {
@@ -1274,6 +1289,7 @@ fn handle_event(mode: WatcherMode, state: &Mutex<WatcherState>, event: notify::R · 72 unmodified lines
12741289 #[cfg(test)]
12751290 mod tests {
12761291 use super::*;
1292+ use notify::RecursiveMode;
12771293 use std::{collections::HashSet, path::PathBuf};
12781294
12791295 fn rescan(path: &str) -> PathEvent {
@@ -1461,17 +1477,30 @@ mod tests { · 181 unmodified lines
14611477 }
14621478 }
14631479
1480+ type FakeBackend = Option<(Arc<Mutex<FakeWatchBackend>>, notify::RecursiveMode)>;
1481+
1482+ fn fake_backend(
1483+ backend: FakeBackend,
1484+ mode: WatcherMode,
1485+ ) -> (Option<Box<dyn WatchBackend>>, notify::RecursiveMode) {
1486+ match backend {
1487+ Some((backend, recursive_mode)) => (
1488+ Some(Box::new(SharedFakeWatchBackend(backend)) as Box<dyn WatchBackend>),
1489+ recursive_mode,
1490+ ),
1491+ None => (None, platform_recursive_mode(mode)),
1492+ }
1493+ }
1494+
14641495 fn test_global(
14651496 executor: &BackgroundExecutor,
1466− native_backend: Option<Arc<Mutex<FakeWatchBackend>>>,
1467− poll_backend: Option<Arc<Mutex<FakeWatchBackend>>>,
1497+ native_backend: FakeBackend,
1498+ poll_backend: FakeBackend,
14681499 ) -> GlobalWatcher {
14691500 GlobalWatcher::with_backends(
14701501 executor,
1471− native_backend
1472− .map(|backend| Box::new(SharedFakeWatchBackend(backend)) as Box<dyn WatchBackend>),
1473− poll_backend
1474− .map(|backend| Box::new(SharedFakeWatchBackend(backend)) as Box<dyn WatchBackend>),
1502+ fake_backend(native_backend, WatcherMode::Native),
1503+ fake_backend(poll_backend, WatcherMode::Poll),
14751504 )
14761505 }
14771506
@@ -1495,7 +1524,14 @@ mod tests { · 17 unmodified lines
14951524 #[gpui::test]
14961525 async fn watch_and_unwatch_call_the_backend_once_per_path(executor: BackgroundExecutor) {
14971526 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1498− let global = test_global(&executor, Some(backend.clone()), None);
1527+ let global = test_global(
1528+ &executor,
1529+ Some((
1530+ backend.clone(),
1531+ platform_recursive_mode(WatcherMode::Native),
1532+ )),
1533+ None,
1534+ );
14991535 let path = Arc::<Path>::from(Path::new("/repo"));
15001536
15011537 let first = global.add(path.clone(), WatcherMode::Native, noop_callback());
@@ -1516,7 +1552,11 @@ mod tests { · 14 unmodified lines
15161552 #[gpui::test]
15171553 async fn covered_child_is_promoted_when_parent_is_unregistered(executor: BackgroundExecutor) {
15181554 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1519− let global = test_global(&executor, None, Some(backend.clone()));
1555+ let global = test_global(
1556+ &executor,
1557+ None,
1558+ Some((backend.clone(), RecursiveMode::Recursive)),
1559+ );
15201560 let parent = Arc::<Path>::from(Path::new("/repo"));
15211561 let child = Arc::<Path>::from(Path::new("/repo/foo.csproj"));
15221562
@@ -1543,7 +1583,11 @@ mod tests { · 20 unmodified lines
15431583 #[gpui::test]
15441584 async fn child_is_demoted_when_covering_parent_is_registered(executor: BackgroundExecutor) {
15451585 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1546− let global = test_global(&executor, None, Some(backend.clone()));
1586+ let global = test_global(
1587+ &executor,
1588+ None,
1589+ Some((backend.clone(), RecursiveMode::Recursive)),
1590+ );
15471591 let parent = Arc::<Path>::from(Path::new("/repo"));
15481592 let child = Arc::<Path>::from(Path::new("/repo/sub"));
15491593
@@ -1568,10 +1612,83 @@ mod tests { · 18 unmodified lines
15681612 assert!(!backend.watched_paths.contains(child.as_ref()));
15691613 }
15701614
1615+ #[gpui::test]
1616+ async fn non_recursive_backend_does_not_cover_children(executor: BackgroundExecutor) {
1617+ // A non-recursive backend (e.g. inotify) does not deliver events for a
1618+ // child via an ancestor's watch, so coverage must not kick in: every
1619+ // registered path keeps its own watch, nothing is demoted, and removing an
1620+ // ancestor leaves the descendant's watch untouched. This is the mirror
1621+ // image of `covered_child_is_promoted_when_parent_is_unregistered`, where
1622+ // a recursive ancestor covers the child and its removal promotes it. The
1623+ // polymorphic fake lets us pin this on any host, independent of what
1624+ // `platform_recursive_mode` would pick.
1625+ let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1626+ let global = test_global(
1627+ &executor,
1628+ Some((backend.clone(), RecursiveMode::NonRecursive)),
1629+ None,
1630+ );
1631+ let parent = Arc::<Path>::from(Path::new("/repo"));
1632+ let child = Arc::<Path>::from(Path::new("/repo/sub"));
1633+
1634+ let (child_events, child_callback) = collecting_callback();
1635+ global.add(child.clone(), WatcherMode::Native, child_callback);
1636+ executor.run_until_parked();
1637+ assert_eq!(backend.lock().watch_calls, &[child.to_path_buf()]);
1638+
1639+ let parent_id = global.add(parent.clone(), WatcherMode::Native, noop_callback());
1640+ executor.run_until_parked();
1641+ {
1642+ let backend = backend.lock();
1643+ assert_eq!(
1644+ backend.watch_calls,
1645+ &[child.to_path_buf(), parent.to_path_buf()],
1646+ "the parent gets its own watch without covering the child"
1647+ );
1648+ assert_eq!(
1649+ backend.unwatch_calls,
1650+ Vec::<PathBuf>::new(),
1651+ "no demotion happens without recursive coverage"
1652+ );
1653+ assert!(backend.watched_paths.contains(parent.as_ref()));
1654+ assert!(
1655+ backend.watched_paths.contains(child.as_ref()),
1656+ "the child keeps its own watch under a non-recursive backend"
1657+ );
1658+ }
1659+
1660+ // Removing the ancestor must not disturb the child: with no coverage there
1661+ // is nothing to promote, so the child is neither re-watched nor rescanned.
1662+ global.remove(parent_id);
1663+ executor.run_until_parked();
1664+ {
1665+ let backend = backend.lock();
1666+ assert_eq!(
1667+ backend.unwatch_calls,
1668+ &[parent.to_path_buf()],
1669+ "only the ancestor is unwatched"
1670+ );
1671+ assert_eq!(
1672+ backend.watch_calls,
1673+ &[child.to_path_buf(), parent.to_path_buf()],
1674+ "the child is not re-watched when the ancestor goes away"
1675+ );
1676+ assert!(backend.watched_paths.contains(child.as_ref()));
1677+ }
1678+ assert!(
1679+ child_events.lock().is_empty(),
1680+ "the child gets no rescan since its watch never lapsed"
1681+ );
1682+ }
1683+
15711684 #[gpui::test]
15721685 async fn removing_a_covered_child_issues_no_unwatch(executor: BackgroundExecutor) {
15731686 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1574− let global = test_global(&executor, None, Some(backend.clone()));
1687+ let global = test_global(
1688+ &executor,
1689+ None,
1690+ Some((backend.clone(), RecursiveMode::Recursive)),
1691+ );
15751692 let parent = Arc::<Path>::from(Path::new("/repo"));
15761693 let child = Arc::<Path>::from(Path::new("/repo/foo.csproj"));
15771694
@@ -1602,7 +1719,11 @@ mod tests { · 24 unmodified lines
16021719 #[gpui::test]
16031720 async fn nested_registrations_keep_applied_as_an_antichain(executor: BackgroundExecutor) {
16041721 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1605− let global = test_global(&executor, None, Some(backend.clone()));
1722+ let global = test_global(
1723+ &executor,
1724+ None,
1725+ Some((backend.clone(), RecursiveMode::Recursive)),
1726+ );
16061727 let parent = Arc::<Path>::from(Path::new("/repo"));
16071728 let child = Arc::<Path>::from(Path::new("/repo/a"));
16081729 let grandchild = Arc::<Path>::from(Path::new("/repo/a/b"));
@@ -1632,7 +1753,11 @@ mod tests { · 23 unmodified lines
16321753 #[gpui::test]
16331754 async fn deferred_child_is_not_retained_as_intent_once_covered(executor: BackgroundExecutor) {
16341755 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1635− let global = test_global(&executor, None, Some(backend.clone()));
1756+ let global = test_global(
1757+ &executor,
1758+ None,
1759+ Some((backend.clone(), RecursiveMode::Recursive)),
1760+ );
16361761 let parent = Arc::<Path>::from(Path::new("/repo"));
16371762 let child = Arc::<Path>::from(Path::new("/repo/sub"));
16381763
@@ -1673,7 +1798,14 @@ mod tests { · 34 unmodified lines
16731798 #[gpui::test]
16741799 async fn unregister_then_register_same_path_coalesces(executor: BackgroundExecutor) {
16751800 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1676− let global = test_global(&executor, Some(backend.clone()), None);
1801+ let global = test_global(
1802+ &executor,
1803+ Some((
1804+ backend.clone(),
1805+ platform_recursive_mode(WatcherMode::Native),
1806+ )),
1807+ None,
1808+ );
16771809 let path = Arc::<Path>::from(Path::new("/repo"));
16781810
16791811 let first = global.add(path.clone(), WatcherMode::Native, noop_callback());
@@ -1703,7 +1835,14 @@ mod tests { · 23 unmodified lines
17031835 #[gpui::test]
17041836 async fn failed_watch_is_not_retried_until_reregistered(executor: BackgroundExecutor) {
17051837 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1706− let global = test_global(&executor, Some(backend.clone()), None);
1838+ let global = test_global(
1839+ &executor,
1840+ Some((
1841+ backend.clone(),
1842+ platform_recursive_mode(WatcherMode::Native),
1843+ )),
1844+ None,
1845+ );
17071846 let path_a = Arc::<Path>::from(Path::new("/repo/a"));
17081847 let path_b = Arc::<Path>::from(Path::new("/repo/b"));
17091848 backend
@@ -1742,7 +1881,14 @@ mod tests { · 32 unmodified lines
17421881 #[gpui::test]
17431882 async fn batched_watch_skips_only_the_path_with_a_generic_error(executor: BackgroundExecutor) {
17441883 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1745− let global = test_global(&executor, Some(backend.clone()), None);
1884+ let global = test_global(
1885+ &executor,
1886+ Some((
1887+ backend.clone(),
1888+ platform_recursive_mode(WatcherMode::Native),
1889+ )),
1890+ None,
1891+ );
17461892 let path_a = Arc::<Path>::from(Path::new("/repo/a"));
17471893 let path_b = Arc::<Path>::from(Path::new("/repo/b"));
17481894 let path_c = Arc::<Path>::from(Path::new("/repo/c"));
@@ -1806,7 +1952,14 @@ mod tests { · 57 unmodified lines
18061952 executor: BackgroundExecutor,
18071953 ) {
18081954 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1809− let global = test_global(&executor, Some(backend.clone()), None);
1955+ let global = test_global(
1956+ &executor,
1957+ Some((
1958+ backend.clone(),
1959+ platform_recursive_mode(WatcherMode::Native),
1960+ )),
1961+ None,
1962+ );
18101963 let path_a = Arc::<Path>::from(Path::new("/repo/a"));
18111964 let path_b = Arc::<Path>::from(Path::new("/repo/b"));
18121965 let path_c = Arc::<Path>::from(Path::new("/repo/c"));
@@ -1866,7 +2019,11 @@ mod tests { · 53 unmodified lines
18662019 async fn events_are_dispatched_to_matching_mode_only(executor: BackgroundExecutor) {
18672020 let native_backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
18682021 let poll_backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1869− let global = test_global(&executor, Some(native_backend), Some(poll_backend));
2022+ let global = test_global(
2023+ &executor,
2024+ Some((native_backend, platform_recursive_mode(WatcherMode::Native))),
2025+ Some((poll_backend, platform_recursive_mode(WatcherMode::Poll))),
2026+ );
18702027
18712028 let (native_events, native_callback) = collecting_callback();
18722029 let (poll_events, poll_callback) = collecting_callback();
@@ -1906,7 +2063,14 @@ mod tests { · 33 unmodified lines
19062063 #[gpui::test]
19072064 async fn cooldown_defers_watches_without_further_syscalls(executor: BackgroundExecutor) {
19082065 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1909− let global = test_global(&executor, Some(backend.clone()), None);
2066+ let global = test_global(
2067+ &executor,
2068+ Some((
2069+ backend.clone(),
2070+ platform_recursive_mode(WatcherMode::Native),
2071+ )),
2072+ None,
2073+ );
19102074 let path_a = Arc::<Path>::from(Path::new("/repo/a"));
19112075 let path_b = Arc::<Path>::from(Path::new("/repo/b"));
19122076 backend
@@ -1928,7 +2092,14 @@ mod tests { · 15 unmodified lines
19282092 #[gpui::test]
19292093 async fn deferred_watches_recover_after_cooldown(executor: BackgroundExecutor) {
19302094 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1931− let global = test_global(&executor, Some(backend.clone()), None);
2095+ let global = test_global(
2096+ &executor,
2097+ Some((
2098+ backend.clone(),
2099+ platform_recursive_mode(WatcherMode::Native),
2100+ )),
2101+ None,
2102+ );
19322103 let path_a = Arc::<Path>::from(Path::new("/repo/a"));
19332104 let path_b = Arc::<Path>::from(Path::new("/repo/b"));
19342105 backend
@@ -1964,7 +2135,14 @@ mod tests { · 29 unmodified lines
19642135 #[gpui::test]
19652136 async fn unwatch_clears_the_cooldown(executor: BackgroundExecutor) {
19662137 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1967− let global = test_global(&executor, Some(backend.clone()), None);
2138+ let global = test_global(
2139+ &executor,
2140+ Some((
2141+ backend.clone(),
2142+ platform_recursive_mode(WatcherMode::Native),
2143+ )),
2144+ None,
2145+ );
19682146 let path_a = Arc::<Path>::from(Path::new("/repo/a"));
19692147 let path_b = Arc::<Path>::from(Path::new("/repo/b"));
19702148
@@ -1995,7 +2173,14 @@ mod tests { · 24 unmodified lines
19952173 #[gpui::test]
19962174 async fn unregistering_a_deferred_path_clears_it(executor: BackgroundExecutor) {
19972175 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
1998− let global = test_global(&executor, Some(backend.clone()), None);
2176+ let global = test_global(
2177+ &executor,
2178+ Some((
2179+ backend.clone(),
2180+ platform_recursive_mode(WatcherMode::Native),
2181+ )),
2182+ None,
2183+ );
19992184 let path = Arc::<Path>::from(Path::new("/repo/a"));
20002185 backend
20012186 .lock()
@@ -2030,7 +2215,14 @@ mod tests { · 28 unmodified lines
20302215 #[gpui::test]
20312216 async fn stream_restart_failure_reestablishes_all_watches(executor: BackgroundExecutor) {
20322217 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
2033− let global = test_global(&executor, Some(backend.clone()), None);
2218+ let global = test_global(
2219+ &executor,
2220+ Some((
2221+ backend.clone(),
2222+ platform_recursive_mode(WatcherMode::Native),
2223+ )),
2224+ None,
2225+ );
20342226 let path_a = Arc::<Path>::from(Path::new("/repo/a"));
20352227 let path_b = Arc::<Path>::from(Path::new("/repo/b"));
20362228
@@ -2068,7 +2260,14 @@ mod tests { · 31 unmodified lines
20682260 #[gpui::test]
20692261 async fn unwatch_skips_the_failed_path_and_unwatches_the_rest(executor: BackgroundExecutor) {
20702262 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
2071− let global = test_global(&executor, Some(backend.clone()), None);
2263+ let global = test_global(
2264+ &executor,
2265+ Some((
2266+ backend.clone(),
2267+ platform_recursive_mode(WatcherMode::Native),
2268+ )),
2269+ None,
2270+ );
20722271 let path_a = Arc::<Path>::from(Path::new("/repo/a"));
20732272 let path_b = Arc::<Path>::from(Path::new("/repo/b"));
20742273 let path_c = Arc::<Path>::from(Path::new("/repo/c"));
@@ -2111,7 +2310,14 @@ mod tests { · 36 unmodified lines
21112310 executor: BackgroundExecutor,
21122311 ) {
21132312 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
2114− let global = test_global(&executor, Some(backend.clone()), None);
2313+ let global = test_global(
2314+ &executor,
2315+ Some((
2316+ backend.clone(),
2317+ platform_recursive_mode(WatcherMode::Native),
2318+ )),
2319+ None,
2320+ );
21152321 let path_a = Arc::<Path>::from(Path::new("/repo/a"));
21162322 let path_b = Arc::<Path>::from(Path::new("/repo/b"));
21172323
@@ -2147,7 +2353,14 @@ mod tests { · 29 unmodified lines
21472353 executor: BackgroundExecutor,
21482354 ) {
21492355 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
2150− let global = test_global(&executor, Some(backend.clone()), None);
2356+ let global = test_global(
2357+ &executor,
2358+ Some((
2359+ backend.clone(),
2360+ platform_recursive_mode(WatcherMode::Native),
2361+ )),
2362+ None,
2363+ );
21512364 let path = Arc::<Path>::from(Path::new("/repo"));
21522365
21532366 // The initial watch applies in the backend, but the stream fails to
@@ -2175,7 +2388,14 @@ mod tests { · 21 unmodified lines
21752388 #[gpui::test]
21762389 async fn flush_resolves_after_reconcile(executor: BackgroundExecutor) {
21772390 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
2178− let global = test_global(&executor, Some(backend.clone()), None);
2391+ let global = test_global(
2392+ &executor,
2393+ Some((
2394+ backend.clone(),
2395+ platform_recursive_mode(WatcherMode::Native),
2396+ )),
2397+ None,
2398+ );
21792399 let path = Arc::<Path>::from(Path::new("/repo"));
21802400
21812401 global.add(path.clone(), WatcherMode::Native, noop_callback());
@@ -2191,7 +2411,11 @@ mod tests { · 9 unmodified lines
21912411 executor: BackgroundExecutor,
21922412 ) {
21932413 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
2194− let global = test_global(&executor, Some(backend), None);
2414+ let global = test_global(
2415+ &executor,
2416+ Some((backend, platform_recursive_mode(WatcherMode::Native))),
2417+ None,
2418+ );
21952419
21962420 let mut health = global.check_health();
21972421 executor.run_until_parked();
@@ -2201,8 +2425,8 @@ mod tests { · 3 unmodified lines
22012425
22022426 fn test_sink(
22032427 executor: &BackgroundExecutor,
2204− native_backend: Option<Arc<Mutex<FakeWatchBackend>>>,
2205− poll_backend: Option<Arc<Mutex<FakeWatchBackend>>>,
2428+ native_backend: FakeBackend,
2429+ poll_backend: FakeBackend,
22062430 ) -> (
22072431 FsWatcher,
22082432 async_channel::Receiver<()>,
@@ -2210,10 +2434,8 @@ mod tests { · 1 unmodified line
22102434 ) {
22112435 let global = GlobalWatcher::with_backends(
22122436 executor,
2213− native_backend
2214− .map(|backend| Box::new(SharedFakeWatchBackend(backend)) as Box<dyn WatchBackend>),
2215− poll_backend
2216− .map(|backend| Box::new(SharedFakeWatchBackend(backend)) as Box<dyn WatchBackend>),
2437+ fake_backend(native_backend, WatcherMode::Native),
2438+ fake_backend(poll_backend, WatcherMode::Poll),
22172439 );
22182440 let (tx, rx) = async_channel::unbounded();
22192441 let pending_path_events: Arc<Mutex<Vec<PathEvent>>> = Default::default();
@@ -2224,7 +2446,14 @@ mod tests { · 4 unmodified lines
22242446 #[gpui::test]
22252447 async fn sink_establishes_watch_for_existing_path(executor: BackgroundExecutor) {
22262448 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
2227− let (sink, _rx, _pending) = test_sink(&executor, Some(backend.clone()), None);
2449+ let (sink, _rx, _pending) = test_sink(
2450+ &executor,
2451+ Some((
2452+ backend.clone(),
2453+ platform_recursive_mode(WatcherMode::Native),
2454+ )),
2455+ None,
2456+ );
22282457 let dir = tempfile::TempDir::new().expect("create temp dir");
22292458
22302459 sink.add(dir.path()).expect("add succeeds");
@@ -2242,7 +2471,14 @@ mod tests { · 11 unmodified lines
22422471 #[gpui::test]
22432472 async fn sink_waits_for_path_creation(executor: BackgroundExecutor) {
22442473 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
2245− let (sink, rx, pending_path_events) = test_sink(&executor, Some(backend.clone()), None);
2474+ let (sink, rx, pending_path_events) = test_sink(
2475+ &executor,
2476+ Some((
2477+ backend.clone(),
2478+ platform_recursive_mode(WatcherMode::Native),
2479+ )),
2480+ None,
2481+ );
22462482 let dir = tempfile::TempDir::new().expect("create temp dir");
22472483 let file = dir.path().join("missing.txt");
22482484
@@ -2271,7 +2507,14 @@ mod tests { · 22 unmodified lines
22712507 #[gpui::test]
22722508 async fn sink_remove_cancels_pending_watch(executor: BackgroundExecutor) {
22732509 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
2274− let (sink, _rx, _pending) = test_sink(&executor, Some(backend.clone()), None);
2510+ let (sink, _rx, _pending) = test_sink(
2511+ &executor,
2512+ Some((
2513+ backend.clone(),
2514+ platform_recursive_mode(WatcherMode::Native),
2515+ )),
2516+ None,
2517+ );
22752518 let dir = tempfile::TempDir::new().expect("create temp dir");
22762519 let file = dir.path().join("missing.txt");
22772520
@@ -2291,7 +2534,14 @@ mod tests { · 13 unmodified lines
22912534 #[gpui::test]
22922535 async fn sink_recovers_deferred_watch_and_emits_rescan(executor: BackgroundExecutor) {
22932536 let backend = Arc::new(Mutex::new(FakeWatchBackend::default()));
2294− let (sink, _rx, pending_path_events) = test_sink(&executor, Some(backend.clone()), None);
2537+ let (sink, _rx, pending_path_events) = test_sink(
2538+ &executor,
2539+ Some((
2540+ backend.clone(),
2541+ platform_recursive_mode(WatcherMode::Native),
2542+ )),
2543+ None,
2544+ );
22952545 let dir = tempfile::TempDir::new().expect("create temp dir");
22962546 backend
22972547 .lock()