|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 * http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 /** |
|
5 * Tests the DownloadImport object. |
|
6 */ |
|
7 |
|
8 "use strict"; |
|
9 |
|
10 //////////////////////////////////////////////////////////////////////////////// |
|
11 //// Globals |
|
12 |
|
13 XPCOMUtils.defineLazyModuleGetter(this, "Sqlite", |
|
14 "resource://gre/modules/Sqlite.jsm"); |
|
15 XPCOMUtils.defineLazyModuleGetter(this, "DownloadImport", |
|
16 "resource://gre/modules/DownloadImport.jsm"); |
|
17 |
|
18 // Importable states |
|
19 const DOWNLOAD_NOTSTARTED = -1; |
|
20 const DOWNLOAD_DOWNLOADING = 0; |
|
21 const DOWNLOAD_PAUSED = 4; |
|
22 const DOWNLOAD_QUEUED = 5; |
|
23 |
|
24 // Non importable states |
|
25 const DOWNLOAD_FAILED = 2; |
|
26 const DOWNLOAD_CANCELED = 3; |
|
27 const DOWNLOAD_BLOCKED_PARENTAL = 6; |
|
28 const DOWNLOAD_SCANNING = 7; |
|
29 const DOWNLOAD_DIRTY = 8; |
|
30 const DOWNLOAD_BLOCKED_POLICY = 9; |
|
31 |
|
32 // The TEST_DATA_TAINTED const is a version of TEST_DATA_SHORT in which the |
|
33 // beginning of the data was changed (with the TEST_DATA_REPLACEMENT value). |
|
34 // We use this to test that the entityID is properly imported and the download |
|
35 // can be resumed from where it was paused. |
|
36 // For simplification purposes, the test requires that TEST_DATA_SHORT and |
|
37 // TEST_DATA_TAINTED have the same length. |
|
38 const TEST_DATA_REPLACEMENT = "-changed- "; |
|
39 const TEST_DATA_TAINTED = TEST_DATA_REPLACEMENT + |
|
40 TEST_DATA_SHORT.substr(TEST_DATA_REPLACEMENT.length); |
|
41 const TEST_DATA_LENGTH = TEST_DATA_SHORT.length; |
|
42 |
|
43 // The length of the partial file that we'll write to disk as an existing |
|
44 // ongoing download. |
|
45 const TEST_DATA_PARTIAL_LENGTH = TEST_DATA_REPLACEMENT.length; |
|
46 |
|
47 // The value of the "maxBytes" column stored in the DB about the downloads. |
|
48 // It's intentionally different than TEST_DATA_LENGTH to test that each value |
|
49 // is seen when expected. |
|
50 const MAXBYTES_IN_DB = TEST_DATA_LENGTH - 10; |
|
51 |
|
52 let gDownloadsRowToImport; |
|
53 let gDownloadsRowNonImportable; |
|
54 |
|
55 /** |
|
56 * Creates a database with an empty moz_downloads table and leaves an |
|
57 * open connection to it. |
|
58 * |
|
59 * @param aPath |
|
60 * String containing the path of the database file to be created. |
|
61 * @param aSchemaVersion |
|
62 * Number with the version of the database schema to set. |
|
63 * |
|
64 * @return {Promise} |
|
65 * @resolves The open connection to the database. |
|
66 * @rejects If an error occurred during the database creation. |
|
67 */ |
|
68 function promiseEmptyDatabaseConnection({aPath, aSchemaVersion}) { |
|
69 return Task.spawn(function () { |
|
70 let connection = yield Sqlite.openConnection({ path: aPath }); |
|
71 |
|
72 yield connection.execute("CREATE TABLE moz_downloads (" |
|
73 + "id INTEGER PRIMARY KEY," |
|
74 + "name TEXT," |
|
75 + "source TEXT," |
|
76 + "target TEXT," |
|
77 + "tempPath TEXT," |
|
78 + "startTime INTEGER," |
|
79 + "endTime INTEGER," |
|
80 + "state INTEGER," |
|
81 + "referrer TEXT," |
|
82 + "entityID TEXT," |
|
83 + "currBytes INTEGER NOT NULL DEFAULT 0," |
|
84 + "maxBytes INTEGER NOT NULL DEFAULT -1," |
|
85 + "mimeType TEXT," |
|
86 + "preferredApplication TEXT," |
|
87 + "preferredAction INTEGER NOT NULL DEFAULT 0," |
|
88 + "autoResume INTEGER NOT NULL DEFAULT 0," |
|
89 + "guid TEXT)"); |
|
90 |
|
91 yield connection.setSchemaVersion(aSchemaVersion); |
|
92 |
|
93 throw new Task.Result(connection); |
|
94 }); |
|
95 } |
|
96 |
|
97 /** |
|
98 * Inserts a new entry in the database with the given columns' values. |
|
99 * |
|
100 * @param aConnection |
|
101 * The database connection. |
|
102 * @param aDownloadRow |
|
103 * An object representing the values for each column of the row |
|
104 * being inserted. |
|
105 * |
|
106 * @return {Promise} |
|
107 * @resolves When the operation completes. |
|
108 * @rejects If there's an error inserting the row. |
|
109 */ |
|
110 function promiseInsertRow(aConnection, aDownloadRow) { |
|
111 // We can't use the aDownloadRow obj directly in the execute statement |
|
112 // because the obj bind code in Sqlite.jsm doesn't allow objects |
|
113 // with extra properties beyond those being binded. So we might as well |
|
114 // use an array as it is simpler. |
|
115 let values = [ |
|
116 aDownloadRow.source, aDownloadRow.target, aDownloadRow.tempPath, |
|
117 aDownloadRow.startTime.getTime() * 1000, aDownloadRow.state, |
|
118 aDownloadRow.referrer, aDownloadRow.entityID, aDownloadRow.maxBytes, |
|
119 aDownloadRow.mimeType, aDownloadRow.preferredApplication, |
|
120 aDownloadRow.preferredAction, aDownloadRow.autoResume |
|
121 ]; |
|
122 |
|
123 return aConnection.execute("INSERT INTO moz_downloads (" |
|
124 + "name, source, target, tempPath, startTime," |
|
125 + "endTime, state, referrer, entityID, currBytes," |
|
126 + "maxBytes, mimeType, preferredApplication," |
|
127 + "preferredAction, autoResume, guid)" |
|
128 + "VALUES (" |
|
129 + "'', ?, ?, ?, ?, " //name, |
|
130 + "0, ?, ?, ?, 0, " //endTime, currBytes |
|
131 + " ?, ?, ?, " // |
|
132 + " ?, ?, '')", //and guid are not imported |
|
133 values); |
|
134 } |
|
135 |
|
136 /** |
|
137 * Retrieves the number of rows in the moz_downloads table of the |
|
138 * database. |
|
139 * |
|
140 * @param aConnection |
|
141 * The database connection. |
|
142 * |
|
143 * @return {Promise} |
|
144 * @resolves With the number of rows. |
|
145 * @rejects Never. |
|
146 */ |
|
147 function promiseTableCount(aConnection) { |
|
148 return aConnection.execute("SELECT COUNT(*) FROM moz_downloads") |
|
149 .then(res => res[0].getResultByName("COUNT(*)")) |
|
150 .then(null, Cu.reportError); |
|
151 } |
|
152 |
|
153 /** |
|
154 * Briefly opens a network channel to a given URL to retrieve |
|
155 * the entityID of this url, as generated by the network code. |
|
156 * |
|
157 * @param aUrl |
|
158 * The URL to retrieve the entityID. |
|
159 * |
|
160 * @return {Promise} |
|
161 * @resolves The EntityID of the given URL. |
|
162 * @rejects When there's a problem accessing the URL. |
|
163 */ |
|
164 function promiseEntityID(aUrl) { |
|
165 let deferred = Promise.defer(); |
|
166 let entityID = ""; |
|
167 let channel = NetUtil.newChannel(NetUtil.newURI(aUrl)); |
|
168 |
|
169 channel.asyncOpen({ |
|
170 onStartRequest: function (aRequest) { |
|
171 if (aRequest instanceof Ci.nsIResumableChannel) { |
|
172 entityID = aRequest.entityID; |
|
173 } |
|
174 aRequest.cancel(Cr.NS_BINDING_ABORTED); |
|
175 }, |
|
176 |
|
177 onStopRequest: function (aRequest, aContext, aStatusCode) { |
|
178 if (aStatusCode == Cr.NS_BINDING_ABORTED) { |
|
179 deferred.resolve(entityID); |
|
180 } else { |
|
181 deferred.reject("Unexpected status code received"); |
|
182 } |
|
183 }, |
|
184 |
|
185 onDataAvailable: function () {} |
|
186 }, null); |
|
187 |
|
188 return deferred.promise; |
|
189 } |
|
190 |
|
191 /** |
|
192 * Gets a file path to a temporary writeable download target, in the |
|
193 * correct format as expected to be stored in the downloads database, |
|
194 * which is file:///absolute/path/to/file |
|
195 * |
|
196 * @param aLeafName |
|
197 * A hint leaf name for the file. |
|
198 * |
|
199 * @return String The path to the download target. |
|
200 */ |
|
201 function getDownloadTarget(aLeafName) { |
|
202 return NetUtil.newURI(getTempFile(aLeafName)).spec; |
|
203 } |
|
204 |
|
205 /** |
|
206 * Generates a temporary partial file to use as an in-progress |
|
207 * download. The file is written to disk with a part of the total expected |
|
208 * download content pre-written. |
|
209 * |
|
210 * @param aLeafName |
|
211 * A hint leaf name for the file. |
|
212 * @param aTainted |
|
213 * A boolean value. When true, the partial content of the file |
|
214 * will be different from the expected content of the original source |
|
215 * file. See the declaration of TEST_DATA_TAINTED for more information. |
|
216 * |
|
217 * @return {Promise} |
|
218 * @resolves When the operation completes, and returns a string with the path |
|
219 * to the generated file. |
|
220 * @rejects If there's an error writing the file. |
|
221 */ |
|
222 function getPartialFile(aLeafName, aTainted = false) { |
|
223 let tempDownload = getTempFile(aLeafName); |
|
224 let partialContent = aTainted |
|
225 ? TEST_DATA_TAINTED.substr(0, TEST_DATA_PARTIAL_LENGTH) |
|
226 : TEST_DATA_SHORT.substr(0, TEST_DATA_PARTIAL_LENGTH); |
|
227 |
|
228 return OS.File.writeAtomic(tempDownload.path, partialContent, |
|
229 { tmpPath: tempDownload.path + ".tmp", |
|
230 flush: true }) |
|
231 .then(() => tempDownload.path); |
|
232 } |
|
233 |
|
234 /** |
|
235 * Generates a Date object to be used as the startTime for the download rows |
|
236 * in the DB. A date that is obviously different from the current time is |
|
237 * generated to make sure this stored data and a `new Date()` can't collide. |
|
238 * |
|
239 * @param aOffset |
|
240 * A offset from the base generated date is used to differentiate each |
|
241 * row in the database. |
|
242 * |
|
243 * @return A Date object. |
|
244 */ |
|
245 function getStartTime(aOffset) { |
|
246 return new Date(1000000 + (aOffset * 10000)); |
|
247 } |
|
248 |
|
249 /** |
|
250 * Performs various checks on an imported Download object to make sure |
|
251 * all properties are properly set as expected from the import procedure. |
|
252 * |
|
253 * @param aDownload |
|
254 * The Download object to be checked. |
|
255 * @param aDownloadRow |
|
256 * An object that represents a row from the original database table, |
|
257 * with extra properties describing expected values that are not |
|
258 * explictly part of the database. |
|
259 * |
|
260 * @return {Promise} |
|
261 * @resolves When the operation completes |
|
262 * @rejects Never |
|
263 */ |
|
264 function checkDownload(aDownload, aDownloadRow) { |
|
265 return Task.spawn(function() { |
|
266 do_check_eq(aDownload.source.url, aDownloadRow.source); |
|
267 do_check_eq(aDownload.source.referrer, aDownloadRow.referrer); |
|
268 |
|
269 do_check_eq(aDownload.target.path, |
|
270 NetUtil.newURI(aDownloadRow.target) |
|
271 .QueryInterface(Ci.nsIFileURL).file.path); |
|
272 |
|
273 do_check_eq(aDownload.target.partFilePath, aDownloadRow.tempPath); |
|
274 |
|
275 if (aDownloadRow.expectedResume) { |
|
276 do_check_true(!aDownload.stopped || aDownload.succeeded); |
|
277 yield promiseDownloadStopped(aDownload); |
|
278 |
|
279 do_check_true(aDownload.succeeded); |
|
280 do_check_eq(aDownload.progress, 100); |
|
281 // If the download has resumed, a new startTime will be set. |
|
282 // By calling toJSON we're also testing that startTime is a Date object. |
|
283 do_check_neq(aDownload.startTime.toJSON(), |
|
284 aDownloadRow.startTime.toJSON()); |
|
285 } else { |
|
286 do_check_false(aDownload.succeeded); |
|
287 do_check_eq(aDownload.startTime.toJSON(), |
|
288 aDownloadRow.startTime.toJSON()); |
|
289 } |
|
290 |
|
291 do_check_eq(aDownload.stopped, true); |
|
292 |
|
293 let serializedSaver = aDownload.saver.toSerializable(); |
|
294 if (typeof(serializedSaver) == "object") { |
|
295 do_check_eq(serializedSaver.type, "copy"); |
|
296 } else { |
|
297 do_check_eq(serializedSaver, "copy"); |
|
298 } |
|
299 |
|
300 if (aDownloadRow.entityID) { |
|
301 do_check_eq(aDownload.saver.entityID, aDownloadRow.entityID); |
|
302 } |
|
303 |
|
304 do_check_eq(aDownload.currentBytes, aDownloadRow.expectedCurrentBytes); |
|
305 do_check_eq(aDownload.totalBytes, aDownloadRow.expectedTotalBytes); |
|
306 |
|
307 if (aDownloadRow.expectedContent) { |
|
308 let fileToCheck = aDownloadRow.expectedResume |
|
309 ? aDownload.target.path |
|
310 : aDownload.target.partFilePath; |
|
311 yield promiseVerifyContents(fileToCheck, aDownloadRow.expectedContent); |
|
312 } |
|
313 |
|
314 do_check_eq(aDownload.contentType, aDownloadRow.expectedContentType); |
|
315 do_check_eq(aDownload.launcherPath, aDownloadRow.preferredApplication); |
|
316 |
|
317 do_check_eq(aDownload.launchWhenSucceeded, |
|
318 aDownloadRow.preferredAction != Ci.nsIMIMEInfo.saveToDisk); |
|
319 }); |
|
320 } |
|
321 |
|
322 //////////////////////////////////////////////////////////////////////////////// |
|
323 //// Preparation tasks |
|
324 |
|
325 /** |
|
326 * Prepares the list of downloads to be added to the database that should |
|
327 * be imported by the import procedure. |
|
328 */ |
|
329 add_task(function prepareDownloadsToImport() { |
|
330 |
|
331 let sourceUrl = httpUrl("source.txt"); |
|
332 let sourceEntityId = yield promiseEntityID(sourceUrl); |
|
333 |
|
334 gDownloadsRowToImport = [ |
|
335 // Paused download with autoResume and a partial file. By |
|
336 // setting the correct entityID the download can resume from |
|
337 // where it stopped, and to test that this works properly we |
|
338 // intentionally set different data in the beginning of the |
|
339 // partial file to make sure it was not replaced. |
|
340 { |
|
341 source: sourceUrl, |
|
342 target: getDownloadTarget("inprogress1.txt"), |
|
343 tempPath: yield getPartialFile("inprogress1.txt.part", true), |
|
344 startTime: getStartTime(1), |
|
345 state: DOWNLOAD_PAUSED, |
|
346 referrer: httpUrl("referrer1"), |
|
347 entityID: sourceEntityId, |
|
348 maxBytes: MAXBYTES_IN_DB, |
|
349 mimeType: "mimeType1", |
|
350 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
351 preferredApplication: "prerredApplication1", |
|
352 autoResume: 1, |
|
353 |
|
354 // Even though the information stored in the DB said |
|
355 // maxBytes was MAXBYTES_IN_DB, the download turned out to be |
|
356 // a different length. Here we make sure the totalBytes property |
|
357 // was correctly set with the actual value. The same consideration |
|
358 // applies to the contentType. |
|
359 expectedCurrentBytes: TEST_DATA_LENGTH, |
|
360 expectedTotalBytes: TEST_DATA_LENGTH, |
|
361 expectedResume: true, |
|
362 expectedContentType: "text/plain", |
|
363 expectedContent: TEST_DATA_TAINTED, |
|
364 }, |
|
365 |
|
366 // Paused download with autoResume and a partial file, |
|
367 // but missing entityID. This means that the download will |
|
368 // start from beginning, and the entire original content of the |
|
369 // source file should replace the different data that was stored |
|
370 // in the partial file. |
|
371 { |
|
372 source: sourceUrl, |
|
373 target: getDownloadTarget("inprogress2.txt"), |
|
374 tempPath: yield getPartialFile("inprogress2.txt.part", true), |
|
375 startTime: getStartTime(2), |
|
376 state: DOWNLOAD_PAUSED, |
|
377 referrer: httpUrl("referrer2"), |
|
378 entityID: "", |
|
379 maxBytes: MAXBYTES_IN_DB, |
|
380 mimeType: "mimeType2", |
|
381 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
382 preferredApplication: "prerredApplication2", |
|
383 autoResume: 1, |
|
384 |
|
385 expectedCurrentBytes: TEST_DATA_LENGTH, |
|
386 expectedTotalBytes: TEST_DATA_LENGTH, |
|
387 expectedResume: true, |
|
388 expectedContentType: "text/plain", |
|
389 expectedContent: TEST_DATA_SHORT |
|
390 }, |
|
391 |
|
392 // Paused download with no autoResume and a partial file. |
|
393 { |
|
394 source: sourceUrl, |
|
395 target: getDownloadTarget("inprogress3.txt"), |
|
396 tempPath: yield getPartialFile("inprogress3.txt.part"), |
|
397 startTime: getStartTime(3), |
|
398 state: DOWNLOAD_PAUSED, |
|
399 referrer: httpUrl("referrer3"), |
|
400 entityID: "", |
|
401 maxBytes: MAXBYTES_IN_DB, |
|
402 mimeType: "mimeType3", |
|
403 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
404 preferredApplication: "prerredApplication3", |
|
405 autoResume: 0, |
|
406 |
|
407 // Since this download has not been resumed, the actual data |
|
408 // about its total size and content type is not known. |
|
409 // Therefore, we're going by the information imported from the DB. |
|
410 expectedCurrentBytes: TEST_DATA_PARTIAL_LENGTH, |
|
411 expectedTotalBytes: MAXBYTES_IN_DB, |
|
412 expectedResume: false, |
|
413 expectedContentType: "mimeType3", |
|
414 expectedContent: TEST_DATA_SHORT.substr(0, TEST_DATA_PARTIAL_LENGTH), |
|
415 }, |
|
416 |
|
417 // Paused download with autoResume and no partial file. |
|
418 { |
|
419 source: sourceUrl, |
|
420 target: getDownloadTarget("inprogress4.txt"), |
|
421 tempPath: "", |
|
422 startTime: getStartTime(4), |
|
423 state: DOWNLOAD_PAUSED, |
|
424 referrer: httpUrl("referrer4"), |
|
425 entityID: "", |
|
426 maxBytes: MAXBYTES_IN_DB, |
|
427 mimeType: "text/plain", |
|
428 preferredAction: Ci.nsIMIMEInfo.useHelperApp, |
|
429 preferredApplication: "prerredApplication4", |
|
430 autoResume: 1, |
|
431 |
|
432 expectedCurrentBytes: TEST_DATA_LENGTH, |
|
433 expectedTotalBytes: TEST_DATA_LENGTH, |
|
434 expectedResume: true, |
|
435 expectedContentType: "text/plain", |
|
436 expectedContent: TEST_DATA_SHORT |
|
437 }, |
|
438 |
|
439 // Paused download with no autoResume and no partial file. |
|
440 { |
|
441 source: sourceUrl, |
|
442 target: getDownloadTarget("inprogress5.txt"), |
|
443 tempPath: "", |
|
444 startTime: getStartTime(5), |
|
445 state: DOWNLOAD_PAUSED, |
|
446 referrer: httpUrl("referrer4"), |
|
447 entityID: "", |
|
448 maxBytes: MAXBYTES_IN_DB, |
|
449 mimeType: "text/plain", |
|
450 preferredAction: Ci.nsIMIMEInfo.useSystemDefault, |
|
451 preferredApplication: "prerredApplication5", |
|
452 autoResume: 0, |
|
453 |
|
454 expectedCurrentBytes: 0, |
|
455 expectedTotalBytes: MAXBYTES_IN_DB, |
|
456 expectedResume: false, |
|
457 expectedContentType: "text/plain", |
|
458 }, |
|
459 |
|
460 // Queued download with no autoResume and no partial file. |
|
461 // Even though autoResume=0, queued downloads always autoResume. |
|
462 { |
|
463 source: sourceUrl, |
|
464 target: getDownloadTarget("inprogress6.txt"), |
|
465 tempPath: "", |
|
466 startTime: getStartTime(6), |
|
467 state: DOWNLOAD_QUEUED, |
|
468 referrer: httpUrl("referrer6"), |
|
469 entityID: "", |
|
470 maxBytes: MAXBYTES_IN_DB, |
|
471 mimeType: "text/plain", |
|
472 preferredAction: Ci.nsIMIMEInfo.useHelperApp, |
|
473 preferredApplication: "prerredApplication6", |
|
474 autoResume: 0, |
|
475 |
|
476 expectedCurrentBytes: TEST_DATA_LENGTH, |
|
477 expectedTotalBytes: TEST_DATA_LENGTH, |
|
478 expectedResume: true, |
|
479 expectedContentType: "text/plain", |
|
480 expectedContent: TEST_DATA_SHORT |
|
481 }, |
|
482 |
|
483 // Notstarted download with no autoResume and no partial file. |
|
484 // Even though autoResume=0, notstarted downloads always autoResume. |
|
485 { |
|
486 source: sourceUrl, |
|
487 target: getDownloadTarget("inprogress7.txt"), |
|
488 tempPath: "", |
|
489 startTime: getStartTime(7), |
|
490 state: DOWNLOAD_NOTSTARTED, |
|
491 referrer: httpUrl("referrer7"), |
|
492 entityID: "", |
|
493 maxBytes: MAXBYTES_IN_DB, |
|
494 mimeType: "text/plain", |
|
495 preferredAction: Ci.nsIMIMEInfo.useHelperApp, |
|
496 preferredApplication: "prerredApplication7", |
|
497 autoResume: 0, |
|
498 |
|
499 expectedCurrentBytes: TEST_DATA_LENGTH, |
|
500 expectedTotalBytes: TEST_DATA_LENGTH, |
|
501 expectedResume: true, |
|
502 expectedContentType: "text/plain", |
|
503 expectedContent: TEST_DATA_SHORT |
|
504 }, |
|
505 |
|
506 // Downloading download with no autoResume and a partial file. |
|
507 // Even though autoResume=0, downloading downloads always autoResume. |
|
508 { |
|
509 source: sourceUrl, |
|
510 target: getDownloadTarget("inprogress8.txt"), |
|
511 tempPath: yield getPartialFile("inprogress8.txt.part", true), |
|
512 startTime: getStartTime(8), |
|
513 state: DOWNLOAD_DOWNLOADING, |
|
514 referrer: httpUrl("referrer8"), |
|
515 entityID: sourceEntityId, |
|
516 maxBytes: MAXBYTES_IN_DB, |
|
517 mimeType: "text/plain", |
|
518 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
519 preferredApplication: "prerredApplication8", |
|
520 autoResume: 0, |
|
521 |
|
522 expectedCurrentBytes: TEST_DATA_LENGTH, |
|
523 expectedTotalBytes: TEST_DATA_LENGTH, |
|
524 expectedResume: true, |
|
525 expectedContentType: "text/plain", |
|
526 expectedContent: TEST_DATA_TAINTED |
|
527 }, |
|
528 ]; |
|
529 }); |
|
530 |
|
531 /** |
|
532 * Prepares the list of downloads to be added to the database that should |
|
533 * *not* be imported by the import procedure. |
|
534 */ |
|
535 add_task(function prepareNonImportableDownloads() |
|
536 { |
|
537 gDownloadsRowNonImportable = [ |
|
538 // Download with no source (should never happen in normal circumstances). |
|
539 { |
|
540 source: "", |
|
541 target: "nonimportable1.txt", |
|
542 tempPath: "", |
|
543 startTime: getStartTime(1), |
|
544 state: DOWNLOAD_PAUSED, |
|
545 referrer: "", |
|
546 entityID: "", |
|
547 maxBytes: MAXBYTES_IN_DB, |
|
548 mimeType: "mimeType1", |
|
549 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
550 preferredApplication: "prerredApplication1", |
|
551 autoResume: 1 |
|
552 }, |
|
553 |
|
554 // state = DOWNLOAD_FAILED |
|
555 { |
|
556 source: httpUrl("source.txt"), |
|
557 target: "nonimportable2.txt", |
|
558 tempPath: "", |
|
559 startTime: getStartTime(2), |
|
560 state: DOWNLOAD_FAILED, |
|
561 referrer: "", |
|
562 entityID: "", |
|
563 maxBytes: MAXBYTES_IN_DB, |
|
564 mimeType: "mimeType2", |
|
565 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
566 preferredApplication: "prerredApplication2", |
|
567 autoResume: 1 |
|
568 }, |
|
569 |
|
570 // state = DOWNLOAD_CANCELED |
|
571 { |
|
572 source: httpUrl("source.txt"), |
|
573 target: "nonimportable3.txt", |
|
574 tempPath: "", |
|
575 startTime: getStartTime(3), |
|
576 state: DOWNLOAD_CANCELED, |
|
577 referrer: "", |
|
578 entityID: "", |
|
579 maxBytes: MAXBYTES_IN_DB, |
|
580 mimeType: "mimeType3", |
|
581 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
582 preferredApplication: "prerredApplication3", |
|
583 autoResume: 1 |
|
584 }, |
|
585 |
|
586 // state = DOWNLOAD_BLOCKED_PARENTAL |
|
587 { |
|
588 source: httpUrl("source.txt"), |
|
589 target: "nonimportable4.txt", |
|
590 tempPath: "", |
|
591 startTime: getStartTime(4), |
|
592 state: DOWNLOAD_BLOCKED_PARENTAL, |
|
593 referrer: "", |
|
594 entityID: "", |
|
595 maxBytes: MAXBYTES_IN_DB, |
|
596 mimeType: "mimeType4", |
|
597 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
598 preferredApplication: "prerredApplication4", |
|
599 autoResume: 1 |
|
600 }, |
|
601 |
|
602 // state = DOWNLOAD_SCANNING |
|
603 { |
|
604 source: httpUrl("source.txt"), |
|
605 target: "nonimportable5.txt", |
|
606 tempPath: "", |
|
607 startTime: getStartTime(5), |
|
608 state: DOWNLOAD_SCANNING, |
|
609 referrer: "", |
|
610 entityID: "", |
|
611 maxBytes: MAXBYTES_IN_DB, |
|
612 mimeType: "mimeType5", |
|
613 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
614 preferredApplication: "prerredApplication5", |
|
615 autoResume: 1 |
|
616 }, |
|
617 |
|
618 // state = DOWNLOAD_DIRTY |
|
619 { |
|
620 source: httpUrl("source.txt"), |
|
621 target: "nonimportable6.txt", |
|
622 tempPath: "", |
|
623 startTime: getStartTime(6), |
|
624 state: DOWNLOAD_DIRTY, |
|
625 referrer: "", |
|
626 entityID: "", |
|
627 maxBytes: MAXBYTES_IN_DB, |
|
628 mimeType: "mimeType6", |
|
629 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
630 preferredApplication: "prerredApplication6", |
|
631 autoResume: 1 |
|
632 }, |
|
633 |
|
634 // state = DOWNLOAD_BLOCKED_POLICY |
|
635 { |
|
636 source: httpUrl("source.txt"), |
|
637 target: "nonimportable7.txt", |
|
638 tempPath: "", |
|
639 startTime: getStartTime(7), |
|
640 state: DOWNLOAD_BLOCKED_POLICY, |
|
641 referrer: "", |
|
642 entityID: "", |
|
643 maxBytes: MAXBYTES_IN_DB, |
|
644 mimeType: "mimeType7", |
|
645 preferredAction: Ci.nsIMIMEInfo.saveToDisk, |
|
646 preferredApplication: "prerredApplication7", |
|
647 autoResume: 1 |
|
648 }, |
|
649 ]; |
|
650 }); |
|
651 |
|
652 //////////////////////////////////////////////////////////////////////////////// |
|
653 //// Test |
|
654 |
|
655 /** |
|
656 * Creates a temporary Sqlite database with download data and perform an |
|
657 * import of that data to the new Downloads API to verify that the import |
|
658 * worked correctly. |
|
659 */ |
|
660 add_task(function test_downloadImport() |
|
661 { |
|
662 let connection = null; |
|
663 let downloadsSqlite = getTempFile("downloads.sqlite").path; |
|
664 |
|
665 try { |
|
666 // Set up the database. |
|
667 connection = yield promiseEmptyDatabaseConnection({ |
|
668 aPath: downloadsSqlite, |
|
669 aSchemaVersion: 9 |
|
670 }); |
|
671 |
|
672 // Insert both the importable and non-importable |
|
673 // downloads together. |
|
674 for (let downloadRow of gDownloadsRowToImport) { |
|
675 yield promiseInsertRow(connection, downloadRow); |
|
676 } |
|
677 |
|
678 for (let downloadRow of gDownloadsRowNonImportable) { |
|
679 yield promiseInsertRow(connection, downloadRow); |
|
680 } |
|
681 |
|
682 // Check that every item was inserted. |
|
683 do_check_eq((yield promiseTableCount(connection)), |
|
684 gDownloadsRowToImport.length + |
|
685 gDownloadsRowNonImportable.length); |
|
686 } finally { |
|
687 // Close the connection so that DownloadImport can open it. |
|
688 yield connection.close(); |
|
689 } |
|
690 |
|
691 // Import items. |
|
692 let list = yield promiseNewList(false); |
|
693 yield new DownloadImport(list, downloadsSqlite).import(); |
|
694 let items = yield list.getAll(); |
|
695 |
|
696 do_check_eq(items.length, gDownloadsRowToImport.length); |
|
697 |
|
698 for (let i = 0; i < gDownloadsRowToImport.length; i++) { |
|
699 yield checkDownload(items[i], gDownloadsRowToImport[i]); |
|
700 } |
|
701 }) |