From f15eb9d8ad8c997e4fa664f34740f32c2a42204d Mon Sep 17 00:00:00 2001 From: David Morgan Date: Mon, 29 Sep 2025 13:35:43 +0200 Subject: [PATCH 1/2] Add test coverage around startup race and missing file on startup. --- pkgs/watcher/test/file_watcher/startup_race_tests.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/watcher/test/file_watcher/startup_race_tests.dart b/pkgs/watcher/test/file_watcher/startup_race_tests.dart index 59e4f8e7b..37814e1ab 100644 --- a/pkgs/watcher/test/file_watcher/startup_race_tests.dart +++ b/pkgs/watcher/test/file_watcher/startup_race_tests.dart @@ -31,7 +31,7 @@ void startupRaceTests({required bool isNative}) { // TODO(davidmorgan): the MacOS watcher currently does get unwanted events, // fix it. if (isNative && Platform.isMacOS) { - expect(events, greaterThan(10)); + expect(events, greaterThan(50)); } else { expect(events, 0); } From c69889e8d19f3e05ac8a0ffe719268bdc179d5d1 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Mon, 29 Sep 2025 16:15:36 +0200 Subject: [PATCH 2/2] Fix unwanted file modify event on MacOS on startup. --- pkgs/watcher/CHANGELOG.md | 7 ++++ pkgs/watcher/lib/src/file_watcher/native.dart | 36 +++++++++++++++---- pkgs/watcher/pubspec.yaml | 2 +- .../test/file_watcher/startup_race_tests.dart | 11 +----- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/pkgs/watcher/CHANGELOG.md b/pkgs/watcher/CHANGELOG.md index 562c2e587..686126dc1 100644 --- a/pkgs/watcher/CHANGELOG.md +++ b/pkgs/watcher/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.1.5-wip + +- Bug fix: with `FileWatcher` on MacOS, a modify event was sometimes reported if + the file was created immediately before the watcher was created. Now, if the + file exists when the watcher is created then this modify event is not sent. + This matches the Linux native and polling (Windows) watchers. + ## 1.1.4 - Improve handling of subdirectories: ignore `PathNotFoundException` due to diff --git a/pkgs/watcher/lib/src/file_watcher/native.dart b/pkgs/watcher/lib/src/file_watcher/native.dart index 502aa1095..28cf8a180 100644 --- a/pkgs/watcher/lib/src/file_watcher/native.dart +++ b/pkgs/watcher/lib/src/file_watcher/native.dart @@ -35,6 +35,9 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher { StreamSubscription>? _subscription; + /// On MacOS only, whether the file existed on startup. + bool? _existedAtStartup; + _NativeFileWatcher(this.path) { _listen(); @@ -43,12 +46,23 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher { _readyCompleter.complete(); } - void _listen() { - // Batch the events together so that we can dedup them. - _subscription = File(path) - .watch() - .batchEvents() - .listen(_onBatch, onError: _eventsController.addError, onDone: _onDone); + void _listen() async { + var file = File(path); + + // Batch the events together so that we can dedupe them. + var stream = file.watch().batchEvents(); + + if (Platform.isMacOS) { + var existedAtStartupFuture = file.exists(); + // Delay processing watch events until the existence check finishes. + stream = stream.asyncMap((event) async { + _existedAtStartup ??= await existedAtStartupFuture; + return event; + }); + } + + _subscription = stream.listen(_onBatch, + onError: _eventsController.addError, onDone: _onDone); } void _onBatch(List batch) { @@ -58,6 +72,16 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher { return; } + if (Platform.isMacOS) { + // On MacOS, a spurious `create` event can be received for a file that is + // created just before the `watch`. If the file existed at startup then it + // should be ignored. + if (_existedAtStartup! && + batch.every((event) => event.type == FileSystemEvent.create)) { + return; + } + } + _eventsController.add(WatchEvent(ChangeType.MODIFY, path)); } diff --git a/pkgs/watcher/pubspec.yaml b/pkgs/watcher/pubspec.yaml index eff642fe5..c7119052d 100644 --- a/pkgs/watcher/pubspec.yaml +++ b/pkgs/watcher/pubspec.yaml @@ -1,5 +1,5 @@ name: watcher -version: 1.1.4 +version: 1.1.5-wip description: >- A file system watcher. It monitors changes to contents of directories and sends notifications when files have been added, removed, or modified. diff --git a/pkgs/watcher/test/file_watcher/startup_race_tests.dart b/pkgs/watcher/test/file_watcher/startup_race_tests.dart index 37814e1ab..50f5328f6 100644 --- a/pkgs/watcher/test/file_watcher/startup_race_tests.dart +++ b/pkgs/watcher/test/file_watcher/startup_race_tests.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'dart:io'; - import 'package:test/test.dart'; import '../utils.dart'; @@ -27,13 +25,6 @@ void startupRaceTests({required bool isNative}) { ); } await Future.wait(futures); - - // TODO(davidmorgan): the MacOS watcher currently does get unwanted events, - // fix it. - if (isNative && Platform.isMacOS) { - expect(events, greaterThan(50)); - } else { - expect(events, 0); - } + expect(events, 0); }); }