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

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

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

Powered by Google App Engine
This is Rietveld 408576698