blob: 05b8481494df9f838aebeb8bd7542799e8768bd8 [file]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// 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 'package:test/test.dart';
import '../utils.dart';
void linkTests({required bool isNative}) {
for (var i = 0; i != runsPerTest; ++i) {
_linkTests(isNative: isNative);
}
}
void _linkTests({required bool isNative}) {
test('notifies when a link is added', () async {
createDir('targets');
createDir('links');
writeFile('targets/a.target');
await startWatcher(path: 'links');
writeLink(
link: 'links/a.link',
target: 'targets/a.target',
unawaitedAsync: true,
);
await expectAddEvent('links/a.link');
});
test('notifies when a link is replaced with a link to a different target '
'with the same contents', () async {
createDir('targets');
createDir('links');
writeFile('targets/a.target');
sleepUntilNewModificationTime();
writeFile('targets/b.target');
writeLink(link: 'links/a.link', target: 'targets/a.target');
await startWatcher(path: 'links');
deleteLink('links/a.link');
writeLink(link: 'links/a.link', target: 'targets/b.target');
await expectModifyEvent('links/a.link');
});
test('notifies when a link is replaced with a link to a different target '
'with different contents', () async {
writeFile('targets/a.target', contents: 'a');
writeFile('targets/b.target', contents: 'ab');
writeLink(link: 'links/a.link', target: 'targets/a.target');
await startWatcher(path: 'links');
deleteLink('links/a.link');
writeLink(link: 'links/a.link', target: 'targets/b.target');
await expectModifyEvent('links/a.link');
});
test('does not notify when a link target is modified', () async {
createDir('targets');
createDir('links');
writeFile('targets/a.target');
writeLink(link: 'links/a.link', target: 'targets/a.target');
await startWatcher(path: 'links');
writeFile('targets/a.target', contents: 'modified');
// Native watchers treat links as files, polling watcher polls through them.
if (isNative) {
await expectNoEvents();
} else {
await expectModifyEvent('links/a.link');
}
});
test('does not notify when a link target is removed', () async {
createDir('targets');
createDir('links');
writeFile('targets/a.target');
writeLink(link: 'links/a.link', target: 'targets/a.target');
await startWatcher(path: 'links');
deleteFile('targets/a.target');
// Native watchers treat links as files, polling watcher polls through them.
if (isNative) {
await expectNoEvents();
} else {
await expectRemoveEvent('links/a.link');
}
});
test('notifies when a link is moved within the watched directory', () async {
createDir('targets');
createDir('links');
writeFile('targets/a.target');
writeLink(link: 'links/a.link', target: 'targets/a.target');
await startWatcher(path: 'links');
renameLink('links/a.link', 'links/b.link');
await inAnyOrder([
isAddEvent('links/b.link'),
isRemoveEvent('links/a.link'),
]);
});
test('notifies when a link to an empty directory is added', () async {
createDir('targets');
createDir('links');
createDir('targets/a.targetdir');
await startWatcher(path: 'links');
writeLink(
link: 'links/a.link',
target: 'targets/a.targetdir',
unawaitedAsync: true,
);
// Native watchers treat links as files, polling watcher polls through them.
if (isNative) {
await expectAddEvent('links/a.link');
} else {
await expectNoEvents();
}
});
test('does not notify about directory contents '
'when a link to a directory is added', () async {
createDir('targets');
createDir('links');
createDir('targets/a.targetdir');
writeFile('targets/a.targetdir/a.target');
await startWatcher(path: 'links');
writeLink(
link: 'links/a.link',
target: 'targets/a.targetdir',
unawaitedAsync: true,
);
// Native watchers treat links as files, polling watcher polls through them.
if (isNative) {
await expectAddEvent('links/a.link');
} else {
await expectAddEvent('links/a.link/a.target');
}
});
test('notifies when a file is added to a linked directory', () async {
createDir('targets');
createDir('links');
createDir('targets/a.targetdir');
writeLink(link: 'links/a.link', target: 'targets/a.targetdir');
await startWatcher(path: 'links');
writeFile('targets/a.targetdir/a.txt');
// Native watchers treat links as files, polling watcher polls through them.
if (isNative) {
await expectNoEvents();
} else {
await expectAddEvent('links/a.link/a.txt');
}
});
test('notifies when a file is added to a newly linked directory', () async {
createDir('targets');
createDir('links');
createDir('targets/a.targetdir');
await startWatcher(path: 'links');
writeLink(link: 'links/a.link', target: 'targets/a.targetdir');
writeFile('targets/a.targetdir/a.txt');
// Native watchers treat links as files, polling watcher polls through them.
if (isNative) {
await expectAddEvent('links/a.link');
} else {
await expectAddEvent('links/a.link/a.txt');
}
});
test('notifies about linked directory contents when a directory with a '
'linked subdirectory is moved in', () async {
createDir('targets');
createDir('links');
createDir('targets/a.targetdir');
createDir('watched');
writeFile('targets/a.targetdir/a.txt');
writeLink(link: 'links/a.link', target: 'targets/a.targetdir');
await startWatcher(path: 'watched');
renameDir('links', 'watched/links');
// Native watchers treat links as files, polling watcher polls through them.
if (isNative) {
await expectAddEvent('watched/links/a.link');
} else {
await expectAddEvent('watched/links/a.link/a.txt');
}
});
test('notifies about linked directory contents when a directory with a '
'linked subdirectory containing a link loop is moved in', () async {
createDir('targets');
createDir('links');
createDir('targets/a.targetdir');
createDir('watched');
writeFile('targets/a.targetdir/a.txt');
writeLink(link: 'links/a.link', target: 'targets/a.targetdir');
writeLink(
link: 'targets/a.targetdir/cycle.link',
target: 'targets/a.targetdir',
);
await startWatcher(path: 'watched');
renameDir('links', 'watched/links');
// Native watchers treat links as files, polling watcher polls through them.
if (isNative) {
await expectAddEvent('watched/links/a.link');
} else {
await expectAddEvent('watched/links/a.link/a.txt');
}
await expectNoEvents();
});
test('notifies about linked directory contents when a directory with a '
'linked subdirectory containing two link loops is moved in', () async {
createDir('targets');
createDir('links');
createDir('targets/a.targetdir');
createDir('watched');
writeFile('targets/a.targetdir/a.txt');
writeLink(link: 'links/a.link', target: 'targets/a.targetdir');
writeLink(
link: 'targets/a.targetdir/cycle1.link',
target: 'targets/a.targetdir',
);
writeLink(
link: 'targets/a.targetdir/cycle2.link',
target: 'targets/a.targetdir',
);
await startWatcher(path: 'watched');
renameDir('links', 'watched/links');
// Native watchers treat links as files, polling watcher polls through them.
if (isNative) {
await expectAddEvent('watched/links/a.link');
} else {
await expectAddEvent('watched/links/a.link/a.txt');
}
await expectNoEvents();
});
test('is not slow in a directory with many link loops', () async {
createDir('links');
writeLink(link: 'links/a/cycle1.link', target: 'links/a');
writeLink(link: 'links/a/cycle2.link', target: 'links/a');
writeLink(link: 'links/a/cycle3.link', target: 'links/a');
writeLink(link: 'links/a/cycle4.link', target: 'links/a');
writeLink(link: 'links/a/cycle5.link', target: 'links/a');
writeLink(link: 'links/a/cycle6.link', target: 'links/a');
writeLink(link: 'links/a/cycle7.link', target: 'links/a');
final stopwatch = Stopwatch()..start();
await startWatcher(path: 'links');
expect(stopwatch.elapsedMilliseconds, lessThan(500));
});
}