1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * Test component extension that tests fileManagerPrivate file watch api.
7 * The extension adds file watch on set of entries and performs set of file
8 * system operations that should trigger onDirectoryChanged events for the
9 * watched entries. On file system operations is performed per a test function.
13 * Helper class to observe the events triggered during a file system operation
14 * performed during a single test function.
15 * The received events are verified against the list of expected events, but
16 * only after the file system operation is done. If an event is received before
17 * an operation is done, it is added to the event queue that will be verified
18 * after the operation. chrome.test.succeed is called when all the expected
19 * events are received and verified.
23 function TestEventListener() {
25 * Maps expectedEvent.entry.toURL() ->
26 * {expectedEvent.eventType, expectedEvent.changeType}
28 * Set of events that are expected to be triggered during the test. Each
29 * object property represents one expected event.
31 * @type {Object.<string, Object>}
34 this.expectedEvents_ = {};
37 * List of fileManagerPrivate.onDirectoryChanged events received before file
38 * system operation was done.
40 * @type {Array.<Object>}
43 this.eventQueue_ = [];
46 * Whether the test listener is done. When set, all further |onSuccess_| and
47 * |onError| calls are ignored.
55 * An entry returned by the test file system operation.
60 this.receivedEntry_ = null;
63 * The listener to the fileManagerPrivate.onDirectoryChanged.
65 * @type {function(Object)}
68 this.eventListener_ = this.onDirectoryChanged_.bind(this);
71 TestEventListener.prototype = {
73 * Starts listening for the onDirectoryChanged events.
76 chrome.fileManagerPrivate.onDirectoryChanged.addListener(
81 * Adds expectation for an event that should be encountered during the test.
83 * @param {Entry} entry The event's entry argument.
84 * @param {string} eventType The event't type.
85 * @param {string} changeType The change type for the entry specified in
86 * event.changedEntries[0].
88 addExpectedEvent: function(entry, eventType, changeType) {
89 this.expectedEvents_[entry.toURL()] = {
91 changeType: changeType,
96 * Called by a test when the file system operation performed in the test
99 * @param {Entry} entry The entry returned by the file system operation.
101 onFileSystemOperation: function(entry) {
102 this.receivedEntry_ = entry;
103 this.eventQueue_.forEach(function(event) {
104 this.verifyReceivedEvent_(event);
109 * Called when the test encounters an error. Does cleanup and ends the test
110 * with failure. Further |onError| and |onSuccess| calls will be ignored.
112 * @param {string} message An error message.
114 onError: function(message) {
119 chrome.fileManagerPrivate.onDirectoryChanged.removeListener(
120 this.eventListener_);
121 chrome.test.fail(message);
125 * Called when the test succeeds. Does cleanup and calls chrome.test.succeed.
126 * Further |onError| and |onSuccess| calls will be ignored.
130 onSuccess_: function() {
135 chrome.fileManagerPrivate.onDirectoryChanged.removeListener(
136 this.eventListener_);
137 chrome.test.succeed();
141 * onDirectoryChanged event listener.
142 * If the test file system operation is done, verifies the event, otherwise
143 * it adds the event to |eventQueue_|. The events from |eventQueue_| will be
144 * verified once the file system operation is done.
146 * @param {Object} event chrome.fileManagerPrivate.onDirectoryChanged event.
149 onDirectoryChanged_: function(event) {
150 if (this.receivedEntry_) {
151 this.verifyReceivedEvent_(event);
153 this.eventQueue_.push(event);
158 * Verifies a received event.
159 * It checks that there is an expected event for |event.entry.toURL()|.
160 * If there is, the event is removed from the set of expected events.
161 * It verifies that the recived event matches the expected event parameters.
162 * If the received event was the last expected event, onSuccess_ is called.
164 * @param {Object} event chrome.fileManagerPrivate.onDirectoryChanged event.
167 verifyReceivedEvent_: function(event) {
168 var entryURL = event.entry.toURL();
169 var expectedEvent = this.expectedEvents_[entryURL];
170 if (!expectedEvent) {
171 this.onError('Event with unexpected dir url: ' + entryURL);
175 delete this.expectedEvents_[entryURL];
177 if (expectedEvent.eventType != event.eventType) {
178 this.onError('Unexpected event type for directory Url: ' +
180 'Expected "' + expectedEvent.eventType + '"\n' +
181 'Got: "' + event.eventType + '"');
185 if (Object.keys(this.expectedEvents_).length == 0)
190 // Gets the path for operations. The path is relative to the mount point for
191 // local entries and relative to the "My Drive" root for Drive entries.
192 function getPath(relativePath, isOnDrive) {
193 return (isOnDrive ? 'root/' : '') + relativePath;
197 * Initializes test parameters:
198 * - Gets local file system.
199 * - Gets the test mount point.
200 * - Adds the entries that will be watched during the test.
202 * @param {function(Object, string)} callback The function called when the test
203 * parameters are initialized. Called with testParams object and an error
204 * message string. The error message should be ignored if testParams are
207 function initTests(callback) {
210 * Whether the test parameters are valid.
215 * TODO(tbarzic) : We should not need to have this. The watch api should
216 * have the same behavior for local and drive file system.
221 * Set of entries that are being watched during the tests.
222 * @type {Object.<Entry>}
226 * File system for the testing volume.
227 * @type {DOMFileSystem}
232 chrome.fileManagerPrivate.getVolumeMetadataList(function(volumeMetadataList) {
233 var possibleVolumeTypes = ['testing', 'drive'];
235 var sortedVolumeMetadataList = volumeMetadataList.filter(function(volume) {
236 return possibleVolumeTypes.indexOf(volume.volumeType) != -1;
237 }).sort(function(volumeA, volumeB) {
238 return possibleVolumeTypes.indexOf(volumeA.volumeType) >
239 possibleVolumeTypes.indexOf(volumeB.volumeType);
242 if (sortedVolumeMetadataList.length == 0) {
244 testParams, 'No volumes available, which could be used for testing.');
248 chrome.fileManagerPrivate.requestFileSystem(
249 sortedVolumeMetadataList[0].volumeId,
250 function(fileSystem) {
252 callback(testParams, 'Failed to acquire the testing volume.');
256 testParams.fileSystem = fileSystem;
257 testParams.isOnDrive =
258 sortedVolumeMetadataList[0].volumeType == 'drive';
260 var testWatchEntries = [
262 path: getPath('test_dir/test_file.xul', testParams.isOnDrive),
264 {name: 'dir', path: getPath('test_dir/', testParams.isOnDrive),
267 path: getPath('test_dir/subdir', testParams.isOnDrive),
271 // Gets the first entry in |testWatchEntries| list.
272 var getNextEntry = function() {
273 // If the list is empty, the test has been successfully
274 // initialized, so call callback.
275 if (testWatchEntries.length == 0) {
276 testParams.valid = true;
277 callback(testParams, 'Success.');
281 var testEntry = testWatchEntries.shift();
283 var getFunction = null;
284 if (testEntry.type == 'file') {
285 getFunction = fileSystem.root.getFile.bind(fileSystem.root);
287 getFunction = fileSystem.root.getDirectory.bind(fileSystem.root);
290 getFunction(testEntry.path, {},
292 testParams.entries[testEntry.name] = entry;
295 callback.bind(null, testParams,
296 'Unable to get entry: \'' + testEntry.path + '\'.'));
299 // Trigger getting the watched entries.
306 initTests(function(testParams, errorMessage) {
307 if (!testParams.valid) {
308 chrome.test.notifyFail('Failed to initialize tests: ' + errorMessage);
312 chrome.test.runTests([
313 function addFileWatch() {
314 chrome.fileManagerPrivate.addFileWatch(
315 testParams.entries.file.toURL(),
316 chrome.test.callbackPass(function(success) {
317 chrome.test.assertTrue(success);
321 function addSubdirWatch() {
322 chrome.fileManagerPrivate.addFileWatch(
323 testParams.entries.subdir.toURL(),
324 chrome.test.callbackPass(function(success) {
325 chrome.test.assertTrue(success);
329 function addDirWatch() {
330 chrome.fileManagerPrivate.addFileWatch(
331 testParams.entries.dir.toURL(),
332 chrome.test.callbackPass(function(success) {
333 chrome.test.assertTrue(success);
337 // Test that onDirectoryChanged is triggerred when a directory in a watched
338 // directory is created.
339 function onCreateDir() {
340 var testEventListener = new TestEventListener();
341 testEventListener.addExpectedEvent(testParams.entries.subdir,
343 testEventListener.start();
345 testParams.fileSystem.root.getDirectory(
346 getPath('test_dir/subdir/subsubdir', testParams.isOnDrive),
347 {create: true, exclusive: true},
348 testEventListener.onFileSystemOperation.bind(testEventListener),
349 testEventListener.onError.bind(testEventListener,
350 'Failed to create directory.'));
353 // Test that onDirectoryChanged is triggerred when a file in a watched
354 // directory is created.
355 function onCreateFile() {
356 var testEventListener = new TestEventListener();
357 testEventListener.addExpectedEvent(testParams.entries.subdir,
359 testEventListener.start();
361 testParams.fileSystem.root.getFile(
362 getPath('test_dir/subdir/file', testParams.isOnDrive),
363 {create: true, exclusive: true},
364 testEventListener.onFileSystemOperation.bind(testEventListener),
365 testEventListener.onError.bind(testEventListener,
366 'Failed to create file.'));
369 // Test that onDirectoryChanged is triggerred when a file in a watched
370 // directory is renamed.
371 function onFileUpdated() {
372 var testEventListener = new TestEventListener();
373 testEventListener.addExpectedEvent(testParams.entries.subdir,
374 'changed', 'updated');
376 testEventListener.start();
378 testParams.fileSystem.root.getFile(
379 getPath('test_dir/subdir/file', testParams.isOnDrive),
382 entry.moveTo(testParams.entries.subdir, 'renamed',
383 testEventListener.onFileSystemOperation.bind(testEventListener),
384 testEventListener.onError.bind(testEventListener,
385 'Failed to rename the file.'));
387 testEventListener.onError.bind(testEventListener,
388 'Failed to get file.'));
391 // Test that onDirectoryChanged is triggerred when a file in a watched
392 // directory is deleted.
393 function onDeleteFile() {
394 var testEventListener = new TestEventListener();
395 testEventListener.addExpectedEvent(testParams.entries.subdir,
396 'changed', 'deleted');
397 testEventListener.start();
399 testParams.fileSystem.root.getFile(
400 getPath('test_dir/subdir/renamed', testParams.isOnDrive), {},
403 testEventListener.onFileSystemOperation.bind(testEventListener,
405 testEventListener.onError.bind(testEventListener,
406 'Failed to remove the file.'));
408 testEventListener.onError.bind(testEventListener,
409 'Failed to get the file.'));
412 // Test that onDirectoryChanged is triggerred when a watched file in a
413 // watched directory is deleted.
414 // The behaviour is different for drive and local mount points. On drive,
415 // there will be no event for the watched file.
416 function onDeleteWatchedFile() {
417 var testEventListener = new TestEventListener();
418 testEventListener.addExpectedEvent(testParams.entries.dir,
419 'changed', 'deleted');
420 if (!testParams.isOnDrive) {
421 testEventListener.addExpectedEvent(testParams.entries.file,
422 'changed', 'deleted');
424 testEventListener.start();
426 testParams.fileSystem.root.getFile(
427 getPath('test_dir/test_file.xul', testParams.isOnDrive), {},
430 testEventListener.onFileSystemOperation.bind(testEventListener,
432 testEventListener.onError.bind(testEventListener,
433 'Failed to remove the file.'));
435 testEventListener.onError.bind(testEventListener,
436 'Failed to get the file.'));
439 // Test that onDirectoryChanged is triggerred when a directory in a
440 // watched directory is deleted.
441 function onDeleteDir() {
442 var testEventListener = new TestEventListener();
443 testEventListener.addExpectedEvent(testParams.entries.subdir,
444 'changed', 'deleted');
445 testEventListener.start();
447 testParams.fileSystem.root.getDirectory(
448 getPath('test_dir/subdir/subsubdir', testParams.isOnDrive), {},
450 entry.removeRecursively(
451 testEventListener.onFileSystemOperation.bind(testEventListener,
453 testEventListener.onError.bind(testEventListener,
454 'Failed to remove the dir.'));
456 testEventListener.onError.bind(testEventListener,
457 'Failed to get the dir.'));
460 // Test that onDirectoryChanged is triggerred when a watched directory in a
461 // watched directory is deleted.
462 // The behaviour is different for drive and local mount points. On drive,
463 // there will be no event for the deleted directory.
464 function onDeleteWatchedDir() {
465 var testEventListener = new TestEventListener();
466 if (!testParams.isOnDrive) {
467 testEventListener.addExpectedEvent(testParams.entries.subdir,
468 'changed', 'deleted');
470 testEventListener.addExpectedEvent(testParams.entries.dir,
471 'changed', 'deleted');
472 testEventListener.start();
474 testParams.fileSystem.root.getDirectory(
475 getPath('test_dir/subdir', testParams.isOnDrive), {},
477 entry.removeRecursively(
478 testEventListener.onFileSystemOperation.bind(testEventListener,
480 testEventListener.onError.bind(testEventListener,
481 'Failed to remove the dir.'));
483 testEventListener.onError.bind(testEventListener,
484 'Failed to get the dir.'));
487 function removeFileWatch() {
488 chrome.fileManagerPrivate.removeFileWatch(
489 testParams.entries.file.toURL(),
490 chrome.test.callbackPass(function(success) {
491 chrome.test.assertTrue(success);
495 function removeDirWatch() {
496 chrome.fileManagerPrivate.removeFileWatch(
497 testParams.entries.dir.toURL(),
498 chrome.test.callbackPass(function(success) {
499 chrome.test.assertTrue(success);
503 // The watch for subdir entry is intentionally not removed to simulate the
504 // case when File Manager does not remove it either (e.g. if it's opened