Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / test / data / extensions / api_test / file_browser / file_watcher_test / test.js
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.
4
5 /**
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.
10 */
11
12 /**
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.
20  *
21  * @constructor
22  */
23 function TestEventListener() {
24   /**
25    * Maps expectedEvent.entry.toURL() ->
26    *     {expectedEvent.eventType, expectedEvent.changeType}
27    *
28    * Set of events that are expected to be triggered during the test. Each
29    * object property represents one expected event.
30    *
31    * @type {Object.<string, Object>}
32    * @private
33    */
34   this.expectedEvents_ = {};
35
36   /**
37    * List of fileManagerPrivate.onDirectoryChanged events received before file
38    * system operation was done.
39    *
40    * @type {Array.<Object>}
41    * @private
42    */
43   this.eventQueue_ = [];
44
45   /**
46    * Whether the test listener is done. When set, all further |onSuccess_| and
47    * |onError| calls are ignored.
48    *
49    * @type {boolean}
50    * @private
51    */
52   this.done_ = false;
53
54   /**
55    * An entry returned by the test file system operation.
56    *
57    * @type {Entry}
58    * @private
59    */
60   this.receivedEntry_ = null;
61
62   /**
63    * The listener to the fileManagerPrivate.onDirectoryChanged.
64    *
65    * @type {function(Object)}
66    * @private
67    */
68   this.eventListener_ = this.onDirectoryChanged_.bind(this);
69 }
70
71 TestEventListener.prototype = {
72   /**
73    * Starts listening for the onDirectoryChanged events.
74    */
75   start: function() {
76     chrome.fileManagerPrivate.onDirectoryChanged.addListener(
77         this.eventListener_);
78   },
79
80   /**
81    * Adds expectation for an event that should be encountered during the test.
82    *
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].
87    */
88   addExpectedEvent: function(entry, eventType, changeType) {
89     this.expectedEvents_[entry.toURL()] = {
90         eventType: eventType,
91         changeType: changeType,
92     };
93   },
94
95   /**
96    * Called by a test when the file system operation performed in the test
97    * succeeds.
98    *
99    * @param {Entry} entry The entry returned by the file system operation.
100    */
101   onFileSystemOperation: function(entry) {
102     this.receivedEntry_ = entry;
103     this.eventQueue_.forEach(function(event) {
104       this.verifyReceivedEvent_(event);
105     }.bind(this));
106   },
107
108   /**
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.
111    *
112    * @param {string} message An error message.
113    */
114   onError: function(message) {
115     if (this.done_)
116       return;
117     this.done_ = true;
118
119     chrome.fileManagerPrivate.onDirectoryChanged.removeListener(
120         this.eventListener_);
121     chrome.test.fail(message);
122   },
123
124   /**
125    * Called when the test succeeds. Does cleanup and calls chrome.test.succeed.
126    * Further |onError| and |onSuccess| calls will be ignored.
127    *
128    * @private
129    */
130   onSuccess_: function() {
131     if (this.done_)
132       return;
133     this.done_ = true;
134
135     chrome.fileManagerPrivate.onDirectoryChanged.removeListener(
136         this.eventListener_);
137     chrome.test.succeed();
138   },
139
140   /**
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.
145    *
146    * @param {Object} event chrome.fileManagerPrivate.onDirectoryChanged event.
147    * @private
148    */
149   onDirectoryChanged_: function(event) {
150     if (this.receivedEntry_) {
151       this.verifyReceivedEvent_(event);
152     } else {
153       this.eventQueue_.push(event);
154     }
155   },
156
157   /**
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.
163    *
164    * @param {Object} event chrome.fileManagerPrivate.onDirectoryChanged event.
165    * @private
166    */
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);
172       return;
173     }
174
175     delete this.expectedEvents_[entryURL];
176
177     if (expectedEvent.eventType != event.eventType) {
178       this.onError('Unexpected event type for directory Url: ' +
179                    entryURL + '.\n' +
180                    'Expected "' + expectedEvent.eventType + '"\n' +
181                    'Got: "' + event.eventType + '"');
182       return;
183     }
184
185     if (Object.keys(this.expectedEvents_).length == 0)
186       this.onSuccess_();
187   }
188 }
189
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;
194 }
195
196 /**
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.
201  *
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
205  *    valid.
206  */
207 function initTests(callback) {
208   var testParams = {
209     /**
210      * Whether the test parameters are valid.
211      * @type {boolean}
212      */
213     valid: false,
214     /**
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.
217      * @type {boolean}}
218      */
219     isOnDrive: false,
220     /**
221      * Set of entries that are being watched during the tests.
222      * @type {Object.<Entry>}
223      */
224     entries: {},
225     /**
226      * File system for the testing volume.
227      * @type {DOMFileSystem}
228      */
229     fileSystem: null
230   };
231
232   chrome.fileManagerPrivate.getVolumeMetadataList(function(volumeMetadataList) {
233     var possibleVolumeTypes = ['testing', 'drive'];
234
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);
240     });
241
242     if (sortedVolumeMetadataList.length == 0) {
243       callback(
244           testParams, 'No volumes available, which could be used for testing.');
245       return;
246     }
247
248     chrome.fileManagerPrivate.requestFileSystem(
249         sortedVolumeMetadataList[0].volumeId,
250         function(fileSystem) {
251           if (!fileSystem) {
252             callback(testParams, 'Failed to acquire the testing volume.');
253             return;
254           }
255
256           testParams.fileSystem = fileSystem;
257           testParams.isOnDrive =
258               sortedVolumeMetadataList[0].volumeType == 'drive';
259
260           var testWatchEntries = [
261             {name: 'file',
262              path: getPath('test_dir/test_file.xul', testParams.isOnDrive),
263              type: 'file'},
264             {name: 'dir', path: getPath('test_dir/', testParams.isOnDrive),
265              type: 'dir'},
266             {name: 'subdir',
267              path: getPath('test_dir/subdir', testParams.isOnDrive),
268              type: 'dir'},
269           ];
270
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.');
278               return;
279             }
280
281             var testEntry = testWatchEntries.shift();
282
283             var getFunction = null;
284             if (testEntry.type == 'file') {
285               getFunction = fileSystem.root.getFile.bind(fileSystem.root);
286             } else {
287               getFunction = fileSystem.root.getDirectory.bind(fileSystem.root);
288             }
289
290             getFunction(testEntry.path, {},
291                 function(entry) {
292                   testParams.entries[testEntry.name] = entry;
293                   getNextEntry();
294                 },
295                 callback.bind(null, testParams,
296                     'Unable to get entry: \'' + testEntry.path + '\'.'));
297           };
298
299           // Trigger getting the watched entries.
300           getNextEntry();
301         });
302   });
303 };
304
305 // Starts the test.
306 initTests(function(testParams, errorMessage) {
307   if (!testParams.valid) {
308     chrome.test.notifyFail('Failed to initialize tests: ' + errorMessage);
309     return;
310   }
311
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);
318           }));
319     },
320
321     function addSubdirWatch() {
322       chrome.fileManagerPrivate.addFileWatch(
323           testParams.entries.subdir.toURL(),
324           chrome.test.callbackPass(function(success) {
325             chrome.test.assertTrue(success);
326           }));
327     },
328
329     function addDirWatch() {
330       chrome.fileManagerPrivate.addFileWatch(
331           testParams.entries.dir.toURL(),
332           chrome.test.callbackPass(function(success) {
333             chrome.test.assertTrue(success);
334           }));
335     },
336
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,
342                                          'changed', 'added');
343       testEventListener.start();
344
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.'));
351     },
352
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,
358                                          'changed', 'added');
359       testEventListener.start();
360
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.'));
367     },
368
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');
375
376       testEventListener.start();
377
378       testParams.fileSystem.root.getFile(
379           getPath('test_dir/subdir/file', testParams.isOnDrive),
380           {},
381           function(entry) {
382             entry.moveTo(testParams.entries.subdir, 'renamed',
383                 testEventListener.onFileSystemOperation.bind(testEventListener),
384                 testEventListener.onError.bind(testEventListener,
385                                                'Failed to rename the file.'));
386           },
387           testEventListener.onError.bind(testEventListener,
388                                          'Failed to get file.'));
389     },
390
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();
398
399       testParams.fileSystem.root.getFile(
400           getPath('test_dir/subdir/renamed', testParams.isOnDrive), {},
401           function(entry) {
402             entry.remove(
403                 testEventListener.onFileSystemOperation.bind(testEventListener,
404                                                              entry),
405                 testEventListener.onError.bind(testEventListener,
406                                                'Failed to remove the file.'));
407           },
408           testEventListener.onError.bind(testEventListener,
409                                          'Failed to get the file.'));
410     },
411
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');
423       }
424       testEventListener.start();
425
426       testParams.fileSystem.root.getFile(
427           getPath('test_dir/test_file.xul', testParams.isOnDrive), {},
428           function(entry) {
429             entry.remove(
430                 testEventListener.onFileSystemOperation.bind(testEventListener,
431                                                              entry),
432                 testEventListener.onError.bind(testEventListener,
433                                                'Failed to remove the file.'));
434           },
435           testEventListener.onError.bind(testEventListener,
436                                          'Failed to get the file.'));
437     },
438
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();
446
447       testParams.fileSystem.root.getDirectory(
448           getPath('test_dir/subdir/subsubdir', testParams.isOnDrive), {},
449           function(entry) {
450             entry.removeRecursively(
451                 testEventListener.onFileSystemOperation.bind(testEventListener,
452                                                              entry),
453                 testEventListener.onError.bind(testEventListener,
454                                                'Failed to remove the dir.'));
455           },
456           testEventListener.onError.bind(testEventListener,
457                                          'Failed to get the dir.'));
458     },
459
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');
469       }
470       testEventListener.addExpectedEvent(testParams.entries.dir,
471                                          'changed', 'deleted');
472       testEventListener.start();
473
474       testParams.fileSystem.root.getDirectory(
475           getPath('test_dir/subdir', testParams.isOnDrive), {},
476           function(entry) {
477             entry.removeRecursively(
478                 testEventListener.onFileSystemOperation.bind(testEventListener,
479                                                              entry),
480                 testEventListener.onError.bind(testEventListener,
481                                                'Failed to remove the dir.'));
482           },
483           testEventListener.onError.bind(testEventListener,
484                                          'Failed to get the dir.'));
485     },
486
487     function removeFileWatch() {
488       chrome.fileManagerPrivate.removeFileWatch(
489           testParams.entries.file.toURL(),
490           chrome.test.callbackPass(function(success) {
491             chrome.test.assertTrue(success);
492           }));
493     },
494
495     function removeDirWatch() {
496       chrome.fileManagerPrivate.removeFileWatch(
497           testParams.entries.dir.toURL(),
498           chrome.test.callbackPass(function(success) {
499             chrome.test.assertTrue(success);
500           }));
501     }
502
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
505     // during shutdown).
506   ]);
507 });