| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library watcher.test.utils; | 5 library watcher.test.utils; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io'; | 8 import 'dart:io'; |
| 9 | 9 |
| 10 import 'package:path/path.dart' as p; | 10 import 'package:path/path.dart' as p; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 _mockFileModificationTimes = null; | 69 _mockFileModificationTimes = null; |
| 70 mockGetModificationTime(null); | 70 mockGetModificationTime(null); |
| 71 }, "delete sandbox"); | 71 }, "delete sandbox"); |
| 72 } | 72 } |
| 73 | 73 |
| 74 /// Creates a new [DirectoryWatcher] that watches a temporary directory. | 74 /// Creates a new [DirectoryWatcher] that watches a temporary directory. |
| 75 /// | 75 /// |
| 76 /// Normally, this will pause the schedule until the watcher is done scanning | 76 /// Normally, this will pause the schedule until the watcher is done scanning |
| 77 /// and is polling for changes. If you pass `false` for [waitForReady], it will | 77 /// and is polling for changes. If you pass `false` for [waitForReady], it will |
| 78 /// not schedule this delay. | 78 /// not schedule this delay. |
| 79 DirectoryWatcher createWatcher({bool waitForReady}) { | 79 /// |
| 80 /// If [dir] is provided, watches a subdirectory in the sandbox with that name. |
| 81 DirectoryWatcher createWatcher({String dir, bool waitForReady}) { |
| 82 if (dir == null) { |
| 83 dir = _sandboxDir; |
| 84 } else { |
| 85 dir = p.join(_sandboxDir, dir); |
| 86 } |
| 87 |
| 80 // Use a short delay to make the tests run quickly. | 88 // Use a short delay to make the tests run quickly. |
| 81 _watcher = new DirectoryWatcher(_sandboxDir, | 89 _watcher = new DirectoryWatcher(dir, |
| 82 pollingDelay: new Duration(milliseconds: 100)); | 90 pollingDelay: new Duration(milliseconds: 100)); |
| 83 | 91 |
| 84 // Wait until the scan is finished so that we don't miss changes to files | 92 // Wait until the scan is finished so that we don't miss changes to files |
| 85 // that could occur before the scan completes. | 93 // that could occur before the scan completes. |
| 86 if (waitForReady != false) { | 94 if (waitForReady != false) { |
| 87 schedule(() => _watcher.ready, "wait for watcher to be ready"); | 95 schedule(() => _watcher.ready, "wait for watcher to be ready"); |
| 88 } | 96 } |
| 89 | 97 |
| 90 currentSchedule.onComplete.schedule(() { | 98 currentSchedule.onComplete.schedule(() { |
| 91 _nextEvent = 0; | 99 _nextEvent = 0; |
| 92 _watcher = null; | 100 _watcher = null; |
| 93 }, "reset watcher"); | 101 }, "reset watcher"); |
| 94 | 102 |
| 95 return _watcher; | 103 return _watcher; |
| 96 } | 104 } |
| 97 | 105 |
| 98 void expectEvent(ChangeType type, String path) { | 106 /// Expects that the next set of events will all be changes of [type] on |
| 99 // Immediately create the future. This ensures we don't register too late and | 107 /// [paths]. |
| 100 // drop the event before we receive it. | 108 /// |
| 101 var future = _watcher.events.elementAt(_nextEvent++).then((event) { | 109 /// Validates that events are delivered for all paths in [paths], but allows |
| 102 expect(event, new _ChangeMatcher(type, path)); | 110 /// them in any order. |
| 103 }); | 111 void expectEvents(ChangeType type, Iterable<String> paths) { |
| 112 var pathSet = paths.map((path) => p.join(_sandboxDir, path)).toSet(); |
| 104 | 113 |
| 105 // Make sure the schedule is watching it in case it fails. | 114 // Create an expectation for as many paths as we have. |
| 106 currentSchedule.wrapFuture(future); | 115 var futures = []; |
| 116 |
| 117 for (var i = 0; i < paths.length; i++) { |
| 118 // Immediately create the futures. This ensures we don't register too |
| 119 // late and drop the event before we receive it. |
| 120 var future = _watcher.events.elementAt(_nextEvent++).then((event) { |
| 121 expect(event.type, equals(type)); |
| 122 expect(pathSet, contains(event.path)); |
| 123 |
| 124 pathSet.remove(event.path); |
| 125 }); |
| 126 |
| 127 // Make sure the schedule is watching it in case it fails. |
| 128 currentSchedule.wrapFuture(future); |
| 129 |
| 130 futures.add(future); |
| 131 } |
| 107 | 132 |
| 108 // Schedule it so that later file modifications don't occur until after this | 133 // Schedule it so that later file modifications don't occur until after this |
| 109 // event is received. | 134 // event is received. |
| 110 schedule(() => future, "wait for $type event"); | 135 schedule(() => Future.wait(futures), |
| 136 "wait for $type events on ${paths.join(', ')}"); |
| 111 } | 137 } |
| 112 | 138 |
| 113 void expectAddEvent(String path) { | 139 void expectAddEvent(String path) => expectEvents(ChangeType.ADD, [path]); |
| 114 expectEvent(ChangeType.ADD, p.join(_sandboxDir, path)); | 140 void expectModifyEvent(String path) => expectEvents(ChangeType.MODIFY, [path]); |
| 115 } | 141 void expectRemoveEvent(String path) => expectEvents(ChangeType.REMOVE, [path]); |
| 116 | 142 |
| 117 void expectModifyEvent(String path) { | 143 void expectRemoveEvents(Iterable<String> paths) { |
| 118 expectEvent(ChangeType.MODIFY, p.join(_sandboxDir, path)); | 144 expectEvents(ChangeType.REMOVE, paths); |
| 119 } | |
| 120 | |
| 121 void expectRemoveEvent(String path) { | |
| 122 expectEvent(ChangeType.REMOVE, p.join(_sandboxDir, path)); | |
| 123 } | 145 } |
| 124 | 146 |
| 125 /// Schedules writing a file in the sandbox at [path] with [contents]. | 147 /// Schedules writing a file in the sandbox at [path] with [contents]. |
| 126 /// | 148 /// |
| 127 /// If [contents] is omitted, creates an empty file. If [updatedModified] is | 149 /// If [contents] is omitted, creates an empty file. If [updatedModified] is |
| 128 /// `false`, the mock file modification time is not changed. | 150 /// `false`, the mock file modification time is not changed. |
| 129 void writeFile(String path, {String contents, bool updateModified}) { | 151 void writeFile(String path, {String contents, bool updateModified}) { |
| 130 if (contents == null) contents = ""; | 152 if (contents == null) contents = ""; |
| 131 if (updateModified == null) updateModified = true; | 153 if (updateModified == null) updateModified = true; |
| 132 | 154 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 | 190 |
| 169 // Make sure we always use the same separator on Windows. | 191 // Make sure we always use the same separator on Windows. |
| 170 to = p.normalize(to); | 192 to = p.normalize(to); |
| 171 | 193 |
| 172 // Manually update the mock modification time for the file. | 194 // Manually update the mock modification time for the file. |
| 173 var milliseconds = _mockFileModificationTimes.putIfAbsent(to, () => 0); | 195 var milliseconds = _mockFileModificationTimes.putIfAbsent(to, () => 0); |
| 174 _mockFileModificationTimes[to]++; | 196 _mockFileModificationTimes[to]++; |
| 175 }, "rename file $from to $to"); | 197 }, "rename file $from to $to"); |
| 176 } | 198 } |
| 177 | 199 |
| 200 /// Schedules deleting a directory in the sandbox at [path]. |
| 201 void deleteDir(String path) { |
| 202 schedule(() { |
| 203 new Directory(p.join(_sandboxDir, path)).deleteSync(recursive: true); |
| 204 }, "delete directory $path"); |
| 205 } |
| 206 |
| 178 /// A [Matcher] for [WatchEvent]s. | 207 /// A [Matcher] for [WatchEvent]s. |
| 179 class _ChangeMatcher extends Matcher { | 208 class _ChangeMatcher extends Matcher { |
| 180 /// The expected change. | 209 /// The expected change. |
| 181 final ChangeType type; | 210 final ChangeType type; |
| 182 | 211 |
| 183 /// The expected path. | 212 /// The expected path. |
| 184 final String path; | 213 final String path; |
| 185 | 214 |
| 186 _ChangeMatcher(this.type, this.path); | 215 _ChangeMatcher(this.type, this.path); |
| 187 | 216 |
| 188 Description describe(Description description) { | 217 Description describe(Description description) { |
| 189 description.add("$type $path"); | 218 description.add("$type $path"); |
| 190 } | 219 } |
| 191 | 220 |
| 192 bool matches(item, Map matchState) => | 221 bool matches(item, Map matchState) => |
| 193 item is WatchEvent && | 222 item is WatchEvent && |
| 194 item.type == type && | 223 item.type == type && |
| 195 p.normalize(item.path) == p.normalize(path); | 224 p.normalize(item.path) == p.normalize(path); |
| 196 } | 225 } |
| OLD | NEW |