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
3 changes: 3 additions & 0 deletions pkgs/watcher/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
moved onto `b`, it would be reported as three events: delete `a`, delete `b`,
create `b`. Now it's reported as two events: delete `a`, modify `b`. This
matches the behavior of the Linux and MacOS watchers.
- Bug fix: with `DirectoryWatcher` on Windows, new links to direcories were
sometimes incorrectly handled as actual directories. Now they are reported
as files, matching the behavior of the Linux and MacOS watchers.
- Bug fix: with `PollingDirectoryWatcher`, fix spurious modify event emitted
because of a file delete during polling.

Expand Down
21 changes: 21 additions & 0 deletions pkgs/watcher/lib/src/directory_watcher/windows.dart
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,27 @@ class _WindowsDirectoryWatcher
Map<String, Set<Event>> _sortEvents(List<Event> batch) {
var eventsForPaths = <String, Set<Event>>{};

// On Windows new links to directories are sometimes reported by
// Directory.watch as directories. On all other platforms it reports them
// consistently as files. See https://github.com/dart-lang/sdk/issues/61797.
//
// The wrong type is because Windows creates links to directories as actual
// directories, then converts them to links. Directory.watch sometimes
// checks the type too early and gets the wrong result.
//
// The batch delay is plenty for the link to be fully created, so verify the
// file system entity type for all createDirectory` events, converting to
// `createFile` when needed.
for (var i = 0; i != batch.length; ++i) {
final event = batch[i];
if (event.type == EventType.createDirectory) {
if (FileSystemEntity.typeSync(event.path, followLinks: false) ==
FileSystemEntityType.link) {
batch[i] = Event.createFile(event.path);
}
}
}

// Events within directories that already have create events are not needed
// as the directory's full content will be listed.
var createdDirectories = unionAll(batch.map((event) {
Expand Down
13 changes: 10 additions & 3 deletions pkgs/watcher/test/directory_watcher/link_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ void _linkTests({required bool isNative}) {
writeFile('targets/a.target');
await startWatcher(path: 'links');

writeLink(link: 'links/a.link', target: 'targets/a.target');
writeLink(
link: 'links/a.link', target: 'targets/a.target', unawaitedAsync: true);

await expectAddEvent('links/a.link');
});
Expand Down Expand Up @@ -109,7 +110,10 @@ void _linkTests({required bool isNative}) {
createDir('targets/a.targetdir');
await startWatcher(path: 'links');

writeLink(link: 'links/a.link', target: 'targets/a.targetdir');
writeLink(
link: 'links/a.link',
target: 'targets/a.targetdir',
unawaitedAsync: true);

// TODO(davidmorgan): reconcile differences.
if (isNative) {
Expand All @@ -128,7 +132,10 @@ void _linkTests({required bool isNative}) {
writeFile('targets/a.targetdir/a.target');
await startWatcher(path: 'links');

writeLink(link: 'links/a.link', target: 'targets/a.targetdir');
writeLink(
link: 'links/a.link',
target: 'targets/a.targetdir',
unawaitedAsync: true);

// TODO(davidmorgan): reconcile differences.
if (isNative) {
Expand Down
11 changes: 10 additions & 1 deletion pkgs/watcher/test/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,14 @@ void writeFile(String path, {String? contents}) {
/// Writes a file in the sandbox at [link] pointing to [target].
///
/// [target] is relative to the sandbox, not to [link].
///
/// If [unawaitedAsync], the link is written asynchronously and not awaited.
/// Otherwise, it's synchronous. See the note in `windows.dart` for issue 61797
/// for why this is needed for testing on Windows.
void writeLink({
required String link,
required String target,
bool unawaitedAsync = false,
}) {
var fullPath = p.join(d.sandbox, link);

Expand All @@ -251,7 +256,11 @@ void writeLink({
dir.createSync(recursive: true);
}

Link(fullPath).createSync(p.join(d.sandbox, target));
if (unawaitedAsync) {
unawaited(Link(fullPath).create(p.join(d.sandbox, target)));
} else {
Link(fullPath).createSync(p.join(d.sandbox, target));
}
}

/// Deletes a file in the sandbox at [path].
Expand Down