blob: 0e496fff7dc09f899ab64bb68011c6d3e60cb24e [file] [log] [blame]
// 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));
});
}