Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / test / data / extensions / api_test / file_manager_browsertest / background.js
1 // Copyright (c) 2014 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 'use strict';
6
7 /**
8  * Extension ID of Files.app.
9  * @type {string}
10  * @const
11  */
12 var FILE_MANAGER_EXTENSIONS_ID = 'hhaomjibdihmijegdhdafkllkbggdgoj';
13
14 /**
15  * Calls a remote test util in Files.app's extension. See: test_util.js.
16  *
17  * @param {string} func Function name.
18  * @param {?string} appId Target window's App ID or null for functions
19  *     not requiring a window.
20  * @param {Array.<*>} args Array of arguments.
21  * @param {function(*)=} opt_callback Callback handling the function's result.
22  * @return {Promise} Promise to be fulfilled with the result of the remote
23  *     utility.
24  */
25 function callRemoteTestUtil(func, appId, args, opt_callback) {
26   return new Promise(function(onFulfilled) {
27     chrome.runtime.sendMessage(
28         FILE_MANAGER_EXTENSIONS_ID, {
29           func: func,
30           appId: appId,
31           args: args
32         },
33         function() {
34           if (opt_callback)
35             opt_callback.apply(null, arguments);
36           onFulfilled(arguments[0]);
37         });
38   });
39 }
40
41 /**
42  * Returns promise to be fulfilled after the given milliseconds.
43  * @param {number} time Time in milliseconds.
44  */
45 function wait(time) {
46   return new Promise(function(callback) {
47     setTimeout(callback, time);
48   });
49 }
50
51 /**
52  * Interval milliseconds between checks of repeatUntil.
53  * @type {number}
54  * @const
55  */
56 var REPEAT_UNTIL_INTERVAL = 200;
57
58 /**
59  * Interval milliseconds between log output of repeatUntil.
60  * @type {number}
61  * @const
62  */
63 var LOG_INTERVAL = 3000;
64
65 /**
66  * Returns a pending marker. See also the repeatUntil function.
67  * @param {string} message Pending reason including %s, %d, or %j markers. %j
68  *     format an object as JSON.
69  * @param {Array.<*>} var_args Values to be assigined to %x markers.
70  * @return {Object} Object which returns true for the expression: obj instanceof
71  *     pending.
72  */
73 function pending(message, var_args) {
74   var index = 1;
75   var args = arguments;
76   var formattedMessage = message.replace(/%[sdj]/g, function(pattern) {
77     var arg = args[index++];
78     switch(pattern) {
79       case '%s': return String(arg);
80       case '%d': return Number(arg);
81       case '%j': return JSON.stringify(arg);
82       default: return pattern;
83     }
84   });
85   var pendingMarker = Object.create(pending.prototype);
86   pendingMarker.message = formattedMessage;
87   return pendingMarker;
88 };
89
90 /**
91  * Waits until the checkFunction returns a value but a pending marker.
92  * @param {function():*} checkFunction Function to check a condition. It can
93  *     return a pending marker created by a pending function.
94  * @return {Promise} Promise to be fulfilled with the return value of
95  *     checkFunction when the checkFunction reutrns a value but a pending
96  *     marker.
97  */
98 function repeatUntil(checkFunction) {
99   var logTime = Date.now() + LOG_INTERVAL;
100   var step = function() {
101     return checkFunction().then(function(result) {
102       if (result instanceof pending) {
103         if (Date.now() > logTime) {
104           console.log(result.message);
105           logTime += LOG_INTERVAL;
106         }
107         return wait(REPEAT_UNTIL_INTERVAL).then(step);
108       } else {
109         return result;
110       }
111     });
112   };
113   return step();
114 };
115
116 /**
117  * Waits until a window having the given ID prefix appears.
118  * @param {string} windowIdPrefix ID prefix of the requested window.
119  * @return {Promise} promise Promise to be fulfilled with a found window's ID.
120  */
121 function waitForWindow(windowIdPrefix) {
122   return repeatUntil(function() {
123     return callRemoteTestUtil('getWindows', null, []).then(function(windows) {
124       for (var id in windows) {
125         if (id.indexOf(windowIdPrefix) === 0)
126           return id;
127       }
128       return pending('Window with the prefix %s is not found.', windowIdPrefix);
129     });
130   });
131 }
132
133 /**
134  * Closes a window and waits until the window is closed.
135  *
136  * @param {string} windowId ID of the window to close.
137  * @return {Promise} promise Promise to be fulfilled with the result (true:
138  *     success, false: failed).
139  */
140 function closeWindowAndWait(windowId) {
141   // Closes the window.
142   return callRemoteTestUtil('closeWindow', null, [windowId]).then(
143       function(result) {
144         // Returns false when the closing is failed.
145         if (!result)
146           return false;
147
148         return repeatUntil(function() {
149           return callRemoteTestUtil('getWindows', null, []).then(
150               function(windows) {
151                 for (var id in windows) {
152                   if (id === windowId) {
153                     // Window is still available. Continues waiting.
154                     return pending('Window with the prefix %s is not found.',
155                                    windowId);
156                   }
157                 }
158                 // Window is not available. Closing is done successfully.
159                 return true;
160               }
161           );
162         });
163       }
164   );
165 }
166
167 /**
168  * Waits until the window turns to the given size.
169  * @param {string} windowId Target window ID.
170  * @param {number} width Requested width in pixels.
171  * @param {number} height Requested height in pixels.
172  */
173 function waitForWindowGeometry(windowId, width, height) {
174   return repeatUntil(function() {
175     return callRemoteTestUtil('getWindows', null, []).then(function(windows) {
176       if (!windows[windowId])
177         return pending('Window %s is not found.', windowId);
178       if (windows[windowId].innerWidth !== width ||
179           windows[windowId].innerHeight !== height) {
180         return pending('Expected window size is %j, but it is %j',
181                        {width: width, height: height},
182                        windows[windowId]);
183       }
184     });
185   });
186 }
187
188 /**
189  * Waits for the specified element appearing in the DOM.
190  * @param {string} windowId Target window ID.
191  * @param {string} query Query string for the element.
192  * @param {string=} opt_iframeQuery Query string for the iframe containing the
193  *     element.
194  * @return {Promise} Promise to be fulfilled when the element appears.
195  */
196 function waitForElement(windowId, query, opt_iframeQuery) {
197   return repeatUntil(function() {
198     return callRemoteTestUtil(
199         'queryAllElements',
200         windowId,
201         [query, opt_iframeQuery]
202     ).then(function(elements) {
203       if (elements.length > 0)
204         return elements[0];
205       else
206         return pending(
207             'Element %s (maybe in iframe %s) is not found.',
208             query,
209             opt_iframeQuery);
210     });
211   });
212 }
213
214 /**
215  * Waits for the specified element leaving from the DOM.
216  * @param {string} windowId Target window ID.
217  * @param {string} query Query string for the element.
218  * @param {string=} opt_iframeQuery Query string for the iframe containing the
219  *     element.
220  * @return {Promise} Promise to be fulfilled when the element is lost.
221  */
222 function waitForElementLost(windowId, query, opt_iframeQuery) {
223   return repeatUntil(function() {
224     return callRemoteTestUtil(
225         'queryAllElements',
226         windowId,
227         [query, opt_iframeQuery]
228     ).then(function(elements) {
229       if (elements.length > 0)
230         return pending('Elements %j is still exists.', elements);
231       return true;
232     });
233   });
234 }
235
236 /**
237 /**
238  * Waits for the file list turns to the given contents.
239  * @param {string} windowId Target window ID.
240  * @param {Array.<Array.<string>>} expected Expected contents of file list.
241  * @param {{orderCheck:boolean=, ignoreLastModifiedTime:boolean=}=} opt_options
242  *     Options of the comparison. If orderCheck is true, it also compares the
243  *     order of files. If ignoreLastModifiedTime is true, it compares the file
244  *     without its last modified time.
245  * @return {Promise} Promise to be fulfilled when the file list turns to the
246  *     given contents.
247  */
248 function waitForFiles(windowId, expected, opt_options) {
249   var options = opt_options || {};
250   return repeatUntil(function() {
251     return callRemoteTestUtil(
252         'getFileList', windowId, []).then(function(files) {
253       if (!options.orderCheck) {
254         files.sort();
255         expected.sort();
256       }
257       for (var i = 0; i < Math.min(files.length, expected.length); i++) {
258         if (options.ignoreFileSize) {
259           files[i][1] = '';
260           expected[i][1] = '';
261         }
262         if (options.ignoreLastModifiedTime) {
263           files[i][3] = '';
264           expected[i][3] = '';
265         }
266       }
267       if (!chrome.test.checkDeepEq(expected, files)) {
268         return pending('waitForFiles: expected: %j actual %j.',
269                        expected,
270                        files);
271       }
272     });
273   });
274 }
275
276 /**
277  * Waits until the number of files in the file list is changed from the given
278  * number.
279  * TODO(hirono): Remove the function.
280  *
281  * @param {string} windowId Target window ID.
282  * @param {number} lengthBefore Number of items visible before.
283  * @return {Promise} Promise to be fulfilled with the contents of files.
284  */
285 function waitForFileListChange(windowId, lengthBefore) {
286   return repeatUntil(function() {
287     return callRemoteTestUtil(
288         'getFileList', windowId, []).then(function(files) {
289       files.sort();
290       var notReadyRows = files.filter(function(row) {
291         return row.filter(function(cell) { return cell == '...'; }).length;
292       });
293       if (notReadyRows.length === 0 &&
294           files.length !== lengthBefore &&
295           files.length !== 0) {
296         return files;
297       } else {
298         return pending('The number of file is %d. Not changed.', lengthBefore);
299       }
300     });
301   });
302 };
303
304 /**
305  * Waits until the given taskId appears in the executed task list.
306  * @param {string} windowId Target window ID.
307  * @param {string} taskId Task ID to watch.
308  * @return {Promise} Promise to be fulfilled when the task appears in the
309  *     executed task list.
310  */
311 function waitUntilTaskExecutes(windowId, taskId) {
312   return repeatUntil(function() {
313     return callRemoteTestUtil('getExecutedTasks', windowId, []).
314         then(function(executedTasks) {
315           if (executedTasks.indexOf(taskId) === -1)
316             return pending('Executed task is %j', executedTasks);
317         });
318   });
319 }
320
321 /**
322  * Adds check of chrome.test to the end of the given promise.
323  * @param {Promise} promise Promise.
324  */
325 function testPromise(promise) {
326   promise.then(function() {
327     return new Promise(checkIfNoErrorsOccured);
328   }).then(chrome.test.callbackPass(function() {
329     // The callbacPass is necessary to avoid prematurely finishing tests.
330     // Don't put chrome.test.succeed() here to avoid doubled success log.
331   }), function(error) {
332     chrome.test.fail(error.stack || error);
333   });
334 };
335
336 /**
337  * Sends a fake key down event.
338  * @param {string} windowId Window ID.
339  * @param {string} query Query for the target element.
340  * @param {string} keyIdentifer Key identifier.
341  * @param {boolean} ctrlKey Control key flag.
342  * @return {Promise} Promise to be fulfilled or rejected depending on the
343  *     result.
344  */
345 function fakeKeyDown(windowId, query, keyIdentifer, ctrlKey) {
346   return new Promise(function(fulfill, reject) {
347     callRemoteTestUtil('fakeKeyDown',
348                        windowId,
349                        [query, keyIdentifer, ctrlKey],
350                        function(result) {
351                          if (result)
352                            fulfill();
353                          else
354                            reject(new Error('Fail to fake key down.'));
355                        });
356   });
357 }
358
359 /**
360  * Executes a sequence of test steps.
361  * @constructor
362  */
363 function StepsRunner() {
364   /**
365    * List of steps.
366    * @type {Array.<function>}
367    * @private
368    */
369   this.steps_ = [];
370 }
371
372 /**
373  * Creates a StepsRunner instance and runs the passed steps.
374  */
375 StepsRunner.run = function(steps) {
376   var stepsRunner = new StepsRunner();
377   stepsRunner.run_(steps);
378 };
379
380 StepsRunner.prototype = {
381   /**
382    * @return {function} The next closure.
383    */
384   get next() {
385     return this.steps_[0];
386   }
387 };
388
389 /**
390  * Runs a sequence of the added test steps.
391  * @type {Array.<function>} List of the sequential steps.
392  */
393 StepsRunner.prototype.run_ = function(steps) {
394   this.steps_ = steps.slice(0);
395
396   // An extra step which acts as an empty callback for optional asynchronous
397   // calls in the last provided step.
398   this.steps_.push(function() {});
399
400   this.steps_ = this.steps_.map(function(f) {
401     return chrome.test.callbackPass(function() {
402       this.steps_.shift();
403       f.apply(this, arguments);
404     }.bind(this));
405   }.bind(this));
406
407   this.next();
408 };
409
410 /**
411  * Adds the givin entries to the target volume(s).
412  * @param {Array.<string>} volumeNames Names of target volumes.
413  * @param {Array.<TestEntryInfo>} entries List of entries to be added.
414  * @param {function(boolean)} callback Callback function to be passed the result
415  *     of function. The argument is true on success.
416  */
417 function addEntries(volumeNames, entries, callback) {
418   if (volumeNames.length == 0) {
419     callback(true);
420     return;
421   }
422   chrome.test.sendMessage(JSON.stringify({
423     name: 'addEntries',
424     volume: volumeNames.shift(),
425     entries: entries
426   }), chrome.test.callbackPass(function(result) {
427     if (result == "onEntryAdded")
428       addEntries(volumeNames, entries, callback);
429     else
430       callback(false);
431   }));
432 };
433
434 /**
435  * @enum {string}
436  * @const
437  */
438 var EntryType = Object.freeze({
439   FILE: 'file',
440   DIRECTORY: 'directory'
441 });
442
443 /**
444  * @enum {string}
445  * @const
446  */
447 var SharedOption = Object.freeze({
448   NONE: 'none',
449   SHARED: 'shared'
450 });
451
452 /**
453  * @enum {string}
454  */
455 var RootPath = Object.seal({
456   DOWNLOADS: '/must-be-filled-in-test-setup',
457   DRIVE: '/must-be-filled-in-test-setup',
458 });
459
460 /**
461  * File system entry information for tests.
462  *
463  * @param {EntryType} type Entry type.
464  * @param {string} sourceFileName Source file name that provides file contents.
465  * @param {string} targetName Name of entry on the test file system.
466  * @param {string} mimeType Mime type.
467  * @param {SharedOption} sharedOption Shared option.
468  * @param {string} lastModifiedTime Last modified time as a text to be shown in
469  *     the last modified column.
470  * @param {string} nameText File name to be shown in the name column.
471  * @param {string} sizeText Size text to be shown in the size column.
472  * @param {string} typeText Type name to be shown in the type column.
473  * @constructor
474  */
475 function TestEntryInfo(type,
476                        sourceFileName,
477                        targetPath,
478                        mimeType,
479                        sharedOption,
480                        lastModifiedTime,
481                        nameText,
482                        sizeText,
483                        typeText) {
484   this.type = type;
485   this.sourceFileName = sourceFileName || '';
486   this.targetPath = targetPath;
487   this.mimeType = mimeType || '';
488   this.sharedOption = sharedOption;
489   this.lastModifiedTime = lastModifiedTime;
490   this.nameText = nameText;
491   this.sizeText = sizeText;
492   this.typeText = typeText;
493   Object.freeze(this);
494 };
495
496 TestEntryInfo.getExpectedRows = function(entries) {
497   return entries.map(function(entry) { return entry.getExpectedRow(); });
498 };
499
500 /**
501  * Obtains a expected row contents of the file in the file list.
502  */
503 TestEntryInfo.prototype.getExpectedRow = function() {
504   return [this.nameText, this.sizeText, this.typeText, this.lastModifiedTime];
505 };
506
507 /**
508  * Filesystem entries used by the test cases.
509  * @type {Object.<string, TestEntryInfo>}
510  * @const
511  */
512 var ENTRIES = {
513   hello: new TestEntryInfo(
514       EntryType.FILE, 'text.txt', 'hello.txt',
515       'text/plain', SharedOption.NONE, 'Sep 4, 1998 12:34 PM',
516       'hello.txt', '51 bytes', 'Plain text'),
517
518   world: new TestEntryInfo(
519       EntryType.FILE, 'video.ogv', 'world.ogv',
520       'text/plain', SharedOption.NONE, 'Jul 4, 2012 10:35 AM',
521       'world.ogv', '59 KB', 'OGG video'),
522
523   unsupported: new TestEntryInfo(
524       EntryType.FILE, 'random.bin', 'unsupported.foo',
525       'application/x-foo', SharedOption.NONE, 'Jul 4, 2012 10:36 AM',
526       'unsupported.foo', '8 KB', 'FOO file'),
527
528   desktop: new TestEntryInfo(
529       EntryType.FILE, 'image.png', 'My Desktop Background.png',
530       'text/plain', SharedOption.NONE, 'Jan 18, 2038 1:02 AM',
531       'My Desktop Background.png', '272 bytes', 'PNG image'),
532
533   beautiful: new TestEntryInfo(
534       EntryType.FILE, 'music.ogg', 'Beautiful Song.ogg',
535       'text/plain', SharedOption.NONE, 'Nov 12, 2086 12:00 PM',
536       'Beautiful Song.ogg', '14 KB', 'OGG audio'),
537
538   photos: new TestEntryInfo(
539       EntryType.DIRECTORY, null, 'photos',
540       null, SharedOption.NONE, 'Jan 1, 1980 11:59 PM',
541       'photos', '--', 'Folder'),
542
543   testDocument: new TestEntryInfo(
544       EntryType.FILE, null, 'Test Document',
545       'application/vnd.google-apps.document',
546       SharedOption.NONE, 'Apr 10, 2013 4:20 PM',
547       'Test Document.gdoc', '--', 'Google document'),
548
549   testSharedDocument: new TestEntryInfo(
550       EntryType.FILE, null, 'Test Shared Document',
551       'application/vnd.google-apps.document',
552       SharedOption.SHARED, 'Mar 20, 2013 10:40 PM',
553       'Test Shared Document.gdoc', '--', 'Google document'),
554
555   newlyAdded: new TestEntryInfo(
556       EntryType.FILE, 'music.ogg', 'newly added file.ogg',
557       'audio/ogg', SharedOption.NONE, 'Sep 4, 1998 12:00 AM',
558       'newly added file.ogg', '14 KB', 'OGG audio'),
559
560   directoryA: new TestEntryInfo(
561       EntryType.DIRECTORY, null, 'A',
562       null, SharedOption.NONE, 'Jan 1, 2000 1:00 AM',
563       'A', '--', 'Folder'),
564
565   directoryB: new TestEntryInfo(
566       EntryType.DIRECTORY, null, 'A/B',
567       null, SharedOption.NONE, 'Jan 1, 2000 1:00 AM',
568       'B', '--', 'Folder'),
569
570   directoryC: new TestEntryInfo(
571       EntryType.DIRECTORY, null, 'A/B/C',
572       null, SharedOption.NONE, 'Jan 1, 2000 1:00 AM',
573       'C', '--', 'Folder'),
574
575   zipArchive: new TestEntryInfo(
576       EntryType.FILE, 'archive.zip', 'archive.zip',
577       'application/x-zip', SharedOption.NONE, 'Jan 1, 2014 1:00 AM',
578       'archive.zip', '533 bytes', 'Zip archive')
579 };
580
581 /**
582  * Basic entry set for the local volume.
583  * @type {Array.<TestEntryInfo>}
584  * @const
585  */
586 var BASIC_LOCAL_ENTRY_SET = [
587   ENTRIES.hello,
588   ENTRIES.world,
589   ENTRIES.desktop,
590   ENTRIES.beautiful,
591   ENTRIES.photos
592 ];
593
594 /**
595  * Basic entry set for the drive volume.
596  *
597  * TODO(hirono): Add a case for an entry cached by FileCache. For testing
598  *               Drive, create more entries with Drive specific attributes.
599  *
600  * @type {Array.<TestEntryInfo>}
601  * @const
602  */
603 var BASIC_DRIVE_ENTRY_SET = [
604   ENTRIES.hello,
605   ENTRIES.world,
606   ENTRIES.desktop,
607   ENTRIES.beautiful,
608   ENTRIES.photos,
609   ENTRIES.unsupported,
610   ENTRIES.testDocument,
611   ENTRIES.testSharedDocument
612 ];
613
614 var NESTED_ENTRY_SET = [
615   ENTRIES.directoryA,
616   ENTRIES.directoryB,
617   ENTRIES.directoryC
618 ];
619
620 /**
621  * Expected files shown in "Recent". Directories (e.g. 'photos') are not in this
622  * list as they are not expected in "Recent".
623  *
624  * @type {Array.<TestEntryInfo>}
625  * @const
626  */
627 var RECENT_ENTRY_SET = [
628   ENTRIES.hello,
629   ENTRIES.world,
630   ENTRIES.desktop,
631   ENTRIES.beautiful,
632   ENTRIES.unsupported,
633   ENTRIES.testDocument,
634   ENTRIES.testSharedDocument
635 ];
636
637 /**
638  * Expected files shown in "Offline", which should have the files
639  * "available offline". Google Documents, Google Spreadsheets, and the files
640  * cached locally are "available offline".
641  *
642  * @type {Array.<TestEntryInfo>}
643  * @const
644  */
645 var OFFLINE_ENTRY_SET = [
646   ENTRIES.testDocument,
647   ENTRIES.testSharedDocument
648 ];
649
650 /**
651  * Expected files shown in "Shared with me", which should be the entries labeled
652  * with "shared-with-me".
653  *
654  * @type {Array.<TestEntryInfo>}
655  * @const
656  */
657 var SHARED_WITH_ME_ENTRY_SET = [
658   ENTRIES.testSharedDocument
659 ];
660
661 /**
662  * Opens a Files.app's main window.
663  *
664  * TODO(mtomasz): Pass a volumeId or an enum value instead of full paths.
665  *
666  * @param {Object} appState App state to be passed with on opening Files.app.
667  *     Can be null.
668  * @param {?string} initialRoot Root path to be used as a default current
669  *     directory during initialization. Can be null, for no default path.
670  * @param {function(string)} Callback with the app id.
671  * @return {Promise} Promise to be fulfilled after window creating.
672  */
673 function openNewWindow(appState, initialRoot, callback) {
674   var appId;
675
676   // TODO(mtomasz): Migrate from full paths to a pair of a volumeId and a
677   // relative path. To compose the URL communicate via messages with
678   // file_manager_browser_test.cc.
679   var processedAppState = appState || {};
680   if (initialRoot) {
681     processedAppState.currentDirectoryURL =
682         'filesystem:chrome-extension://' + FILE_MANAGER_EXTENSIONS_ID +
683         '/external' + initialRoot;
684   }
685
686   return callRemoteTestUtil('openMainWindow',
687                             null,
688                             [processedAppState],
689                             callback);
690 }
691
692 /**
693  * Opens a Files.app's main window and waits until it is initialized. Fills
694  * the window with initial files. Should be called for the first window only.
695  *
696  * TODO(hirono): Add parameters to specify the entry set to be prepared.
697  * TODO(mtomasz): Pass a volumeId or an enum value instead of full paths.
698  *
699  * @param {Object} appState App state to be passed with on opening Files.app.
700  *     Can be null.
701  * @param {?string} initialRoot Root path to be used as a default current
702  *     directory during initialization. Can be null, for no default path.
703  * @param {function(string, Array.<Array.<string>>)} Callback with the app id
704  *     and with the file list.
705  */
706 function setupAndWaitUntilReady(appState, initialRoot, callback) {
707   var appId;
708
709   StepsRunner.run([
710     function() {
711       openNewWindow(appState, initialRoot, this.next);
712     },
713     function(inAppId) {
714       appId = inAppId;
715       addEntries(['local'], BASIC_LOCAL_ENTRY_SET, this.next);
716     },
717     function(success) {
718       chrome.test.assertTrue(success);
719       addEntries(['drive'], BASIC_DRIVE_ENTRY_SET, this.next);
720     },
721     function(success) {
722       chrome.test.assertTrue(success);
723       waitForElement(appId, '#detail-table').then(this.next);
724     },
725     function(success) {
726       waitForFileListChange(appId, 0).then(this.next);
727     },
728     function(fileList) {
729       callback(appId, fileList);
730       this.next();
731     }
732   ]);
733 }
734
735 /**
736  * Verifies if there are no Javascript errors in any of the app windows.
737  * @param {function()} Completion callback.
738  */
739 function checkIfNoErrorsOccured(callback) {
740   callRemoteTestUtil('getErrorCount', null, [], function(count) {
741     chrome.test.assertEq(0, count, 'The error count is not 0.');
742     callback();
743   });
744 }
745
746 /**
747  * Returns the name of the given file list entry.
748  * @param {Array.<string>} file An entry in a file list.
749  * @return {string} Name of the file.
750  */
751 function getFileName(fileListEntry) {
752   return fileListEntry[0];
753 }
754
755 /**
756  * Returns the size of the given file list entry.
757  * @param {Array.<string>} An entry in a file list.
758  * @return {string} Size of the file.
759  */
760 function getFileSize(fileListEntry) {
761   return fileListEntry[1];
762 }
763
764 /**
765  * Returns the type of the given file list entry.
766  * @param {Array.<string>} An entry in a file list.
767  * @return {string} Type of the file.
768  */
769 function getFileType(fileListEntry) {
770   return fileListEntry[2];
771 }
772
773 /**
774  * Namespace for test cases.
775  */
776 var testcase = {};
777
778 // Ensure the test cases are loaded.
779 window.addEventListener('load', function() {
780   var steps = [
781     // Check for the guest mode.
782     function() {
783       chrome.test.sendMessage(
784           JSON.stringify({name: 'isInGuestMode'}), steps.shift());
785     },
786     // Obtain the test case name.
787     function(result) {
788       if (JSON.parse(result) != chrome.extension.inIncognitoContext)
789         return;
790       chrome.test.sendMessage(
791           JSON.stringify({name: 'getRootPaths'}), steps.shift());
792     },
793     // Obtain the root entry paths.
794     function(result) {
795       var roots = JSON.parse(result);
796       RootPath.DOWNLOADS = roots.downloads;
797       RootPath.DRIVE = roots.drive;
798       chrome.test.sendMessage(
799           JSON.stringify({name: 'getTestName'}), steps.shift());
800     },
801     // Run the test case.
802     function(testCaseName) {
803       if (!testcase[testCaseName]) {
804         chrome.test.runTests([function() {
805           chrome.test.fail(testCaseName + ' is not found.');
806         }]);
807         return;
808       }
809       chrome.test.runTests([testcase[testCaseName]]);
810     }
811   ];
812   steps.shift()();
813 });