blob: a302c933eef5bac69a45d8d01471363e8dc536f1 [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 Set.from([
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);
});
});
}