fix: ignore local watcher access events
The peer CLI could flood LocalGamesUpdated events when run from the Docker harness. The local monitor rescans game roots, and some bind-mounted filesystems report those read/close operations back as notify access events. Treating those non-mutating events as real library changes queued another rescan, making the headless CLI unusable for manual peer-to-peer testing. Ignore access events before mapping paths to game IDs. Create, modify, remove, and rename events still flow through the existing per-game rescan gate, while fallback scans continue to reconcile missed writes. Test Plan: - just fmt - just test - just clippy Refs: manual peer-cli P2P testing
This commit is contained in:
@@ -7,7 +7,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use tokio::sync::{RwLock, mpsc::UnboundedSender};
|
||||
|
||||
use crate::{
|
||||
@@ -219,6 +219,10 @@ async fn handle_watch_event(
|
||||
}
|
||||
};
|
||||
|
||||
if matches!(event.kind, EventKind::Access(_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let game_dir = ctx.game_dir.read().await.clone();
|
||||
let ids = event
|
||||
.paths
|
||||
@@ -338,7 +342,10 @@ mod tests {
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use notify::EventKind;
|
||||
use notify::{
|
||||
EventKind,
|
||||
event::{AccessKind, AccessMode},
|
||||
};
|
||||
use tokio::sync::{RwLock, mpsc};
|
||||
use tokio_util::{sync::CancellationToken, task::TaskTracker};
|
||||
|
||||
@@ -470,6 +477,40 @@ mod tests {
|
||||
assert!(gate.pending.read().await.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn access_watch_event_is_ignored() {
|
||||
let temp = TempDir::new("lanspread-local-monitor");
|
||||
write_file(&temp.path().join("game").join("version.ini"), b"20250101");
|
||||
let ctx = test_ctx(
|
||||
temp.path().to_path_buf(),
|
||||
HashSet::from(["game".to_string()]),
|
||||
);
|
||||
let gate = RescanGate::default();
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
|
||||
handle_watch_event(
|
||||
&ctx,
|
||||
&tx,
|
||||
&gate,
|
||||
Ok(
|
||||
Event::new(EventKind::Access(AccessKind::Close(AccessMode::Read)))
|
||||
.add_path(temp.path().join("game").join("version.ini")),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
ctx.task_tracker.close();
|
||||
ctx.task_tracker.wait().await;
|
||||
|
||||
assert!(
|
||||
tokio::time::timeout(Duration::from_millis(50), rx.recv())
|
||||
.await
|
||||
.is_err(),
|
||||
"access events should not schedule a UI update"
|
||||
);
|
||||
assert!(gate.running.read().await.is_empty());
|
||||
assert!(gate.pending.read().await.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn burst_watch_events_collapse_to_two_rescans_for_same_game() {
|
||||
let temp = TempDir::new("lanspread-local-monitor");
|
||||
|
||||
Reference in New Issue
Block a user