1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/jsdownloads/test/unit/test_DownloadImport.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,701 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + * http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +/** 1.8 + * Tests the DownloadImport object. 1.9 + */ 1.10 + 1.11 +"use strict"; 1.12 + 1.13 +//////////////////////////////////////////////////////////////////////////////// 1.14 +//// Globals 1.15 + 1.16 +XPCOMUtils.defineLazyModuleGetter(this, "Sqlite", 1.17 + "resource://gre/modules/Sqlite.jsm"); 1.18 +XPCOMUtils.defineLazyModuleGetter(this, "DownloadImport", 1.19 + "resource://gre/modules/DownloadImport.jsm"); 1.20 + 1.21 +// Importable states 1.22 +const DOWNLOAD_NOTSTARTED = -1; 1.23 +const DOWNLOAD_DOWNLOADING = 0; 1.24 +const DOWNLOAD_PAUSED = 4; 1.25 +const DOWNLOAD_QUEUED = 5; 1.26 + 1.27 +// Non importable states 1.28 +const DOWNLOAD_FAILED = 2; 1.29 +const DOWNLOAD_CANCELED = 3; 1.30 +const DOWNLOAD_BLOCKED_PARENTAL = 6; 1.31 +const DOWNLOAD_SCANNING = 7; 1.32 +const DOWNLOAD_DIRTY = 8; 1.33 +const DOWNLOAD_BLOCKED_POLICY = 9; 1.34 + 1.35 +// The TEST_DATA_TAINTED const is a version of TEST_DATA_SHORT in which the 1.36 +// beginning of the data was changed (with the TEST_DATA_REPLACEMENT value). 1.37 +// We use this to test that the entityID is properly imported and the download 1.38 +// can be resumed from where it was paused. 1.39 +// For simplification purposes, the test requires that TEST_DATA_SHORT and 1.40 +// TEST_DATA_TAINTED have the same length. 1.41 +const TEST_DATA_REPLACEMENT = "-changed- "; 1.42 +const TEST_DATA_TAINTED = TEST_DATA_REPLACEMENT + 1.43 + TEST_DATA_SHORT.substr(TEST_DATA_REPLACEMENT.length); 1.44 +const TEST_DATA_LENGTH = TEST_DATA_SHORT.length; 1.45 + 1.46 +// The length of the partial file that we'll write to disk as an existing 1.47 +// ongoing download. 1.48 +const TEST_DATA_PARTIAL_LENGTH = TEST_DATA_REPLACEMENT.length; 1.49 + 1.50 +// The value of the "maxBytes" column stored in the DB about the downloads. 1.51 +// It's intentionally different than TEST_DATA_LENGTH to test that each value 1.52 +// is seen when expected. 1.53 +const MAXBYTES_IN_DB = TEST_DATA_LENGTH - 10; 1.54 + 1.55 +let gDownloadsRowToImport; 1.56 +let gDownloadsRowNonImportable; 1.57 + 1.58 +/** 1.59 + * Creates a database with an empty moz_downloads table and leaves an 1.60 + * open connection to it. 1.61 + * 1.62 + * @param aPath 1.63 + * String containing the path of the database file to be created. 1.64 + * @param aSchemaVersion 1.65 + * Number with the version of the database schema to set. 1.66 + * 1.67 + * @return {Promise} 1.68 + * @resolves The open connection to the database. 1.69 + * @rejects If an error occurred during the database creation. 1.70 + */ 1.71 +function promiseEmptyDatabaseConnection({aPath, aSchemaVersion}) { 1.72 + return Task.spawn(function () { 1.73 + let connection = yield Sqlite.openConnection({ path: aPath }); 1.74 + 1.75 + yield connection.execute("CREATE TABLE moz_downloads (" 1.76 + + "id INTEGER PRIMARY KEY," 1.77 + + "name TEXT," 1.78 + + "source TEXT," 1.79 + + "target TEXT," 1.80 + + "tempPath TEXT," 1.81 + + "startTime INTEGER," 1.82 + + "endTime INTEGER," 1.83 + + "state INTEGER," 1.84 + + "referrer TEXT," 1.85 + + "entityID TEXT," 1.86 + + "currBytes INTEGER NOT NULL DEFAULT 0," 1.87 + + "maxBytes INTEGER NOT NULL DEFAULT -1," 1.88 + + "mimeType TEXT," 1.89 + + "preferredApplication TEXT," 1.90 + + "preferredAction INTEGER NOT NULL DEFAULT 0," 1.91 + + "autoResume INTEGER NOT NULL DEFAULT 0," 1.92 + + "guid TEXT)"); 1.93 + 1.94 + yield connection.setSchemaVersion(aSchemaVersion); 1.95 + 1.96 + throw new Task.Result(connection); 1.97 + }); 1.98 +} 1.99 + 1.100 +/** 1.101 + * Inserts a new entry in the database with the given columns' values. 1.102 + * 1.103 + * @param aConnection 1.104 + * The database connection. 1.105 + * @param aDownloadRow 1.106 + * An object representing the values for each column of the row 1.107 + * being inserted. 1.108 + * 1.109 + * @return {Promise} 1.110 + * @resolves When the operation completes. 1.111 + * @rejects If there's an error inserting the row. 1.112 + */ 1.113 +function promiseInsertRow(aConnection, aDownloadRow) { 1.114 + // We can't use the aDownloadRow obj directly in the execute statement 1.115 + // because the obj bind code in Sqlite.jsm doesn't allow objects 1.116 + // with extra properties beyond those being binded. So we might as well 1.117 + // use an array as it is simpler. 1.118 + let values = [ 1.119 + aDownloadRow.source, aDownloadRow.target, aDownloadRow.tempPath, 1.120 + aDownloadRow.startTime.getTime() * 1000, aDownloadRow.state, 1.121 + aDownloadRow.referrer, aDownloadRow.entityID, aDownloadRow.maxBytes, 1.122 + aDownloadRow.mimeType, aDownloadRow.preferredApplication, 1.123 + aDownloadRow.preferredAction, aDownloadRow.autoResume 1.124 + ]; 1.125 + 1.126 + return aConnection.execute("INSERT INTO moz_downloads (" 1.127 + + "name, source, target, tempPath, startTime," 1.128 + + "endTime, state, referrer, entityID, currBytes," 1.129 + + "maxBytes, mimeType, preferredApplication," 1.130 + + "preferredAction, autoResume, guid)" 1.131 + + "VALUES (" 1.132 + + "'', ?, ?, ?, ?, " //name, 1.133 + + "0, ?, ?, ?, 0, " //endTime, currBytes 1.134 + + " ?, ?, ?, " // 1.135 + + " ?, ?, '')", //and guid are not imported 1.136 + values); 1.137 +} 1.138 + 1.139 +/** 1.140 + * Retrieves the number of rows in the moz_downloads table of the 1.141 + * database. 1.142 + * 1.143 + * @param aConnection 1.144 + * The database connection. 1.145 + * 1.146 + * @return {Promise} 1.147 + * @resolves With the number of rows. 1.148 + * @rejects Never. 1.149 + */ 1.150 +function promiseTableCount(aConnection) { 1.151 + return aConnection.execute("SELECT COUNT(*) FROM moz_downloads") 1.152 + .then(res => res[0].getResultByName("COUNT(*)")) 1.153 + .then(null, Cu.reportError); 1.154 +} 1.155 + 1.156 +/** 1.157 + * Briefly opens a network channel to a given URL to retrieve 1.158 + * the entityID of this url, as generated by the network code. 1.159 + * 1.160 + * @param aUrl 1.161 + * The URL to retrieve the entityID. 1.162 + * 1.163 + * @return {Promise} 1.164 + * @resolves The EntityID of the given URL. 1.165 + * @rejects When there's a problem accessing the URL. 1.166 + */ 1.167 +function promiseEntityID(aUrl) { 1.168 + let deferred = Promise.defer(); 1.169 + let entityID = ""; 1.170 + let channel = NetUtil.newChannel(NetUtil.newURI(aUrl)); 1.171 + 1.172 + channel.asyncOpen({ 1.173 + onStartRequest: function (aRequest) { 1.174 + if (aRequest instanceof Ci.nsIResumableChannel) { 1.175 + entityID = aRequest.entityID; 1.176 + } 1.177 + aRequest.cancel(Cr.NS_BINDING_ABORTED); 1.178 + }, 1.179 + 1.180 + onStopRequest: function (aRequest, aContext, aStatusCode) { 1.181 + if (aStatusCode == Cr.NS_BINDING_ABORTED) { 1.182 + deferred.resolve(entityID); 1.183 + } else { 1.184 + deferred.reject("Unexpected status code received"); 1.185 + } 1.186 + }, 1.187 + 1.188 + onDataAvailable: function () {} 1.189 + }, null); 1.190 + 1.191 + return deferred.promise; 1.192 +} 1.193 + 1.194 +/** 1.195 + * Gets a file path to a temporary writeable download target, in the 1.196 + * correct format as expected to be stored in the downloads database, 1.197 + * which is file:///absolute/path/to/file 1.198 + * 1.199 + * @param aLeafName 1.200 + * A hint leaf name for the file. 1.201 + * 1.202 + * @return String The path to the download target. 1.203 + */ 1.204 +function getDownloadTarget(aLeafName) { 1.205 + return NetUtil.newURI(getTempFile(aLeafName)).spec; 1.206 +} 1.207 + 1.208 +/** 1.209 + * Generates a temporary partial file to use as an in-progress 1.210 + * download. The file is written to disk with a part of the total expected 1.211 + * download content pre-written. 1.212 + * 1.213 + * @param aLeafName 1.214 + * A hint leaf name for the file. 1.215 + * @param aTainted 1.216 + * A boolean value. When true, the partial content of the file 1.217 + * will be different from the expected content of the original source 1.218 + * file. See the declaration of TEST_DATA_TAINTED for more information. 1.219 + * 1.220 + * @return {Promise} 1.221 + * @resolves When the operation completes, and returns a string with the path 1.222 + * to the generated file. 1.223 + * @rejects If there's an error writing the file. 1.224 + */ 1.225 +function getPartialFile(aLeafName, aTainted = false) { 1.226 + let tempDownload = getTempFile(aLeafName); 1.227 + let partialContent = aTainted 1.228 + ? TEST_DATA_TAINTED.substr(0, TEST_DATA_PARTIAL_LENGTH) 1.229 + : TEST_DATA_SHORT.substr(0, TEST_DATA_PARTIAL_LENGTH); 1.230 + 1.231 + return OS.File.writeAtomic(tempDownload.path, partialContent, 1.232 + { tmpPath: tempDownload.path + ".tmp", 1.233 + flush: true }) 1.234 + .then(() => tempDownload.path); 1.235 +} 1.236 + 1.237 +/** 1.238 + * Generates a Date object to be used as the startTime for the download rows 1.239 + * in the DB. A date that is obviously different from the current time is 1.240 + * generated to make sure this stored data and a `new Date()` can't collide. 1.241 + * 1.242 + * @param aOffset 1.243 + * A offset from the base generated date is used to differentiate each 1.244 + * row in the database. 1.245 + * 1.246 + * @return A Date object. 1.247 + */ 1.248 +function getStartTime(aOffset) { 1.249 + return new Date(1000000 + (aOffset * 10000)); 1.250 +} 1.251 + 1.252 +/** 1.253 + * Performs various checks on an imported Download object to make sure 1.254 + * all properties are properly set as expected from the import procedure. 1.255 + * 1.256 + * @param aDownload 1.257 + * The Download object to be checked. 1.258 + * @param aDownloadRow 1.259 + * An object that represents a row from the original database table, 1.260 + * with extra properties describing expected values that are not 1.261 + * explictly part of the database. 1.262 + * 1.263 + * @return {Promise} 1.264 + * @resolves When the operation completes 1.265 + * @rejects Never 1.266 + */ 1.267 +function checkDownload(aDownload, aDownloadRow) { 1.268 + return Task.spawn(function() { 1.269 + do_check_eq(aDownload.source.url, aDownloadRow.source); 1.270 + do_check_eq(aDownload.source.referrer, aDownloadRow.referrer); 1.271 + 1.272 + do_check_eq(aDownload.target.path, 1.273 + NetUtil.newURI(aDownloadRow.target) 1.274 + .QueryInterface(Ci.nsIFileURL).file.path); 1.275 + 1.276 + do_check_eq(aDownload.target.partFilePath, aDownloadRow.tempPath); 1.277 + 1.278 + if (aDownloadRow.expectedResume) { 1.279 + do_check_true(!aDownload.stopped || aDownload.succeeded); 1.280 + yield promiseDownloadStopped(aDownload); 1.281 + 1.282 + do_check_true(aDownload.succeeded); 1.283 + do_check_eq(aDownload.progress, 100); 1.284 + // If the download has resumed, a new startTime will be set. 1.285 + // By calling toJSON we're also testing that startTime is a Date object. 1.286 + do_check_neq(aDownload.startTime.toJSON(), 1.287 + aDownloadRow.startTime.toJSON()); 1.288 + } else { 1.289 + do_check_false(aDownload.succeeded); 1.290 + do_check_eq(aDownload.startTime.toJSON(), 1.291 + aDownloadRow.startTime.toJSON()); 1.292 + } 1.293 + 1.294 + do_check_eq(aDownload.stopped, true); 1.295 + 1.296 + let serializedSaver = aDownload.saver.toSerializable(); 1.297 + if (typeof(serializedSaver) == "object") { 1.298 + do_check_eq(serializedSaver.type, "copy"); 1.299 + } else { 1.300 + do_check_eq(serializedSaver, "copy"); 1.301 + } 1.302 + 1.303 + if (aDownloadRow.entityID) { 1.304 + do_check_eq(aDownload.saver.entityID, aDownloadRow.entityID); 1.305 + } 1.306 + 1.307 + do_check_eq(aDownload.currentBytes, aDownloadRow.expectedCurrentBytes); 1.308 + do_check_eq(aDownload.totalBytes, aDownloadRow.expectedTotalBytes); 1.309 + 1.310 + if (aDownloadRow.expectedContent) { 1.311 + let fileToCheck = aDownloadRow.expectedResume 1.312 + ? aDownload.target.path 1.313 + : aDownload.target.partFilePath; 1.314 + yield promiseVerifyContents(fileToCheck, aDownloadRow.expectedContent); 1.315 + } 1.316 + 1.317 + do_check_eq(aDownload.contentType, aDownloadRow.expectedContentType); 1.318 + do_check_eq(aDownload.launcherPath, aDownloadRow.preferredApplication); 1.319 + 1.320 + do_check_eq(aDownload.launchWhenSucceeded, 1.321 + aDownloadRow.preferredAction != Ci.nsIMIMEInfo.saveToDisk); 1.322 + }); 1.323 +} 1.324 + 1.325 +//////////////////////////////////////////////////////////////////////////////// 1.326 +//// Preparation tasks 1.327 + 1.328 +/** 1.329 + * Prepares the list of downloads to be added to the database that should 1.330 + * be imported by the import procedure. 1.331 + */ 1.332 +add_task(function prepareDownloadsToImport() { 1.333 + 1.334 + let sourceUrl = httpUrl("source.txt"); 1.335 + let sourceEntityId = yield promiseEntityID(sourceUrl); 1.336 + 1.337 + gDownloadsRowToImport = [ 1.338 + // Paused download with autoResume and a partial file. By 1.339 + // setting the correct entityID the download can resume from 1.340 + // where it stopped, and to test that this works properly we 1.341 + // intentionally set different data in the beginning of the 1.342 + // partial file to make sure it was not replaced. 1.343 + { 1.344 + source: sourceUrl, 1.345 + target: getDownloadTarget("inprogress1.txt"), 1.346 + tempPath: yield getPartialFile("inprogress1.txt.part", true), 1.347 + startTime: getStartTime(1), 1.348 + state: DOWNLOAD_PAUSED, 1.349 + referrer: httpUrl("referrer1"), 1.350 + entityID: sourceEntityId, 1.351 + maxBytes: MAXBYTES_IN_DB, 1.352 + mimeType: "mimeType1", 1.353 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.354 + preferredApplication: "prerredApplication1", 1.355 + autoResume: 1, 1.356 + 1.357 + // Even though the information stored in the DB said 1.358 + // maxBytes was MAXBYTES_IN_DB, the download turned out to be 1.359 + // a different length. Here we make sure the totalBytes property 1.360 + // was correctly set with the actual value. The same consideration 1.361 + // applies to the contentType. 1.362 + expectedCurrentBytes: TEST_DATA_LENGTH, 1.363 + expectedTotalBytes: TEST_DATA_LENGTH, 1.364 + expectedResume: true, 1.365 + expectedContentType: "text/plain", 1.366 + expectedContent: TEST_DATA_TAINTED, 1.367 + }, 1.368 + 1.369 + // Paused download with autoResume and a partial file, 1.370 + // but missing entityID. This means that the download will 1.371 + // start from beginning, and the entire original content of the 1.372 + // source file should replace the different data that was stored 1.373 + // in the partial file. 1.374 + { 1.375 + source: sourceUrl, 1.376 + target: getDownloadTarget("inprogress2.txt"), 1.377 + tempPath: yield getPartialFile("inprogress2.txt.part", true), 1.378 + startTime: getStartTime(2), 1.379 + state: DOWNLOAD_PAUSED, 1.380 + referrer: httpUrl("referrer2"), 1.381 + entityID: "", 1.382 + maxBytes: MAXBYTES_IN_DB, 1.383 + mimeType: "mimeType2", 1.384 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.385 + preferredApplication: "prerredApplication2", 1.386 + autoResume: 1, 1.387 + 1.388 + expectedCurrentBytes: TEST_DATA_LENGTH, 1.389 + expectedTotalBytes: TEST_DATA_LENGTH, 1.390 + expectedResume: true, 1.391 + expectedContentType: "text/plain", 1.392 + expectedContent: TEST_DATA_SHORT 1.393 + }, 1.394 + 1.395 + // Paused download with no autoResume and a partial file. 1.396 + { 1.397 + source: sourceUrl, 1.398 + target: getDownloadTarget("inprogress3.txt"), 1.399 + tempPath: yield getPartialFile("inprogress3.txt.part"), 1.400 + startTime: getStartTime(3), 1.401 + state: DOWNLOAD_PAUSED, 1.402 + referrer: httpUrl("referrer3"), 1.403 + entityID: "", 1.404 + maxBytes: MAXBYTES_IN_DB, 1.405 + mimeType: "mimeType3", 1.406 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.407 + preferredApplication: "prerredApplication3", 1.408 + autoResume: 0, 1.409 + 1.410 + // Since this download has not been resumed, the actual data 1.411 + // about its total size and content type is not known. 1.412 + // Therefore, we're going by the information imported from the DB. 1.413 + expectedCurrentBytes: TEST_DATA_PARTIAL_LENGTH, 1.414 + expectedTotalBytes: MAXBYTES_IN_DB, 1.415 + expectedResume: false, 1.416 + expectedContentType: "mimeType3", 1.417 + expectedContent: TEST_DATA_SHORT.substr(0, TEST_DATA_PARTIAL_LENGTH), 1.418 + }, 1.419 + 1.420 + // Paused download with autoResume and no partial file. 1.421 + { 1.422 + source: sourceUrl, 1.423 + target: getDownloadTarget("inprogress4.txt"), 1.424 + tempPath: "", 1.425 + startTime: getStartTime(4), 1.426 + state: DOWNLOAD_PAUSED, 1.427 + referrer: httpUrl("referrer4"), 1.428 + entityID: "", 1.429 + maxBytes: MAXBYTES_IN_DB, 1.430 + mimeType: "text/plain", 1.431 + preferredAction: Ci.nsIMIMEInfo.useHelperApp, 1.432 + preferredApplication: "prerredApplication4", 1.433 + autoResume: 1, 1.434 + 1.435 + expectedCurrentBytes: TEST_DATA_LENGTH, 1.436 + expectedTotalBytes: TEST_DATA_LENGTH, 1.437 + expectedResume: true, 1.438 + expectedContentType: "text/plain", 1.439 + expectedContent: TEST_DATA_SHORT 1.440 + }, 1.441 + 1.442 + // Paused download with no autoResume and no partial file. 1.443 + { 1.444 + source: sourceUrl, 1.445 + target: getDownloadTarget("inprogress5.txt"), 1.446 + tempPath: "", 1.447 + startTime: getStartTime(5), 1.448 + state: DOWNLOAD_PAUSED, 1.449 + referrer: httpUrl("referrer4"), 1.450 + entityID: "", 1.451 + maxBytes: MAXBYTES_IN_DB, 1.452 + mimeType: "text/plain", 1.453 + preferredAction: Ci.nsIMIMEInfo.useSystemDefault, 1.454 + preferredApplication: "prerredApplication5", 1.455 + autoResume: 0, 1.456 + 1.457 + expectedCurrentBytes: 0, 1.458 + expectedTotalBytes: MAXBYTES_IN_DB, 1.459 + expectedResume: false, 1.460 + expectedContentType: "text/plain", 1.461 + }, 1.462 + 1.463 + // Queued download with no autoResume and no partial file. 1.464 + // Even though autoResume=0, queued downloads always autoResume. 1.465 + { 1.466 + source: sourceUrl, 1.467 + target: getDownloadTarget("inprogress6.txt"), 1.468 + tempPath: "", 1.469 + startTime: getStartTime(6), 1.470 + state: DOWNLOAD_QUEUED, 1.471 + referrer: httpUrl("referrer6"), 1.472 + entityID: "", 1.473 + maxBytes: MAXBYTES_IN_DB, 1.474 + mimeType: "text/plain", 1.475 + preferredAction: Ci.nsIMIMEInfo.useHelperApp, 1.476 + preferredApplication: "prerredApplication6", 1.477 + autoResume: 0, 1.478 + 1.479 + expectedCurrentBytes: TEST_DATA_LENGTH, 1.480 + expectedTotalBytes: TEST_DATA_LENGTH, 1.481 + expectedResume: true, 1.482 + expectedContentType: "text/plain", 1.483 + expectedContent: TEST_DATA_SHORT 1.484 + }, 1.485 + 1.486 + // Notstarted download with no autoResume and no partial file. 1.487 + // Even though autoResume=0, notstarted downloads always autoResume. 1.488 + { 1.489 + source: sourceUrl, 1.490 + target: getDownloadTarget("inprogress7.txt"), 1.491 + tempPath: "", 1.492 + startTime: getStartTime(7), 1.493 + state: DOWNLOAD_NOTSTARTED, 1.494 + referrer: httpUrl("referrer7"), 1.495 + entityID: "", 1.496 + maxBytes: MAXBYTES_IN_DB, 1.497 + mimeType: "text/plain", 1.498 + preferredAction: Ci.nsIMIMEInfo.useHelperApp, 1.499 + preferredApplication: "prerredApplication7", 1.500 + autoResume: 0, 1.501 + 1.502 + expectedCurrentBytes: TEST_DATA_LENGTH, 1.503 + expectedTotalBytes: TEST_DATA_LENGTH, 1.504 + expectedResume: true, 1.505 + expectedContentType: "text/plain", 1.506 + expectedContent: TEST_DATA_SHORT 1.507 + }, 1.508 + 1.509 + // Downloading download with no autoResume and a partial file. 1.510 + // Even though autoResume=0, downloading downloads always autoResume. 1.511 + { 1.512 + source: sourceUrl, 1.513 + target: getDownloadTarget("inprogress8.txt"), 1.514 + tempPath: yield getPartialFile("inprogress8.txt.part", true), 1.515 + startTime: getStartTime(8), 1.516 + state: DOWNLOAD_DOWNLOADING, 1.517 + referrer: httpUrl("referrer8"), 1.518 + entityID: sourceEntityId, 1.519 + maxBytes: MAXBYTES_IN_DB, 1.520 + mimeType: "text/plain", 1.521 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.522 + preferredApplication: "prerredApplication8", 1.523 + autoResume: 0, 1.524 + 1.525 + expectedCurrentBytes: TEST_DATA_LENGTH, 1.526 + expectedTotalBytes: TEST_DATA_LENGTH, 1.527 + expectedResume: true, 1.528 + expectedContentType: "text/plain", 1.529 + expectedContent: TEST_DATA_TAINTED 1.530 + }, 1.531 + ]; 1.532 +}); 1.533 + 1.534 +/** 1.535 + * Prepares the list of downloads to be added to the database that should 1.536 + * *not* be imported by the import procedure. 1.537 + */ 1.538 +add_task(function prepareNonImportableDownloads() 1.539 +{ 1.540 + gDownloadsRowNonImportable = [ 1.541 + // Download with no source (should never happen in normal circumstances). 1.542 + { 1.543 + source: "", 1.544 + target: "nonimportable1.txt", 1.545 + tempPath: "", 1.546 + startTime: getStartTime(1), 1.547 + state: DOWNLOAD_PAUSED, 1.548 + referrer: "", 1.549 + entityID: "", 1.550 + maxBytes: MAXBYTES_IN_DB, 1.551 + mimeType: "mimeType1", 1.552 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.553 + preferredApplication: "prerredApplication1", 1.554 + autoResume: 1 1.555 + }, 1.556 + 1.557 + // state = DOWNLOAD_FAILED 1.558 + { 1.559 + source: httpUrl("source.txt"), 1.560 + target: "nonimportable2.txt", 1.561 + tempPath: "", 1.562 + startTime: getStartTime(2), 1.563 + state: DOWNLOAD_FAILED, 1.564 + referrer: "", 1.565 + entityID: "", 1.566 + maxBytes: MAXBYTES_IN_DB, 1.567 + mimeType: "mimeType2", 1.568 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.569 + preferredApplication: "prerredApplication2", 1.570 + autoResume: 1 1.571 + }, 1.572 + 1.573 + // state = DOWNLOAD_CANCELED 1.574 + { 1.575 + source: httpUrl("source.txt"), 1.576 + target: "nonimportable3.txt", 1.577 + tempPath: "", 1.578 + startTime: getStartTime(3), 1.579 + state: DOWNLOAD_CANCELED, 1.580 + referrer: "", 1.581 + entityID: "", 1.582 + maxBytes: MAXBYTES_IN_DB, 1.583 + mimeType: "mimeType3", 1.584 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.585 + preferredApplication: "prerredApplication3", 1.586 + autoResume: 1 1.587 + }, 1.588 + 1.589 + // state = DOWNLOAD_BLOCKED_PARENTAL 1.590 + { 1.591 + source: httpUrl("source.txt"), 1.592 + target: "nonimportable4.txt", 1.593 + tempPath: "", 1.594 + startTime: getStartTime(4), 1.595 + state: DOWNLOAD_BLOCKED_PARENTAL, 1.596 + referrer: "", 1.597 + entityID: "", 1.598 + maxBytes: MAXBYTES_IN_DB, 1.599 + mimeType: "mimeType4", 1.600 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.601 + preferredApplication: "prerredApplication4", 1.602 + autoResume: 1 1.603 + }, 1.604 + 1.605 + // state = DOWNLOAD_SCANNING 1.606 + { 1.607 + source: httpUrl("source.txt"), 1.608 + target: "nonimportable5.txt", 1.609 + tempPath: "", 1.610 + startTime: getStartTime(5), 1.611 + state: DOWNLOAD_SCANNING, 1.612 + referrer: "", 1.613 + entityID: "", 1.614 + maxBytes: MAXBYTES_IN_DB, 1.615 + mimeType: "mimeType5", 1.616 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.617 + preferredApplication: "prerredApplication5", 1.618 + autoResume: 1 1.619 + }, 1.620 + 1.621 + // state = DOWNLOAD_DIRTY 1.622 + { 1.623 + source: httpUrl("source.txt"), 1.624 + target: "nonimportable6.txt", 1.625 + tempPath: "", 1.626 + startTime: getStartTime(6), 1.627 + state: DOWNLOAD_DIRTY, 1.628 + referrer: "", 1.629 + entityID: "", 1.630 + maxBytes: MAXBYTES_IN_DB, 1.631 + mimeType: "mimeType6", 1.632 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.633 + preferredApplication: "prerredApplication6", 1.634 + autoResume: 1 1.635 + }, 1.636 + 1.637 + // state = DOWNLOAD_BLOCKED_POLICY 1.638 + { 1.639 + source: httpUrl("source.txt"), 1.640 + target: "nonimportable7.txt", 1.641 + tempPath: "", 1.642 + startTime: getStartTime(7), 1.643 + state: DOWNLOAD_BLOCKED_POLICY, 1.644 + referrer: "", 1.645 + entityID: "", 1.646 + maxBytes: MAXBYTES_IN_DB, 1.647 + mimeType: "mimeType7", 1.648 + preferredAction: Ci.nsIMIMEInfo.saveToDisk, 1.649 + preferredApplication: "prerredApplication7", 1.650 + autoResume: 1 1.651 + }, 1.652 + ]; 1.653 +}); 1.654 + 1.655 +//////////////////////////////////////////////////////////////////////////////// 1.656 +//// Test 1.657 + 1.658 +/** 1.659 + * Creates a temporary Sqlite database with download data and perform an 1.660 + * import of that data to the new Downloads API to verify that the import 1.661 + * worked correctly. 1.662 + */ 1.663 +add_task(function test_downloadImport() 1.664 +{ 1.665 + let connection = null; 1.666 + let downloadsSqlite = getTempFile("downloads.sqlite").path; 1.667 + 1.668 + try { 1.669 + // Set up the database. 1.670 + connection = yield promiseEmptyDatabaseConnection({ 1.671 + aPath: downloadsSqlite, 1.672 + aSchemaVersion: 9 1.673 + }); 1.674 + 1.675 + // Insert both the importable and non-importable 1.676 + // downloads together. 1.677 + for (let downloadRow of gDownloadsRowToImport) { 1.678 + yield promiseInsertRow(connection, downloadRow); 1.679 + } 1.680 + 1.681 + for (let downloadRow of gDownloadsRowNonImportable) { 1.682 + yield promiseInsertRow(connection, downloadRow); 1.683 + } 1.684 + 1.685 + // Check that every item was inserted. 1.686 + do_check_eq((yield promiseTableCount(connection)), 1.687 + gDownloadsRowToImport.length + 1.688 + gDownloadsRowNonImportable.length); 1.689 + } finally { 1.690 + // Close the connection so that DownloadImport can open it. 1.691 + yield connection.close(); 1.692 + } 1.693 + 1.694 + // Import items. 1.695 + let list = yield promiseNewList(false); 1.696 + yield new DownloadImport(list, downloadsSqlite).import(); 1.697 + let items = yield list.getAll(); 1.698 + 1.699 + do_check_eq(items.length, gDownloadsRowToImport.length); 1.700 + 1.701 + for (let i = 0; i < gDownloadsRowToImport.length; i++) { 1.702 + yield checkDownload(items[i], gDownloadsRowToImport[i]); 1.703 + } 1.704 +})