blob: c08137ea8614386d0ed404f0d2071a6053dea9b3 [file] [log] [blame]
// Copyright (c) 2018, 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 'dart:io' as io;
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:watcher/watcher.dart';
import 'physical_file_system_test.dart' show BaseTest;
main() {
if (!bool.fromEnvironment('skipPhysicalResourceProviderTests')) {
defineReflectiveSuite(() {
defineReflectiveTests(PhysicalResourceProviderWatchTest);
});
}
}
@reflectiveTest
class PhysicalResourceProviderWatchTest extends BaseTest {
test_watchFile_delete() {
var filePath = path.join(tempPath, 'foo');
var file = io.File(filePath);
file.writeAsStringSync('contents 1');
return _watchingFile(filePath, (changesReceived) {
expect(changesReceived, hasLength(0));
file.deleteSync();
return _delayed(() {
expect(changesReceived, hasLength(1));
if (io.Platform.isWindows) {
// See https://github.com/dart-lang/sdk/issues/23762
// Not sure why this breaks under Windows, but testing to see whether
// we are running Windows causes the type to change. For now we print
// the type out of curiosity.
print('PhysicalResourceProviderWatchTest:test_watchFile_delete '
'received an event with type = ${changesReceived[0].type}');
} else {
expect(changesReceived[0].type, equals(ChangeType.REMOVE));
}
expect(changesReceived[0].path, equals(filePath));
});
});
}
test_watchFile_modify() {
var filePath = path.join(tempPath, 'foo');
var file = io.File(filePath);
file.writeAsStringSync('contents 1');
return _watchingFile(filePath, (changesReceived) {
expect(changesReceived, hasLength(0));
file.writeAsStringSync('contents 2');
return _delayed(() {
expect(changesReceived, hasLength(1));
expect(changesReceived[0].type, equals(ChangeType.MODIFY));
expect(changesReceived[0].path, equals(filePath));
});
});
}
test_watchFolder_createFile() {
return _watchingFolder(tempPath, (changesReceived) {
expect(changesReceived, hasLength(0));
var filePath = path.join(tempPath, 'foo');
io.File(filePath).writeAsStringSync('contents');
return _delayed(() {
// There should be an "add" event indicating that the file was added.
// Depending on how long it took to write the contents, it may be
// followed by "modify" events.
expect(changesReceived, isNotEmpty);
expect(changesReceived[0].type, equals(ChangeType.ADD));
expect(changesReceived[0].path, equals(filePath));
for (int i = 1; i < changesReceived.length; i++) {
expect(changesReceived[i].type, equals(ChangeType.MODIFY));
expect(changesReceived[i].path, equals(filePath));
}
});
});
}
test_watchFolder_deleteFile() {
var filePath = path.join(tempPath, 'foo');
var file = io.File(filePath);
file.writeAsStringSync('contents 1');
return _watchingFolder(tempPath, (changesReceived) {
expect(changesReceived, hasLength(0));
file.deleteSync();
return _delayed(() {
expect(changesReceived, hasLength(1));
expect(changesReceived[0].type, equals(ChangeType.REMOVE));
expect(changesReceived[0].path, equals(filePath));
});
});
}
test_watchFolder_modifyFile() {
var filePath = path.join(tempPath, 'foo');
var file = io.File(filePath);
file.writeAsStringSync('contents 1');
return _watchingFolder(tempPath, (changesReceived) {
expect(changesReceived, hasLength(0));
file.writeAsStringSync('contents 2');
return _delayed(() {
expect(changesReceived, hasLength(1));
expect(changesReceived[0].type, equals(ChangeType.MODIFY));
expect(changesReceived[0].path, equals(filePath));
});
});
}
test_watchFolder_modifyFile_inSubDir() {
var fooPath = path.join(tempPath, 'foo');
io.Directory(fooPath).createSync();
var barPath = path.join(tempPath, 'bar');
var file = io.File(barPath);
file.writeAsStringSync('contents 1');
return _watchingFolder(tempPath, (changesReceived) {
expect(changesReceived, hasLength(0));
file.writeAsStringSync('contents 2');
return _delayed(() {
expect(changesReceived, anyOf(hasLength(1), hasLength(2)));
expect(changesReceived[0].type, equals(ChangeType.MODIFY));
expect(changesReceived[0].path, equals(barPath));
});
});
}
Future _delayed(Function() computation) {
// Give the tests 1 second to detect the changes. While it may only
// take up to a few hundred ms, a whole second gives a good margin
// for when running tests.
return Future.delayed(Duration(seconds: 1), computation);
}
_watchingFile(
String filePath, Function(List<WatchEvent> changesReceived) test) {
// Delay before we start watching the file. This is necessary
// because on MacOS, file modifications that occur just before we
// start watching are sometimes misclassified as happening just after
// we start watching.
return _delayed(() {
var file =
PhysicalResourceProvider.INSTANCE.getResource(filePath) as File;
var changesReceived = <WatchEvent>[];
var subscription = file.changes.listen(changesReceived.add);
// Delay running the rest of the test to allow file.changes propagate.
return _delayed(() => test(changesReceived)).whenComplete(() {
subscription.cancel();
});
});
}
_watchingFolder(
String filePath, Function(List<WatchEvent> changesReceived) test) {
// Delay before we start watching the folder. This is necessary
// because on MacOS, file modifications that occur just before we
// start watching are sometimes misclassified as happening just after
// we start watching.
return _delayed(() {
var folder =
PhysicalResourceProvider.INSTANCE.getResource(filePath) as Folder;
var changesReceived = <WatchEvent>[];
var subscription = folder.changes.listen(changesReceived.add);
// Delay running the rest of the test to allow folder.changes to
// take a snapshot of the current directory state. Otherwise it
// won't be able to reliably distinguish new files from modified
// ones.
return _delayed(() => test(changesReceived)).whenComplete(() {
subscription.cancel();
});
});
}
}