Skip to content

Commit 43f67a5

Browse files
committed
Fix MacOS watch issue with same name directory deletes.
1 parent 9480d20 commit 43f67a5

File tree

3 files changed

+34
-6
lines changed

3 files changed

+34
-6
lines changed

pkgs/watcher/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
various situations involving subdirectory moves.
4040
- Bug fix: with `DirectoryWatcher` on MacOS, fix events for changes in new
4141
directories: don't emit duplicate ADD, don't emit MODIFY without ADD.
42+
- Bug fix: with `DirectoryWatcher` on MacOS, fix handling of repeated deletes
43+
of a directory with the same name in a short space of time.
4244
- Bug fix: with `FileWatcher` on MacOS, a modify event was sometimes reported if
4345
the file was created immediately before the watcher was created. Now, if the
4446
file exists when the watcher is created then this modify event is not sent.

pkgs/watcher/lib/src/directory_watcher/mac_os.dart

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,18 +195,23 @@ class _MacOSDirectoryWatcher
195195
batch = batch.where((event) => event.path != path).toList();
196196

197197
// Events within directories that already have create events are not needed
198-
// as the directory's full content will be listed.
199-
var createdDirectories = unionAll(batch.map((event) {
200-
return event.type == EventType.createDirectory
198+
// as the directory's full content will be listed. And, events that are
199+
// under direcory deletes are not needed as all files are removed.
200+
var ignoredPaths = unionAll(batch.map((event) {
201+
return event.type == EventType.createDirectory ||
202+
// Events don't distinguish file deletes from directory deletes,
203+
// but that doesn't matter here as deleted files will not match
204+
// as the parent directory of any file.
205+
event.type == EventType.delete
201206
? {event.path}
202207
: const <String>{};
203208
}));
204209

205-
bool isInCreatedDirectory(String path) =>
206-
createdDirectories.any((dir) => path != dir && p.isWithin(dir, path));
210+
bool isUnderDeleteOrDirectoryCreate(String path) =>
211+
ignoredPaths.any((dir) => path != dir && p.isWithin(dir, path));
207212

208213
void addEvent(String path, Event event) {
209-
if (isInCreatedDirectory(path)) return;
214+
if (isUnderDeleteOrDirectoryCreate(path)) return;
210215
eventsForPaths.putIfAbsent(path, () => <Event>{}).add(event);
211216
}
212217

pkgs/watcher/test/directory_watcher/file_tests.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import 'dart:io';
88
import 'dart:isolate';
99

1010
import 'package:async/async.dart';
11+
import 'package:path/path.dart' as p;
1112
import 'package:test/test.dart';
1213
import 'package:test_descriptor/test_descriptor.dart' as d;
1314
import 'package:watcher/src/utils.dart';
15+
import 'package:watcher/watcher.dart';
1416

1517
import '../utils.dart';
1618

@@ -471,6 +473,25 @@ void _fileTests({required bool isNative}) {
471473
await expectNoEvents();
472474
});
473475

476+
test('multiple deletes order is respected', () async {
477+
createDir('watched');
478+
writeFile('a/1');
479+
writeFile('b/1');
480+
481+
await startWatcher(path: 'watched');
482+
483+
renameDir('a', 'watched/x');
484+
renameDir('watched/x', 'a');
485+
renameDir('b', 'watched/x');
486+
writeFile('watched/x/1', contents: 'updated');
487+
// This is a "duplicate" delete of x, but it's not the same delete and the
488+
// watcher needs to notice that it happens after the update to x/1 so
489+
// there is no file left behind.
490+
renameDir('watched/x', 'b');
491+
492+
await expectNoEvents();
493+
});
494+
474495
test('subdirectory watching is robust against races', () async {
475496
// Make sandboxPath accessible to child isolates created by Isolate.run.
476497
final sandboxPath = d.sandbox;

0 commit comments

Comments
 (0)