Skip to content

Commit 533fb87

Browse files
committed
Fix unwanted file modify event on MacOS on startup.
1 parent 8e5f6ce commit 533fb87

File tree

3 files changed

+28
-12
lines changed

3 files changed

+28
-12
lines changed

pkgs/watcher/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## 1.1.4-wip
22

3+
- Bug fix: with `FileWatcher` on MacOS, a modify event was sometimes reported if
4+
the file was created immediately before the watcher was created. Now, if the
5+
file exists when the watcher is created then this create event is ignored,
6+
which matches the Linux native and polling (Windows) watchers.
37
- Improve handling of subdirectories: ignore `PathNotFoundException` due to
48
subdirectory deletion racing with watcher internals, instead of raising
59
it on the event stream.

pkgs/watcher/lib/src/file_watcher/native.dart

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher {
3535

3636
StreamSubscription<List<FileSystemEvent>>? _subscription;
3737

38+
// On MacOS events can arrive from file changes before the `watch`. Check
39+
// existence on startup to differentiate creation before and after the
40+
// `watch`.
41+
final bool _startupExistenceCheck = Platform.isMacOS;
42+
Future<bool>? _existedAtStartupFuture;
43+
bool? _existedAtStartup;
44+
3845
_NativeFileWatcher(this.path) {
3946
_listen();
4047

@@ -44,20 +51,34 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher {
4451
}
4552

4653
void _listen() {
54+
var file = File(path);
55+
4756
// Batch the events together so that we can dedup them.
48-
_subscription = File(path)
57+
_subscription = file
4958
.watch()
5059
.batchEvents()
5160
.listen(_onBatch, onError: _eventsController.addError, onDone: _onDone);
61+
62+
if (_startupExistenceCheck) _existedAtStartupFuture = file.exists();
5263
}
5364

54-
void _onBatch(List<FileSystemEvent> batch) {
65+
void _onBatch(List<FileSystemEvent> batch) async {
5566
if (batch.any((event) => event.type == FileSystemEvent.delete)) {
5667
// If the file is deleted, the underlying stream will close. We handle
5768
// emitting our own REMOVE event in [_onDone].
5869
return;
5970
}
6071

72+
if (_startupExistenceCheck) {
73+
var existedAtStartup =
74+
_existedAtStartup ??= await _existedAtStartupFuture!;
75+
if (existedAtStartup &&
76+
batch.every((event) => event.type == FileSystemEvent.create)) {
77+
// It's a create event from before `watch` was called, ignore it.
78+
return;
79+
}
80+
}
81+
6182
_eventsController.add(WatchEvent(ChangeType.MODIFY, path));
6283
}
6384

pkgs/watcher/test/file_watcher/startup_race_tests.dart

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'dart:io';
6-
75
import 'package:test/test.dart';
86

97
import '../utils.dart';
@@ -27,13 +25,6 @@ void startupRaceTests({required bool isNative}) {
2725
);
2826
}
2927
await Future.wait(futures);
30-
31-
// TODO(davidmorgan): the MacOS watcher currently does get unwanted events,
32-
// fix it.
33-
if (isNative && Platform.isMacOS) {
34-
expect(events, greaterThan(50));
35-
} else {
36-
expect(events, 0);
37-
}
28+
expect(events, 0);
3829
});
3930
}

0 commit comments

Comments
 (0)