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 |