diff --git a/crates/memofs/src/lib.rs b/crates/memofs/src/lib.rs index 62c72557..c99acb1a 100644 --- a/crates/memofs/src/lib.rs +++ b/crates/memofs/src/lib.rs @@ -419,6 +419,11 @@ impl Vfs { } } + /// Returns whether automatic file watching is currently enabled. + pub fn is_watch_enabled(&self) -> bool { + self.inner.lock().unwrap().watch_enabled + } + /// Turns automatic file watching on or off. Enabled by default. /// /// Turning off file watching may be useful for single-use cases, especially @@ -1008,6 +1013,63 @@ mod test { } } + #[test] + fn prefetch_cache_walked_roots_fast_reject() { + let mut imfs = InMemoryFs::new(); + imfs.load_snapshot( + "/root", + VfsSnapshot::dir(HashMap::from([ + ("known.txt", VfsSnapshot::file("hello")), + ( + "subdir", + VfsSnapshot::dir(HashMap::from([("nested.txt", VfsSnapshot::file("world"))])), + ), + ])), + ) + .unwrap(); + imfs.load_snapshot("/outside/other.txt", VfsSnapshot::file("external")) + .unwrap(); + + let vfs = Vfs::new(imfs); + let mut is_file = HashMap::new(); + is_file.insert(PathBuf::from("/root"), false); + is_file.insert(PathBuf::from("/root/known.txt"), true); + is_file.insert(PathBuf::from("/root/subdir"), false); + + vfs.set_prefetch_cache(PrefetchCache { + files: HashMap::new(), + is_file, + children: HashMap::new(), + dir_init: HashMap::new(), + walked_roots: vec![PathBuf::from("/root")], + }); + + let known = vfs.metadata("/root/known.txt").unwrap(); + assert!( + known.is_file(), + "cached entry should return correct metadata" + ); + + let subdir = vfs.metadata("/root/subdir").unwrap(); + assert!( + subdir.is_dir(), + "cached directory should return correct metadata" + ); + + let missing = vfs.metadata("/root/subdir/nested.txt"); + assert!( + missing.is_err(), + "path under walked root but not in cache should return NotFound" + ); + assert_eq!(missing.unwrap_err().kind(), io::ErrorKind::NotFound); + + let outside = vfs.metadata("/outside/other.txt").unwrap(); + assert!( + outside.is_file(), + "path outside walked roots should fall through to backend" + ); + } + #[test] fn prefetch_cache_read_after_write_ignores_cache() { let mut imfs = InMemoryFs::new(); diff --git a/crates/memofs/src/std_backend.rs b/crates/memofs/src/std_backend.rs index f3c2c145..bf44ca28 100644 --- a/crates/memofs/src/std_backend.rs +++ b/crates/memofs/src/std_backend.rs @@ -331,12 +331,8 @@ impl VfsBackend for StdBackend { } fn watch(&mut self, path: &Path, recursive: bool) -> io::Result<()> { - if self.watches.contains(path) { - if !recursive || self.recursive_watches.contains(path) { - return Ok(()); - } - // Upgrade: path was watched non-recursively, now needs recursive. - // Fall through to re-issue the watch with RecursiveMode::Recursive. + if self.watches.contains(path) && (!recursive || self.recursive_watches.contains(path)) { + return Ok(()); } if path .ancestors() diff --git a/src/lib.rs b/src/lib.rs index c7b4f501..1269c257 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,6 @@ pub use snapshot::{ InstanceContext, InstanceMetadata, InstanceSnapshot, InstanceWithMeta, InstanceWithMetaMut, RojoDescendants, RojoTree, }; -pub use snapshot_middleware::{snapshot_from_vfs, Middleware, ScriptType}; +pub use snapshot_middleware::{snapshot_from_vfs, Middleware, ScriptType, INIT_FILE_PRIORITY}; pub use syncback::{syncback_loop, FsSnapshot, SyncbackData, SyncbackResult, SyncbackSnapshot}; pub use web::interface as web_api; diff --git a/src/serve_session.rs b/src/serve_session.rs index 010580bc..f71f756f 100644 --- a/src/serve_session.rs +++ b/src/serve_session.rs @@ -349,9 +349,10 @@ impl ServeSession { let snap_start = Instant::now(); log::trace!("Generating snapshot of instances from VFS"); + let was_watch_enabled = vfs.is_watch_enabled(); vfs.set_watch_enabled(false); let snapshot = snapshot_from_vfs(&instance_context, vfs, start_path)?; - vfs.set_watch_enabled(true); + vfs.set_watch_enabled(was_watch_enabled); log::debug!("Snapshot built in {:.1?}", snap_start.elapsed()); vfs.clear_prefetch_cache(); diff --git a/src/snapshot_middleware/mod.rs b/src/snapshot_middleware/mod.rs index 29b880e8..c1273f73 100644 --- a/src/snapshot_middleware/mod.rs +++ b/src/snapshot_middleware/mod.rs @@ -544,6 +544,12 @@ mod test { "DEFAULT_PROJECT_NAMES entry {project_name:?} missing from INIT_FILE_PRIORITY" ); } + for &name in &priority_names { + assert!( + DEFAULT_PROJECT_NAMES.contains(&name), + "INIT_FILE_PRIORITY has Project entry {name:?} not in DEFAULT_PROJECT_NAMES" + ); + } } #[test] diff --git a/tests/tests/build.rs b/tests/tests/build.rs index 2d8865b3..69e6e0b4 100644 --- a/tests/tests/build.rs +++ b/tests/tests/build.rs @@ -263,12 +263,29 @@ fn parallel_snapshot_with_prefetch_cache() { children.sort_by(|a, b| a.file_name().cmp(&b.file_name())); } + let mut dir_init: HashMap> = + HashMap::new(); + for (dir_path, child_paths) in &children_map { + let child_names: std::collections::HashSet<&str> = child_paths + .iter() + .filter_map(|p| p.file_name().and_then(|n| n.to_str())) + .collect(); + let mut found = None; + for &(_, init_name) in librojo::INIT_FILE_PRIORITY { + if child_names.contains(init_name) { + found = Some((init_name.to_string(), dir_path.join(init_name))); + break; + } + } + dir_init.insert(dir_path.clone(), found); + } + let vfs_cached = memofs::Vfs::new_default(); vfs_cached.set_prefetch_cache(memofs::PrefetchCache { files, is_file, children: children_map, - dir_init: std::collections::HashMap::new(), + dir_init, walked_roots: Vec::new(), });