Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(214)

Side by Side Diff: pkg/watcher/test/utils.dart

Issue 812253002: Delete a bunch of packages that are now on GitHub. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Un-delete http Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/watcher/test/ready/shared.dart ('k') | pkg/yaml/CHANGELOG.md » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library watcher.test.utils;
6
7 import 'dart:io';
8
9 import 'package:path/path.dart' as p;
10 import 'package:scheduled_test/scheduled_stream.dart';
11 import 'package:scheduled_test/scheduled_test.dart';
12 import 'package:unittest/compact_vm_config.dart';
13 import 'package:watcher/watcher.dart';
14 import 'package:watcher/src/stat.dart';
15 import 'package:watcher/src/utils.dart';
16
17 // TODO(nweiz): remove this when issue 15042 is fixed.
18 import 'package:watcher/src/directory_watcher/mac_os.dart';
19
20 /// The path to the temporary sandbox created for each test. All file
21 /// operations are implicitly relative to this directory.
22 String _sandboxDir;
23
24 /// The [DirectoryWatcher] being used for the current scheduled test.
25 DirectoryWatcher _watcher;
26
27 /// The mock modification times (in milliseconds since epoch) for each file.
28 ///
29 /// The actual file system has pretty coarse granularity for file modification
30 /// times. This means using the real file system requires us to put delays in
31 /// the tests to ensure we wait long enough between operations for the mod time
32 /// to be different.
33 ///
34 /// Instead, we'll just mock that out. Each time a file is written, we manually
35 /// increment the mod time for that file instantly.
36 Map<String, int> _mockFileModificationTimes;
37
38 typedef DirectoryWatcher WatcherFactory(String directory);
39
40 /// Sets the function used to create the directory watcher.
41 set watcherFactory(WatcherFactory factory) {
42 _watcherFactory = factory;
43 }
44 WatcherFactory _watcherFactory;
45
46 void initConfig() {
47 useCompactVMConfiguration();
48 filterStacks = true;
49 }
50
51 /// Creates the sandbox directory the other functions in this library use and
52 /// ensures it's deleted when the test ends.
53 ///
54 /// This should usually be called by [setUp].
55 void createSandbox() {
56 var dir = Directory.systemTemp.createTempSync('watcher_test_');
57 _sandboxDir = dir.path;
58
59 _mockFileModificationTimes = new Map<String, int>();
60 mockGetModificationTime((path) {
61 path = p.normalize(p.relative(path, from: _sandboxDir));
62
63 // Make sure we got a path in the sandbox.
64 assert(p.isRelative(path) && !path.startsWith(".."));
65
66 var mtime = _mockFileModificationTimes[path];
67 return new DateTime.fromMillisecondsSinceEpoch(mtime == null ? 0 : mtime);
68 });
69
70 // Delete the sandbox when done.
71 currentSchedule.onComplete.schedule(() {
72 if (_sandboxDir != null) {
73 // TODO(rnystrom): Issue 19155. The watcher should already be closed when
74 // we clean up the sandbox.
75 if (_watcherEvents != null) {
76 _watcherEvents.close();
77 }
78 new Directory(_sandboxDir).deleteSync(recursive: true);
79 _sandboxDir = null;
80 }
81
82 _mockFileModificationTimes = null;
83 mockGetModificationTime(null);
84 }, "delete sandbox");
85 }
86
87 /// Creates a new [DirectoryWatcher] that watches a temporary directory.
88 ///
89 /// Normally, this will pause the schedule until the watcher is done scanning
90 /// and is polling for changes. If you pass `false` for [waitForReady], it will
91 /// not schedule this delay.
92 ///
93 /// If [dir] is provided, watches a subdirectory in the sandbox with that name.
94 DirectoryWatcher createWatcher({String dir, bool waitForReady}) {
95 if (dir == null) {
96 dir = _sandboxDir;
97 } else {
98 dir = p.join(_sandboxDir, dir);
99 }
100
101 var watcher = _watcherFactory(dir);
102
103 // Wait until the scan is finished so that we don't miss changes to files
104 // that could occur before the scan completes.
105 if (waitForReady != false) {
106 schedule(() => watcher.ready, "wait for watcher to be ready");
107 }
108
109 return watcher;
110 }
111
112 /// The stream of events from the watcher started with [startWatcher].
113 ScheduledStream<WatchEvent> _watcherEvents;
114
115 /// Creates a new [DirectoryWatcher] that watches a temporary directory and
116 /// starts monitoring it for events.
117 ///
118 /// If [dir] is provided, watches a subdirectory in the sandbox with that name.
119 void startWatcher({String dir}) {
120 var testCase = currentTestCase.description;
121 if (MacOSDirectoryWatcher.logDebugInfo) {
122 print("starting watcher for $testCase (${new DateTime.now()})");
123 }
124
125 // We want to wait until we're ready *after* we subscribe to the watcher's
126 // events.
127 _watcher = createWatcher(dir: dir, waitForReady: false);
128
129 // Schedule [_watcher.events.listen] so that the watcher doesn't start
130 // watching [dir] before it exists. Expose [_watcherEvents] immediately so
131 // that it can be accessed synchronously after this.
132 _watcherEvents = new ScheduledStream(futureStream(schedule(() {
133 currentSchedule.onComplete.schedule(() {
134 if (MacOSDirectoryWatcher.logDebugInfo) {
135 print("stopping watcher for $testCase (${new DateTime.now()})");
136 }
137
138 _watcher = null;
139 if (!_closePending) _watcherEvents.close();
140
141 // If there are already errors, don't add this to the output and make
142 // people think it might be the root cause.
143 if (currentSchedule.errors.isEmpty) {
144 _watcherEvents.expect(isDone);
145 }
146 }, "reset watcher");
147
148 return _watcher.events;
149 }, "create watcher"), broadcast: true));
150
151 schedule(() => _watcher.ready, "wait for watcher to be ready");
152 }
153
154 /// Whether an event to close [_watcherEvents] has been scheduled.
155 bool _closePending = false;
156
157 /// Schedule closing the directory watcher stream after the event queue has been
158 /// pumped.
159 ///
160 /// This is necessary when events are allowed to occur, but don't have to occur,
161 /// at the end of a test. Otherwise, if they don't occur, the test will wait
162 /// indefinitely because they might in the future and because the watcher is
163 /// normally only closed after the test completes.
164 void startClosingEventStream() {
165 schedule(() {
166 _closePending = true;
167 pumpEventQueue().then((_) => _watcherEvents.close()).whenComplete(() {
168 _closePending = false;
169 });
170 }, 'start closing event stream');
171 }
172
173 /// A list of [StreamMatcher]s that have been collected using
174 /// [_collectStreamMatcher].
175 List<StreamMatcher> _collectedStreamMatchers;
176
177 /// Collects all stream matchers that are registered within [block] into a
178 /// single stream matcher.
179 ///
180 /// The returned matcher will match each of the collected matchers in order.
181 StreamMatcher _collectStreamMatcher(block()) {
182 var oldStreamMatchers = _collectedStreamMatchers;
183 _collectedStreamMatchers = new List<StreamMatcher>();
184 try {
185 block();
186 return inOrder(_collectedStreamMatchers);
187 } finally {
188 _collectedStreamMatchers = oldStreamMatchers;
189 }
190 }
191
192 /// Either add [streamMatcher] as an expectation to [_watcherEvents], or collect
193 /// it with [_collectStreamMatcher].
194 ///
195 /// [streamMatcher] can be a [StreamMatcher], a [Matcher], or a value.
196 void _expectOrCollect(streamMatcher) {
197 if (_collectedStreamMatchers != null) {
198 _collectedStreamMatchers.add(new StreamMatcher.wrap(streamMatcher));
199 } else {
200 _watcherEvents.expect(streamMatcher);
201 }
202 }
203
204 /// Expects that [matchers] will match emitted events in any order.
205 ///
206 /// [matchers] may be [Matcher]s or values, but not [StreamMatcher]s.
207 void inAnyOrder(Iterable matchers) {
208 matchers = matchers.toSet();
209 _expectOrCollect(nextValues(matchers.length, unorderedMatches(matchers)));
210 }
211
212 /// Expects that the expectations established in either [block1] or [block2]
213 /// will match the emitted events.
214 ///
215 /// If both blocks match, the one that consumed more events will be used.
216 void allowEither(block1(), block2()) {
217 _expectOrCollect(either(
218 _collectStreamMatcher(block1), _collectStreamMatcher(block2)));
219 }
220
221 /// Allows the expectations established in [block] to match the emitted events.
222 ///
223 /// If the expectations in [block] don't match, no error will be raised and no
224 /// events will be consumed. If this is used at the end of a test,
225 /// [startClosingEventStream] should be called before it.
226 void allowEvents(block()) {
227 _expectOrCollect(allow(_collectStreamMatcher(block)));
228 }
229
230 /// Returns a matcher that matches a [WatchEvent] with the given [type] and
231 /// [path].
232 Matcher isWatchEvent(ChangeType type, String path) {
233 return predicate((e) {
234 return e is WatchEvent && e.type == type &&
235 e.path == p.join(_sandboxDir, p.normalize(path));
236 }, "is $type $path");
237 }
238
239 /// Returns a [Matcher] that matches a [WatchEvent] for an add event for [path].
240 Matcher isAddEvent(String path) => isWatchEvent(ChangeType.ADD, path);
241
242 /// Returns a [Matcher] that matches a [WatchEvent] for a modification event for
243 /// [path].
244 Matcher isModifyEvent(String path) => isWatchEvent(ChangeType.MODIFY, path);
245
246 /// Returns a [Matcher] that matches a [WatchEvent] for a removal event for
247 /// [path].
248 Matcher isRemoveEvent(String path) => isWatchEvent(ChangeType.REMOVE, path);
249
250 /// Expects that the next event emitted will be for an add event for [path].
251 void expectAddEvent(String path) =>
252 _expectOrCollect(isWatchEvent(ChangeType.ADD, path));
253
254 /// Expects that the next event emitted will be for a modification event for
255 /// [path].
256 void expectModifyEvent(String path) =>
257 _expectOrCollect(isWatchEvent(ChangeType.MODIFY, path));
258
259 /// Expects that the next event emitted will be for a removal event for [path].
260 void expectRemoveEvent(String path) =>
261 _expectOrCollect(isWatchEvent(ChangeType.REMOVE, path));
262
263 /// Consumes an add event for [path] if one is emitted at this point in the
264 /// schedule, but doesn't throw an error if it isn't.
265 ///
266 /// If this is used at the end of a test, [startClosingEventStream] should be
267 /// called before it.
268 void allowAddEvent(String path) =>
269 _expectOrCollect(allow(isWatchEvent(ChangeType.ADD, path)));
270
271 /// Consumes a modification event for [path] if one is emitted at this point in
272 /// the schedule, but doesn't throw an error if it isn't.
273 ///
274 /// If this is used at the end of a test, [startClosingEventStream] should be
275 /// called before it.
276 void allowModifyEvent(String path) =>
277 _expectOrCollect(allow(isWatchEvent(ChangeType.MODIFY, path)));
278
279 /// Consumes a removal event for [path] if one is emitted at this point in the
280 /// schedule, but doesn't throw an error if it isn't.
281 ///
282 /// If this is used at the end of a test, [startClosingEventStream] should be
283 /// called before it.
284 void allowRemoveEvent(String path) =>
285 _expectOrCollect(allow(isWatchEvent(ChangeType.REMOVE, path)));
286
287 /// Schedules writing a file in the sandbox at [path] with [contents].
288 ///
289 /// If [contents] is omitted, creates an empty file. If [updatedModified] is
290 /// `false`, the mock file modification time is not changed.
291 void writeFile(String path, {String contents, bool updateModified}) {
292 if (contents == null) contents = "";
293 if (updateModified == null) updateModified = true;
294
295 schedule(() {
296 var fullPath = p.join(_sandboxDir, path);
297
298 // Create any needed subdirectories.
299 var dir = new Directory(p.dirname(fullPath));
300 if (!dir.existsSync()) {
301 dir.createSync(recursive: true);
302 }
303
304 if (MacOSDirectoryWatcher.logDebugInfo) {
305 print("[test] writing file $path");
306 }
307 new File(fullPath).writeAsStringSync(contents);
308
309 // Manually update the mock modification time for the file.
310 if (updateModified) {
311 // Make sure we always use the same separator on Windows.
312 path = p.normalize(path);
313
314 var milliseconds = _mockFileModificationTimes.putIfAbsent(path, () => 0);
315 _mockFileModificationTimes[path]++;
316 }
317 }, "write file $path");
318 }
319
320 /// Schedules deleting a file in the sandbox at [path].
321 void deleteFile(String path) {
322 schedule(() {
323 if (MacOSDirectoryWatcher.logDebugInfo) {
324 print("[test] deleting file $path");
325 }
326 new File(p.join(_sandboxDir, path)).deleteSync();
327 }, "delete file $path");
328 }
329
330 /// Schedules renaming a file in the sandbox from [from] to [to].
331 ///
332 /// If [contents] is omitted, creates an empty file.
333 void renameFile(String from, String to) {
334 schedule(() {
335 if (MacOSDirectoryWatcher.logDebugInfo) {
336 print("[test] renaming file $from to $to");
337 }
338
339 new File(p.join(_sandboxDir, from)).renameSync(p.join(_sandboxDir, to));
340
341 // Make sure we always use the same separator on Windows.
342 to = p.normalize(to);
343
344 // Manually update the mock modification time for the file.
345 var milliseconds = _mockFileModificationTimes.putIfAbsent(to, () => 0);
346 _mockFileModificationTimes[to]++;
347 }, "rename file $from to $to");
348 }
349
350 /// Schedules creating a directory in the sandbox at [path].
351 void createDir(String path) {
352 schedule(() {
353 if (MacOSDirectoryWatcher.logDebugInfo) {
354 print("[test] creating directory $path");
355 }
356 new Directory(p.join(_sandboxDir, path)).createSync();
357 }, "create directory $path");
358 }
359
360 /// Schedules renaming a directory in the sandbox from [from] to [to].
361 void renameDir(String from, String to) {
362 schedule(() {
363 if (MacOSDirectoryWatcher.logDebugInfo) {
364 print("[test] renaming directory $from to $to");
365 }
366 new Directory(p.join(_sandboxDir, from))
367 .renameSync(p.join(_sandboxDir, to));
368 }, "rename directory $from to $to");
369 }
370
371 /// Schedules deleting a directory in the sandbox at [path].
372 void deleteDir(String path) {
373 schedule(() {
374 if (MacOSDirectoryWatcher.logDebugInfo) {
375 print("[test] deleting directory $path");
376 }
377 new Directory(p.join(_sandboxDir, path)).deleteSync(recursive: true);
378 }, "delete directory $path");
379 }
380
381 /// Runs [callback] with every permutation of non-negative [i], [j], and [k]
382 /// less than [limit].
383 ///
384 /// Returns a set of all values returns by [callback].
385 ///
386 /// [limit] defaults to 3.
387 Set withPermutations(callback(int i, int j, int k), {int limit}) {
388 if (limit == null) limit = 3;
389 var results = new Set();
390 for (var i = 0; i < limit; i++) {
391 for (var j = 0; j < limit; j++) {
392 for (var k = 0; k < limit; k++) {
393 results.add(callback(i, j, k));
394 }
395 }
396 }
397 return results;
398 }
OLDNEW
« no previous file with comments | « pkg/watcher/test/ready/shared.dart ('k') | pkg/yaml/CHANGELOG.md » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698