Skip to content

Commit e6ce99d

Browse files
authored
Test new link to directory races, fix for them (#2223)
1 parent 9053fae commit e6ce99d

File tree

4 files changed

+44
-4
lines changed

4 files changed

+44
-4
lines changed

pkgs/watcher/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
moved onto `b`, it would be reported as three events: delete `a`, delete `b`,
1313
create `b`. Now it's reported as two events: delete `a`, modify `b`. This
1414
matches the behavior of the Linux and MacOS watchers.
15+
- Bug fix: with `DirectoryWatcher` on Windows, new links to direcories were
16+
sometimes incorrectly handled as actual directories. Now they are reported
17+
as files, matching the behavior of the Linux and MacOS watchers.
1518
- Bug fix: with `PollingDirectoryWatcher`, fix spurious modify event emitted
1619
because of a file delete during polling.
1720

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,27 @@ class _WindowsDirectoryWatcher
261261
Map<String, Set<Event>> _sortEvents(List<Event> batch) {
262262
var eventsForPaths = <String, Set<Event>>{};
263263

264+
// On Windows new links to directories are sometimes reported by
265+
// Directory.watch as directories. On all other platforms it reports them
266+
// consistently as files. See https://github.com/dart-lang/sdk/issues/61797.
267+
//
268+
// The wrong type is because Windows creates links to directories as actual
269+
// directories, then converts them to links. Directory.watch sometimes
270+
// checks the type too early and gets the wrong result.
271+
//
272+
// The batch delay is plenty for the link to be fully created, so verify the
273+
// file system entity type for all createDirectory` events, converting to
274+
// `createFile` when needed.
275+
for (var i = 0; i != batch.length; ++i) {
276+
final event = batch[i];
277+
if (event.type == EventType.createDirectory) {
278+
if (FileSystemEntity.typeSync(event.path, followLinks: false) ==
279+
FileSystemEntityType.link) {
280+
batch[i] = Event.createFile(event.path);
281+
}
282+
}
283+
}
284+
264285
// Events within directories that already have create events are not needed
265286
// as the directory's full content will be listed.
266287
var createdDirectories = unionAll(batch.map((event) {

pkgs/watcher/test/directory_watcher/link_tests.dart

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ void _linkTests({required bool isNative}) {
2121
writeFile('targets/a.target');
2222
await startWatcher(path: 'links');
2323

24-
writeLink(link: 'links/a.link', target: 'targets/a.target');
24+
writeLink(
25+
link: 'links/a.link', target: 'targets/a.target', unawaitedAsync: true);
2526

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

112-
writeLink(link: 'links/a.link', target: 'targets/a.targetdir');
113+
writeLink(
114+
link: 'links/a.link',
115+
target: 'targets/a.targetdir',
116+
unawaitedAsync: true);
113117

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

131-
writeLink(link: 'links/a.link', target: 'targets/a.targetdir');
135+
writeLink(
136+
link: 'links/a.link',
137+
target: 'targets/a.targetdir',
138+
unawaitedAsync: true);
132139

133140
// TODO(davidmorgan): reconcile differences.
134141
if (isNative) {

pkgs/watcher/test/utils.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,14 @@ void writeFile(String path, {String? contents}) {
239239
/// Writes a file in the sandbox at [link] pointing to [target].
240240
///
241241
/// [target] is relative to the sandbox, not to [link].
242+
///
243+
/// If [unawaitedAsync], the link is written asynchronously and not awaited.
244+
/// Otherwise, it's synchronous. See the note in `windows.dart` for issue 61797
245+
/// for why this is needed for testing on Windows.
242246
void writeLink({
243247
required String link,
244248
required String target,
249+
bool unawaitedAsync = false,
245250
}) {
246251
var fullPath = p.join(d.sandbox, link);
247252

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

254-
Link(fullPath).createSync(p.join(d.sandbox, target));
259+
if (unawaitedAsync) {
260+
unawaited(Link(fullPath).create(p.join(d.sandbox, target)));
261+
} else {
262+
Link(fullPath).createSync(p.join(d.sandbox, target));
263+
}
255264
}
256265

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

0 commit comments

Comments
 (0)