blob: ebce488db196d447daaafa088dfb792de6a2779c [file] [log] [blame]
// Copyright (c) 2012, 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 'package:watcher/src/utils.dart';
import '../utils.dart';
void sharedTests() {
test('does not notify for files that already exist when started', () async {
// Make some pre-existing files.
writeFile('a.txt');
writeFile('b.txt');
await startWatcher();
// Change one after the watcher is running.
writeFile('b.txt', contents: 'modified');
// We should get a modify event for the changed file, but no add events
// for them before this.
await expectModifyEvent('b.txt');
});
test('notifies when a file is added', () async {
await startWatcher();
writeFile('file.txt');
await expectAddEvent('file.txt');
});
test('notifies when a file is modified', () async {
writeFile('file.txt');
await startWatcher();
writeFile('file.txt', contents: 'modified');
await expectModifyEvent('file.txt');
});
test('notifies when a file is removed', () async {
writeFile('file.txt');
await startWatcher();
deleteFile('file.txt');
await expectRemoveEvent('file.txt');
});
test('notifies when a file is modified multiple times', () async {
writeFile('file.txt');
await startWatcher();
writeFile('file.txt', contents: 'modified');
await expectModifyEvent('file.txt');
writeFile('file.txt', contents: 'modified again');
await expectModifyEvent('file.txt');
});
test('notifies even if the file contents are unchanged', () async {
writeFile('a.txt', contents: 'same');
writeFile('b.txt', contents: 'before');
await startWatcher();
writeFile('a.txt', contents: 'same');
writeFile('b.txt', contents: 'after');
await inAnyOrder([isModifyEvent('a.txt'), isModifyEvent('b.txt')]);
});
test('when the watched directory is deleted, removes all files', () async {
writeFile('dir/a.txt');
writeFile('dir/b.txt');
await startWatcher(path: 'dir');
deleteDir('dir');
await inAnyOrder([isRemoveEvent('dir/a.txt'), isRemoveEvent('dir/b.txt')]);
});
test('when the watched directory is moved, removes all files', () async {
writeFile('dir/a.txt');
writeFile('dir/b.txt');
await startWatcher(path: 'dir');
renameDir('dir', 'moved_dir');
createDir('dir');
await inAnyOrder([isRemoveEvent('dir/a.txt'), isRemoveEvent('dir/b.txt')]);
});
// Regression test for b/30768513.
test(
"doesn't crash when the directory is moved immediately after a subdir "
'is added', () async {
writeFile('dir/a.txt');
writeFile('dir/b.txt');
await startWatcher(path: 'dir');
createDir('dir/subdir');
renameDir('dir', 'moved_dir');
createDir('dir');
await inAnyOrder([isRemoveEvent('dir/a.txt'), isRemoveEvent('dir/b.txt')]);
});
group('moves', () {
test('notifies when a file is moved within the watched directory',
() async {
writeFile('old.txt');
await startWatcher();
renameFile('old.txt', 'new.txt');
await inAnyOrder([isAddEvent('new.txt'), isRemoveEvent('old.txt')]);
});
test('notifies when a file is moved from outside the watched directory',
() async {
writeFile('old.txt');
createDir('dir');
await startWatcher(path: 'dir');
renameFile('old.txt', 'dir/new.txt');
await expectAddEvent('dir/new.txt');
});
test('notifies when a file is moved outside the watched directory',
() async {
writeFile('dir/old.txt');
await startWatcher(path: 'dir');
renameFile('dir/old.txt', 'new.txt');
await expectRemoveEvent('dir/old.txt');
});
test('notifies when a file is moved onto an existing one', () async {
writeFile('from.txt');
writeFile('to.txt');
await startWatcher();
renameFile('from.txt', 'to.txt');
await inAnyOrder([isRemoveEvent('from.txt'), isModifyEvent('to.txt')]);
});
});
// Most of the time, when multiple filesystem actions happen in sequence,
// they'll be batched together and the watcher will see them all at once.
// These tests verify that the watcher normalizes and combine these events
// properly. However, very occasionally the events will be reported in
// separate batches, and the watcher will report them as though they occurred
// far apart in time, so each of these tests has a "backup case" to allow for
// that as well.
group('clustered changes', () {
test("doesn't notify when a file is created and then immediately removed",
() async {
writeFile('test.txt');
await startWatcher();
writeFile('file.txt');
deleteFile('file.txt');
// Backup case.
startClosingEventStream();
await allowEvents(() {
expectAddEvent('file.txt');
expectRemoveEvent('file.txt');
});
});
test(
'reports a modification when a file is deleted and then immediately '
'recreated', () async {
writeFile('file.txt');
await startWatcher();
deleteFile('file.txt');
writeFile('file.txt', contents: 're-created');
await allowEither(() {
expectModifyEvent('file.txt');
}, () {
// Backup case.
expectRemoveEvent('file.txt');
expectAddEvent('file.txt');
});
});
test(
'reports a modification when a file is moved and then immediately '
'recreated', () async {
writeFile('old.txt');
await startWatcher();
renameFile('old.txt', 'new.txt');
writeFile('old.txt', contents: 're-created');
await allowEither(() {
inAnyOrder([isModifyEvent('old.txt'), isAddEvent('new.txt')]);
}, () {
// Backup case.
expectRemoveEvent('old.txt');
expectAddEvent('new.txt');
expectAddEvent('old.txt');
});
});
test(
'reports a removal when a file is modified and then immediately '
'removed', () async {
writeFile('file.txt');
await startWatcher();
writeFile('file.txt', contents: 'modified');
deleteFile('file.txt');
// Backup case.
await allowModifyEvent('file.txt');
await expectRemoveEvent('file.txt');
});
test('reports an add when a file is added and then immediately modified',
() async {
await startWatcher();
writeFile('file.txt');
writeFile('file.txt', contents: 'modified');
await expectAddEvent('file.txt');
// Backup case.
startClosingEventStream();
await allowModifyEvent('file.txt');
});
});
group('subdirectories', () {
test('watches files in subdirectories', () async {
await startWatcher();
writeFile('a/b/c/d/file.txt');
await expectAddEvent('a/b/c/d/file.txt');
});
test(
'notifies when a subdirectory is moved within the watched directory '
'and then its contents are modified', () async {
writeFile('old/file.txt');
await startWatcher();
renameDir('old', 'new');
await inAnyOrder(
[isRemoveEvent('old/file.txt'), isAddEvent('new/file.txt')]);
writeFile('new/file.txt', contents: 'modified');
await expectModifyEvent('new/file.txt');
});
test('notifies when a file is replaced by a subdirectory', () async {
writeFile('new');
writeFile('old/file.txt');
await startWatcher();
deleteFile('new');
renameDir('old', 'new');
await inAnyOrder([
isRemoveEvent('new'),
isRemoveEvent('old/file.txt'),
isAddEvent('new/file.txt')
]);
});
test('notifies when a subdirectory is replaced by a file', () async {
writeFile('old');
writeFile('new/file.txt');
await startWatcher();
renameDir('new', 'newer');
renameFile('old', 'new');
await inAnyOrder([
isRemoveEvent('new/file.txt'),
isAddEvent('newer/file.txt'),
isRemoveEvent('old'),
isAddEvent('new')
]);
}, onPlatform: {
'mac-os': Skip('https://github.com/dart-lang/watcher/issues/21')
});
test('emits events for many nested files added at once', () async {
withPermutations((i, j, k) => writeFile('sub/sub-$i/sub-$j/file-$k.txt'));
createDir('dir');
await startWatcher(path: 'dir');
renameDir('sub', 'dir/sub');
await inAnyOrder(withPermutations(
(i, j, k) => isAddEvent('dir/sub/sub-$i/sub-$j/file-$k.txt')));
});
test('emits events for many nested files removed at once', () async {
withPermutations(
(i, j, k) => writeFile('dir/sub/sub-$i/sub-$j/file-$k.txt'));
createDir('dir');
await startWatcher(path: 'dir');
// Rename the directory rather than deleting it because native watchers
// report a rename as a single DELETE event for the directory, whereas
// they report recursive deletion with DELETE events for every file in the
// directory.
renameDir('dir/sub', 'sub');
await inAnyOrder(withPermutations(
(i, j, k) => isRemoveEvent('dir/sub/sub-$i/sub-$j/file-$k.txt')));
});
test('emits events for many nested files moved at once', () async {
withPermutations(
(i, j, k) => writeFile('dir/old/sub-$i/sub-$j/file-$k.txt'));
createDir('dir');
await startWatcher(path: 'dir');
renameDir('dir/old', 'dir/new');
await inAnyOrder(unionAll(withPermutations((i, j, k) {
return {
isRemoveEvent('dir/old/sub-$i/sub-$j/file-$k.txt'),
isAddEvent('dir/new/sub-$i/sub-$j/file-$k.txt')
};
})));
});
test(
'emits events for many files added at once in a subdirectory with the '
'same name as a removed file', () async {
writeFile('dir/sub');
withPermutations((i, j, k) => writeFile('old/sub-$i/sub-$j/file-$k.txt'));
await startWatcher(path: 'dir');
deleteFile('dir/sub');
renameDir('old', 'dir/sub');
var events = withPermutations(
(i, j, k) => isAddEvent('dir/sub/sub-$i/sub-$j/file-$k.txt'));
events.add(isRemoveEvent('dir/sub'));
await inAnyOrder(events);
});
});
}