1 // Copyright 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.
8 * Sends a test message.
9 * @param {Object} message Message to be sent. It is converted into JSON string
11 * @return {Promise} Promise to be fulfilled with a returned value.
13 function sendTestMessage(message
) {
14 return new Promise(function(fulfill
) {
15 chrome
.test
.sendMessage(JSON
.stringify(message
), fulfill
);
20 * Returns promise to be fulfilled after the given milliseconds.
21 * @param {number} time Time in milliseconds.
24 return new Promise(function(callback
) {
25 setTimeout(callback
, time
);
30 * Verifies if there are no Javascript errors in any of the app windows.
31 * @param {function()} Completion callback.
33 function checkIfNoErrorsOccuredOnApp(app
, callback
) {
34 var countPromise
= app
.callRemoteTestUtil('getErrorCount', null, []);
35 countPromise
.then(function(count
) {
36 chrome
.test
.assertEq(0, count
, 'The error count is not 0.');
42 * Adds check of chrome.test to the end of the given promise.
43 * @param {Promise} promise Promise.
45 function testPromiseAndApps(promise
, apps
) {
46 promise
.then(function() {
48 apps
.map(function(app
) {
49 return new Promise(checkIfNoErrorsOccuredOnApp
.bind(null, app
));
51 }).then(chrome
.test
.callbackPass(function() {
52 // The callbacPass is necessary to avoid prematurely finishing tests.
53 // Don't put chrome.test.succeed() here to avoid doubled success log.
55 chrome
.test
.fail(error
.stack
|| error
);
60 * Interval milliseconds between checks of repeatUntil.
64 var REPEAT_UNTIL_INTERVAL
= 200;
67 * Interval milliseconds between log output of repeatUntil.
71 var LOG_INTERVAL
= 3000;
74 * Returns a pending marker. See also the repeatUntil function.
75 * @param {string} message Pending reason including %s, %d, or %j markers. %j
76 * format an object as JSON.
77 * @param {Array<*>} var_args Values to be assigined to %x markers.
78 * @return {Object} Object which returns true for the expression: obj instanceof
81 function pending(message
, var_args
) {
84 var formattedMessage
= message
.replace(/%[sdj]/g, function(pattern
) {
85 var arg
= args
[index
++];
87 case '%s': return String(arg
);
88 case '%d': return Number(arg
);
89 case '%j': return JSON
.stringify(arg
);
90 default: return pattern
;
93 var pendingMarker
= Object
.create(pending
.prototype);
94 pendingMarker
.message
= formattedMessage
;
99 * Waits until the checkFunction returns a value but a pending marker.
100 * @param {function():*} checkFunction Function to check a condition. It can
101 * return a pending marker created by a pending function.
102 * @return {Promise} Promise to be fulfilled with the return value of
103 * checkFunction when the checkFunction reutrns a value but a pending
106 function repeatUntil(checkFunction
) {
107 var logTime
= Date
.now() + LOG_INTERVAL
;
108 var step = function() {
109 return Promise
.resolve(checkFunction()).then(function(result
) {
110 if (result
instanceof pending
) {
111 if (Date
.now() > logTime
) {
112 console
.warn(result
.message
);
113 logTime
+= LOG_INTERVAL
;
115 return wait(REPEAT_UNTIL_INTERVAL
).then(step
);
125 * Adds the givin entries to the target volume(s).
126 * @param {Array<string>} volumeNames Names of target volumes.
127 * @param {Array<TestEntryInfo>} entries List of entries to be added.
128 * @param {function(boolean)=} opt_callback Callback function to be passed the
129 * result of function. The argument is true on success.
130 * @return {Promise} Promise to be fulfilled when the entries are added.
132 function addEntries(volumeNames
, entries
, opt_callback
) {
133 if (volumeNames
.length
== 0) {
137 var volumeResultPromises
= volumeNames
.map(function(volume
) {
138 return sendTestMessage({
144 var resultPromise
= Promise
.all(volumeResultPromises
);
146 resultPromise
.then(opt_callback
.bind(null, true),
147 opt_callback
.bind(null, false));
149 return resultPromise
;
156 var EntryType
= Object
.freeze({
158 DIRECTORY
: 'directory'
165 var SharedOption
= Object
.freeze({
173 var RootPath
= Object
.seal({
174 DOWNLOADS
: '/must-be-filled-in-test-setup',
175 DRIVE
: '/must-be-filled-in-test-setup',
179 * File system entry information for tests.
181 * @param {EntryType} type Entry type.
182 * @param {string} sourceFileName Source file name that provides file contents.
183 * @param {string} targetName Name of entry on the test file system.
184 * @param {string} mimeType Mime type.
185 * @param {SharedOption} sharedOption Shared option.
186 * @param {string} lastModifiedTime Last modified time as a text to be shown in
187 * the last modified column.
188 * @param {string} nameText File name to be shown in the name column.
189 * @param {string} sizeText Size text to be shown in the size column.
190 * @param {string} typeText Type name to be shown in the type column.
193 function TestEntryInfo(type
,
203 this.sourceFileName
= sourceFileName
|| '';
204 this.targetPath
= targetPath
;
205 this.mimeType
= mimeType
|| '';
206 this.sharedOption
= sharedOption
;
207 this.lastModifiedTime
= lastModifiedTime
;
208 this.nameText
= nameText
;
209 this.sizeText
= sizeText
;
210 this.typeText
= typeText
;
214 TestEntryInfo
.getExpectedRows = function(entries
) {
215 return entries
.map(function(entry
) { return entry
.getExpectedRow(); });
219 * Obtains a expected row contents of the file in the file list.
221 TestEntryInfo
.prototype.getExpectedRow = function() {
222 return [this.nameText
, this.sizeText
, this.typeText
, this.lastModifiedTime
];
226 * Filesystem entries used by the test cases.
227 * @type {Object<TestEntryInfo>}
231 hello
: new TestEntryInfo(
232 EntryType
.FILE
, 'text.txt', 'hello.txt',
233 'text/plain', SharedOption
.NONE
, 'Sep 4, 1998, 12:34 PM',
234 'hello.txt', '51 bytes', 'Plain text'),
236 world
: new TestEntryInfo(
237 EntryType
.FILE
, 'video.ogv', 'world.ogv',
238 'video/ogg', SharedOption
.NONE
, 'Jul 4, 2012, 10:35 AM',
239 'world.ogv', '59 KB', 'OGG video'),
241 unsupported
: new TestEntryInfo(
242 EntryType
.FILE
, 'random.bin', 'unsupported.foo',
243 'application/x-foo', SharedOption
.NONE
, 'Jul 4, 2012, 10:36 AM',
244 'unsupported.foo', '8 KB', 'FOO file'),
246 desktop
: new TestEntryInfo(
247 EntryType
.FILE
, 'image.png', 'My Desktop Background.png',
248 'image/png', SharedOption
.NONE
, 'Jan 18, 2038, 1:02 AM',
249 'My Desktop Background.png', '272 bytes', 'PNG image'),
251 // An image file without an extension, to confirm that file type detection
252 // using mime types works fine.
253 image2
: new TestEntryInfo(
254 EntryType
.FILE
, 'image2.png', 'image2',
255 'image/png', SharedOption
.NONE
, 'Jan 18, 2038, 1:02 AM',
256 'image2', '4 KB', 'PNG image'),
258 image3
: new TestEntryInfo(
259 EntryType
.FILE
, 'image3.jpg', 'image3.jpg',
260 'image/jpeg', SharedOption
.NONE
, 'Jan 18, 2038, 1:02 AM',
261 'image3.jpg', '3 KB', 'JPEG image'),
263 // An ogg file without a mime type, to confirm that file type detection using
264 // file extensions works fine.
265 beautiful
: new TestEntryInfo(
266 EntryType
.FILE
, 'music.ogg', 'Beautiful Song.ogg',
267 null, SharedOption
.NONE
, 'Nov 12, 2086, 12:00 PM',
268 'Beautiful Song.ogg', '14 KB', 'OGG audio'),
270 photos
: new TestEntryInfo(
271 EntryType
.DIRECTORY
, null, 'photos',
272 null, SharedOption
.NONE
, 'Jan 1, 1980, 11:59 PM',
273 'photos', '--', 'Folder'),
275 testDocument
: new TestEntryInfo(
276 EntryType
.FILE
, null, 'Test Document',
277 'application/vnd.google-apps.document',
278 SharedOption
.NONE
, 'Apr 10, 2013, 4:20 PM',
279 'Test Document.gdoc', '--', 'Google document'),
281 testSharedDocument
: new TestEntryInfo(
282 EntryType
.FILE
, null, 'Test Shared Document',
283 'application/vnd.google-apps.document',
284 SharedOption
.SHARED
, 'Mar 20, 2013, 10:40 PM',
285 'Test Shared Document.gdoc', '--', 'Google document'),
287 newlyAdded
: new TestEntryInfo(
288 EntryType
.FILE
, 'music.ogg', 'newly added file.ogg',
289 'audio/ogg', SharedOption
.NONE
, 'Sep 4, 1998, 12:00 AM',
290 'newly added file.ogg', '14 KB', 'OGG audio'),
292 directoryA
: new TestEntryInfo(
293 EntryType
.DIRECTORY
, null, 'A',
294 null, SharedOption
.NONE
, 'Jan 1, 2000, 1:00 AM',
295 'A', '--', 'Folder'),
297 directoryB
: new TestEntryInfo(
298 EntryType
.DIRECTORY
, null, 'A/B',
299 null, SharedOption
.NONE
, 'Jan 1, 2000, 1:00 AM',
300 'B', '--', 'Folder'),
302 directoryC
: new TestEntryInfo(
303 EntryType
.DIRECTORY
, null, 'A/B/C',
304 null, SharedOption
.NONE
, 'Jan 1, 2000, 1:00 AM',
305 'C', '--', 'Folder'),
307 directoryD
: new TestEntryInfo(
308 EntryType
.DIRECTORY
, null, 'D',
309 null, SharedOption
.NONE
, 'Jan 1, 2000, 1:00 AM',
310 'D', '--', 'Folder'),
312 directoryE
: new TestEntryInfo(
313 EntryType
.DIRECTORY
, null, 'D/E',
314 null, SharedOption
.NONE
, 'Jan 1, 2000, 1:00 AM',
315 'E', '--', 'Folder'),
317 directoryF
: new TestEntryInfo(
318 EntryType
.DIRECTORY
, null, 'D/E/F',
319 null, SharedOption
.NONE
, 'Jan 1, 2000, 1:00 AM',
320 'F', '--', 'Folder'),
322 zipArchive
: new TestEntryInfo(
323 EntryType
.FILE
, 'archive.zip', 'archive.zip',
324 'application/x-zip', SharedOption
.NONE
, 'Jan 1, 2014, 1:00 AM',
325 'archive.zip', '533 bytes', 'Zip archive')