| // Copyright (c) 2020, 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:async'; | |
| import 'package:test/test.dart'; | |
| import 'package:watcher/watcher.dart'; | |
| void main() { | |
| late _MemFs memFs; | |
| final memFsFactoryId = 'MemFs'; | |
| final noOpFactoryId = 'NoOp'; | |
| setUpAll(() { | |
| memFs = _MemFs(); | |
| var memFsWatcherFactory = _MemFsWatcherFactory(memFs); | |
| var noOpWatcherFactory = _NoOpWatcherFactory(); | |
| registerCustomWatcher( | |
| noOpFactoryId, | |
| noOpWatcherFactory.createDirectoryWatcher, | |
| noOpWatcherFactory.createFileWatcher, | |
| ); | |
| registerCustomWatcher( | |
| memFsFactoryId, | |
| memFsWatcherFactory.createDirectoryWatcher, | |
| memFsWatcherFactory.createFileWatcher, | |
| ); | |
| }); | |
| test('notifies for files', () async { | |
| var watcher = FileWatcher('file.txt'); | |
| var completer = Completer<WatchEvent>(); | |
| watcher.events.listen((event) => completer.complete(event)); | |
| await watcher.ready; | |
| memFs.add('file.txt'); | |
| var event = await completer.future; | |
| expect(event.type, ChangeType.ADD); | |
| expect(event.path, 'file.txt'); | |
| }); | |
| test('notifies for directories', () async { | |
| var watcher = DirectoryWatcher('/dir'); | |
| var completer = Completer<WatchEvent>(); | |
| watcher.events.listen((event) => completer.complete(event)); | |
| await watcher.ready; | |
| memFs.add('/dir'); | |
| var event = await completer.future; | |
| expect(event.type, ChangeType.ADD); | |
| expect(event.path, '/dir'); | |
| }); | |
| test('registering twice throws', () async { | |
| expect( | |
| () => registerCustomWatcher( | |
| memFsFactoryId, | |
| (_, {pollingDelay}) => throw UnimplementedError(), | |
| (_, {pollingDelay}) => throw UnimplementedError(), | |
| ), | |
| throwsA(isA<ArgumentError>()), | |
| ); | |
| }); | |
| test('finding two applicable factories throws', () async { | |
| // Note that _MemFsWatcherFactory always returns a watcher, so having two | |
| // will always produce a conflict. | |
| var watcherFactory = _MemFsWatcherFactory(memFs); | |
| registerCustomWatcher( | |
| 'Different id', | |
| watcherFactory.createDirectoryWatcher, | |
| watcherFactory.createFileWatcher, | |
| ); | |
| expect(() => FileWatcher('file.txt'), throwsA(isA<StateError>())); | |
| expect(() => DirectoryWatcher('/dir'), throwsA(isA<StateError>())); | |
| }); | |
| } | |
| class _MemFs { | |
| final _streams = <String, Set<StreamController<WatchEvent>>>{}; | |
| StreamController<WatchEvent> watchStream(String path) { | |
| var controller = StreamController<WatchEvent>(); | |
| _streams | |
| .putIfAbsent(path, () => <StreamController<WatchEvent>>{}) | |
| .add(controller); | |
| return controller; | |
| } | |
| void add(String path) { | |
| var controllers = _streams[path]; | |
| if (controllers != null) { | |
| for (var controller in controllers) { | |
| controller.add(WatchEvent(ChangeType.ADD, path)); | |
| } | |
| } | |
| } | |
| void remove(String path) { | |
| var controllers = _streams[path]; | |
| if (controllers != null) { | |
| for (var controller in controllers) { | |
| controller.add(WatchEvent(ChangeType.REMOVE, path)); | |
| } | |
| } | |
| } | |
| } | |
| class _MemFsWatcher implements FileWatcher, DirectoryWatcher, Watcher { | |
| final String _path; | |
| final StreamController<WatchEvent> _controller; | |
| _MemFsWatcher(this._path, this._controller); | |
| @override | |
| String get path => _path; | |
| @override | |
| String get directory => throw UnsupportedError('directory is not supported'); | |
| @override | |
| Stream<WatchEvent> get events => _controller.stream; | |
| @override | |
| bool get isReady => true; | |
| @override | |
| Future<void> get ready async {} | |
| } | |
| class _MemFsWatcherFactory { | |
| final _MemFs _memFs; | |
| _MemFsWatcherFactory(this._memFs); | |
| DirectoryWatcher? createDirectoryWatcher( | |
| String path, { | |
| Duration? pollingDelay, | |
| }) => _MemFsWatcher(path, _memFs.watchStream(path)); | |
| FileWatcher? createFileWatcher(String path, {Duration? pollingDelay}) => | |
| _MemFsWatcher(path, _memFs.watchStream(path)); | |
| } | |
| class _NoOpWatcherFactory { | |
| DirectoryWatcher? createDirectoryWatcher( | |
| String path, { | |
| Duration? pollingDelay, | |
| }) => null; | |
| FileWatcher? createFileWatcher(String path, {Duration? pollingDelay}) => null; | |
| } |