Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions pkgs/watcher/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
36 changes: 30 additions & 6 deletions pkgs/watcher/lib/src/file_watcher/native.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class _NativeFileWatcher implements FileWatcher, ManuallyClosedWatcher {

StreamSubscription<List<FileSystemEvent>>? _subscription;

/// On MacOS only, whether the file existed on startup.
bool? _existedAtStartup;

_NativeFileWatcher(this.path) {
_listen();

Expand All @@ -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<FileSystemEvent> batch) {
Expand All @@ -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));
}

Expand Down
2 changes: 1 addition & 1 deletion pkgs/watcher/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
11 changes: 1 addition & 10 deletions pkgs/watcher/test/file_watcher/startup_race_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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(10));
} else {
expect(events, 0);
}
expect(events, 0);
});
}