Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 4 | http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 5 | |
michael@0 | 6 | "use strict"; |
michael@0 | 7 | |
michael@0 | 8 | /** |
michael@0 | 9 | * Provides infrastructure for automated download components tests. |
michael@0 | 10 | * (adapted from browser/component/downloads test's head.js) |
michael@0 | 11 | */ |
michael@0 | 12 | |
michael@0 | 13 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 14 | //// Globals |
michael@0 | 15 | |
michael@0 | 16 | XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", |
michael@0 | 17 | "resource://gre/modules/FileUtils.jsm"); |
michael@0 | 18 | |
michael@0 | 19 | const nsIDM = Ci.nsIDownloadManager; |
michael@0 | 20 | |
michael@0 | 21 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 22 | // Test Helpers |
michael@0 | 23 | |
michael@0 | 24 | var { spawn } = Task; |
michael@0 | 25 | |
michael@0 | 26 | function equalStrings(){ |
michael@0 | 27 | let ref = ""+arguments[0]; |
michael@0 | 28 | for (let i=1; i<arguments.length; i++){ |
michael@0 | 29 | if (ref !== ""+arguments[i]) { |
michael@0 | 30 | info("equalStrings failure: " + ref + " != " + arguments[i]); |
michael@0 | 31 | return false |
michael@0 | 32 | } |
michael@0 | 33 | } |
michael@0 | 34 | return true; |
michael@0 | 35 | } |
michael@0 | 36 | |
michael@0 | 37 | function equalNumbers(){ |
michael@0 | 38 | let ref = Number(arguments[0]); |
michael@0 | 39 | for (let i=1; i<arguments.length; i++){ |
michael@0 | 40 | if (ref !== Number(arguments[i])) return false; |
michael@0 | 41 | if (ref !== Number(arguments[i])) { |
michael@0 | 42 | info("equalNumbers failure: " + ref + " != " + Number(arguments[i])); |
michael@0 | 43 | return false |
michael@0 | 44 | } |
michael@0 | 45 | } |
michael@0 | 46 | return true; |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | function getPromisedDbResult(aStatement) { |
michael@0 | 50 | let dbConnection = MetroDownloadsView.manager.DBConnection; |
michael@0 | 51 | let statement = ("string" == typeof aStatement) ? |
michael@0 | 52 | dbConnection.createAsyncStatement( |
michael@0 | 53 | aStatement |
michael@0 | 54 | ) : aStatement; |
michael@0 | 55 | |
michael@0 | 56 | let deferred = Promise.defer(), |
michael@0 | 57 | resultRows = [], |
michael@0 | 58 | err = null; |
michael@0 | 59 | try { |
michael@0 | 60 | statement.executeAsync({ |
michael@0 | 61 | handleResult: function(aResultSet) { |
michael@0 | 62 | let row; |
michael@0 | 63 | if(!aResultSet) { |
michael@0 | 64 | return; |
michael@0 | 65 | } |
michael@0 | 66 | while ((row = aResultSet.getNextRow())){ |
michael@0 | 67 | resultRows.push(row); |
michael@0 | 68 | } |
michael@0 | 69 | }, |
michael@0 | 70 | handleError: function(aError) { |
michael@0 | 71 | Cu.reportError(aError); |
michael@0 | 72 | err = aError; |
michael@0 | 73 | }, |
michael@0 | 74 | handleCompletion: function(){ |
michael@0 | 75 | if (err) { |
michael@0 | 76 | deferred.reject(err); |
michael@0 | 77 | } else { |
michael@0 | 78 | deferred.resolve(resultRows); |
michael@0 | 79 | } |
michael@0 | 80 | } |
michael@0 | 81 | }); |
michael@0 | 82 | } finally { |
michael@0 | 83 | statement.finalize(); |
michael@0 | 84 | } |
michael@0 | 85 | return deferred.promise; |
michael@0 | 86 | } |
michael@0 | 87 | |
michael@0 | 88 | let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]); |
michael@0 | 89 | gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); |
michael@0 | 90 | registerCleanupFunction(function () { |
michael@0 | 91 | gTestTargetFile.remove(false); |
michael@0 | 92 | PanelUI.hide(); |
michael@0 | 93 | }); |
michael@0 | 94 | |
michael@0 | 95 | /** |
michael@0 | 96 | * This object contains a property for each column in the downloads table. |
michael@0 | 97 | */ |
michael@0 | 98 | let gDownloadRowTemplate = { |
michael@0 | 99 | name: "test-download.txt", |
michael@0 | 100 | source: "http://www.example.com/test-download.txt", |
michael@0 | 101 | target: NetUtil.newURI(gTestTargetFile).spec, |
michael@0 | 102 | startTime: 1180493839859230, |
michael@0 | 103 | endTime: 1180493839859234, |
michael@0 | 104 | state: nsIDM.DOWNLOAD_FINISHED, |
michael@0 | 105 | currBytes: 0, |
michael@0 | 106 | maxBytes: -1, |
michael@0 | 107 | preferredAction: 0, |
michael@0 | 108 | autoResume: 0 |
michael@0 | 109 | }; |
michael@0 | 110 | |
michael@0 | 111 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 112 | // Test Infrastructure |
michael@0 | 113 | |
michael@0 | 114 | function test() { |
michael@0 | 115 | runTests(); |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | ///////////////////////////////////// |
michael@0 | 119 | // shared test setup |
michael@0 | 120 | function resetDownloads(){ |
michael@0 | 121 | // clear out existing and any pending downloads in the db |
michael@0 | 122 | // returns a promise |
michael@0 | 123 | |
michael@0 | 124 | let promisedResult = getPromisedDbResult( |
michael@0 | 125 | "DELETE FROM moz_downloads" |
michael@0 | 126 | ); |
michael@0 | 127 | return promisedResult.then(function(aResult){ |
michael@0 | 128 | // // Reset any prefs that might have been changed. |
michael@0 | 129 | // Services.prefs.clearUserPref("browser.download.panel.shown"); |
michael@0 | 130 | |
michael@0 | 131 | // Ensure that data is unloaded. |
michael@0 | 132 | let dlMgr = MetroDownloadsView.manager; |
michael@0 | 133 | let dlsToRemove = []; |
michael@0 | 134 | // Clear all completed/cancelled downloads |
michael@0 | 135 | dlMgr.cleanUp(); |
michael@0 | 136 | dlMgr.cleanUpPrivate(); |
michael@0 | 137 | |
michael@0 | 138 | // Queue up all active ones as well |
michael@0 | 139 | for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) { |
michael@0 | 140 | while (dlsEnum.hasMoreElements()) { |
michael@0 | 141 | dlsToRemove.push(dlsEnum.next()); |
michael@0 | 142 | } |
michael@0 | 143 | } |
michael@0 | 144 | // Remove any queued up active downloads |
michael@0 | 145 | dlsToRemove.forEach(function (dl) { |
michael@0 | 146 | dl.remove(); |
michael@0 | 147 | }); |
michael@0 | 148 | }); |
michael@0 | 149 | } |
michael@0 | 150 | |
michael@0 | 151 | function addDownloadRow(aDataRow) { |
michael@0 | 152 | let deferredInsert = Promise.defer(); |
michael@0 | 153 | let dataRow = aDataRow; |
michael@0 | 154 | |
michael@0 | 155 | let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); |
michael@0 | 156 | let db = dm.DBConnection; |
michael@0 | 157 | |
michael@0 | 158 | let columnNames = Object.keys(gDownloadRowTemplate).join(", "); |
michael@0 | 159 | let parameterNames = Object.keys(gDownloadRowTemplate) |
michael@0 | 160 | .map(function(n) ":" + n) |
michael@0 | 161 | .join(", "); |
michael@0 | 162 | |
michael@0 | 163 | let statement = db.createAsyncStatement( |
michael@0 | 164 | "INSERT INTO moz_downloads (" + columnNames + |
michael@0 | 165 | ", guid) VALUES(" + parameterNames + ", GENERATE_GUID())"); |
michael@0 | 166 | |
michael@0 | 167 | // Populate insert parameters from the provided data. |
michael@0 | 168 | for (let columnName in gDownloadRowTemplate) { |
michael@0 | 169 | if (!(columnName in dataRow)) { |
michael@0 | 170 | // Update the provided row object with data from the global template, |
michael@0 | 171 | // for columns whose value is not provided explicitly. |
michael@0 | 172 | dataRow[columnName] = gDownloadRowTemplate[columnName]; |
michael@0 | 173 | } |
michael@0 | 174 | statement.params[columnName] = dataRow[columnName]; |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | // Run the statement asynchronously and wait. |
michael@0 | 178 | let promisedDownloads = getPromisedDbResult( |
michael@0 | 179 | statement |
michael@0 | 180 | ); |
michael@0 | 181 | yield promisedDownloads.then(function(){ |
michael@0 | 182 | let newItemId = db.lastInsertRowID; |
michael@0 | 183 | let download = dm.getDownload(newItemId); |
michael@0 | 184 | deferredInsert.resolve(download); |
michael@0 | 185 | }); |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | function gen_addDownloadRows(aDataRows){ |
michael@0 | 189 | if (!aDataRows.length) { |
michael@0 | 190 | yield null; |
michael@0 | 191 | } |
michael@0 | 192 | |
michael@0 | 193 | try { |
michael@0 | 194 | // Add each of the provided downloads in reverse. |
michael@0 | 195 | for (let i = aDataRows.length - 1; i >= 0; i--) { |
michael@0 | 196 | let dataRow = aDataRows[i]; |
michael@0 | 197 | let download = yield addDownloadRow(dataRow); |
michael@0 | 198 | |
michael@0 | 199 | // At each iteration, ensure that the start and end time in the global |
michael@0 | 200 | // template is distinct, as these column are used to sort each download |
michael@0 | 201 | // in its category. |
michael@0 | 202 | gDownloadRowTemplate.startTime++; |
michael@0 | 203 | gDownloadRowTemplate.endTime++; |
michael@0 | 204 | } |
michael@0 | 205 | } finally { |
michael@0 | 206 | info("gen_addDownloadRows, finally"); |
michael@0 | 207 | } |
michael@0 | 208 | } |
michael@0 | 209 | |
michael@0 | 210 | ///////////////////////////////////// |
michael@0 | 211 | // Test implementations |
michael@0 | 212 | |
michael@0 | 213 | gTests.push({ |
michael@0 | 214 | desc: "zero downloads", |
michael@0 | 215 | run: function () { |
michael@0 | 216 | yield resetDownloads(); |
michael@0 | 217 | todo(false, "Test there are no visible notifications with an empty db."); |
michael@0 | 218 | } |
michael@0 | 219 | }); |
michael@0 | 220 | |
michael@0 | 221 | /** |
michael@0 | 222 | * Make sure the downloads panel can display items in the right order and |
michael@0 | 223 | * contains the expected data. |
michael@0 | 224 | */ |
michael@0 | 225 | gTests.push({ |
michael@0 | 226 | desc: "Show downloads", |
michael@0 | 227 | run: function(){ |
michael@0 | 228 | // Display one of each download state. |
michael@0 | 229 | let DownloadData = [ |
michael@0 | 230 | { endTime: 1180493839859239, state: nsIDM.DOWNLOAD_NOTSTARTED }, |
michael@0 | 231 | { endTime: 1180493839859238, state: nsIDM.DOWNLOAD_DOWNLOADING }, |
michael@0 | 232 | { endTime: 1180493839859237, state: nsIDM.DOWNLOAD_PAUSED }, |
michael@0 | 233 | { endTime: 1180493839859236, state: nsIDM.DOWNLOAD_SCANNING }, |
michael@0 | 234 | { endTime: 1180493839859235, state: nsIDM.DOWNLOAD_QUEUED }, |
michael@0 | 235 | { endTime: 1180493839859234, state: nsIDM.DOWNLOAD_FINISHED }, |
michael@0 | 236 | { endTime: 1180493839859233, state: nsIDM.DOWNLOAD_FAILED }, |
michael@0 | 237 | { endTime: 1180493839859232, state: nsIDM.DOWNLOAD_CANCELED }, |
michael@0 | 238 | { endTime: 1180493839859231, state: nsIDM.DOWNLOAD_BLOCKED_PARENTAL }, |
michael@0 | 239 | { endTime: 1180493839859230, state: nsIDM.DOWNLOAD_DIRTY }, |
michael@0 | 240 | { endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY } |
michael@0 | 241 | ]; |
michael@0 | 242 | |
michael@0 | 243 | yield resetDownloads(); |
michael@0 | 244 | |
michael@0 | 245 | try { |
michael@0 | 246 | // Populate the downloads database with the data required by this test. |
michael@0 | 247 | // we're going to add stuff to the downloads db. |
michael@0 | 248 | yield spawn( gen_addDownloadRows( DownloadData ) ); |
michael@0 | 249 | |
michael@0 | 250 | todo( false, "Check that MetroDownloadsView._progressNotificationInfo and MetroDownloadsView._downloadCount \ |
michael@0 | 251 | have the correct length (DownloadData.length) \ |
michael@0 | 252 | May also test that the correct notifications show up for various states."); |
michael@0 | 253 | |
michael@0 | 254 | todo(false, "Iterate through download objects in MetroDownloadsView._progressNotificationInfo \ |
michael@0 | 255 | and confirm that the downloads they refer to are the same as those in \ |
michael@0 | 256 | DownloadData."); |
michael@0 | 257 | } catch(e) { |
michael@0 | 258 | info("Show downloads, some error: " + e); |
michael@0 | 259 | } |
michael@0 | 260 | finally { |
michael@0 | 261 | // Clean up when the test finishes. |
michael@0 | 262 | yield resetDownloads(); |
michael@0 | 263 | } |
michael@0 | 264 | } |
michael@0 | 265 | }); |
michael@0 | 266 | |
michael@0 | 267 | /** |
michael@0 | 268 | * Make sure the downloads can be removed with the expected result on the notifications |
michael@0 | 269 | */ |
michael@0 | 270 | gTests.push({ |
michael@0 | 271 | desc: "Remove downloads", |
michael@0 | 272 | run: function(){ |
michael@0 | 273 | // Push a few items into the downloads db. |
michael@0 | 274 | let DownloadData = [ |
michael@0 | 275 | { endTime: 1180493839859239, state: nsIDM.DOWNLOAD_FINISHED }, |
michael@0 | 276 | { endTime: 1180493839859238, state: nsIDM.DOWNLOAD_FINISHED }, |
michael@0 | 277 | { endTime: 1180493839859237, state: nsIDM.DOWNLOAD_FINISHED } |
michael@0 | 278 | ]; |
michael@0 | 279 | |
michael@0 | 280 | yield resetDownloads(); |
michael@0 | 281 | |
michael@0 | 282 | try { |
michael@0 | 283 | // Populate the downloads database with the data required by this test. |
michael@0 | 284 | yield spawn( gen_addDownloadRows( DownloadData ) ); |
michael@0 | 285 | |
michael@0 | 286 | let downloadRows = null, |
michael@0 | 287 | promisedDownloads; |
michael@0 | 288 | // get all the downloads from the db |
michael@0 | 289 | promisedDownloads = getPromisedDbResult( |
michael@0 | 290 | "SELECT guid " |
michael@0 | 291 | + "FROM moz_downloads " |
michael@0 | 292 | + "ORDER BY startTime DESC" |
michael@0 | 293 | ).then(function(aRows){ |
michael@0 | 294 | downloadRows = aRows; |
michael@0 | 295 | }, function(aError){ |
michael@0 | 296 | throw aError; |
michael@0 | 297 | }); |
michael@0 | 298 | yield promisedDownloads; |
michael@0 | 299 | |
michael@0 | 300 | is(downloadRows.length, 3, "Correct number of downloads in the db before removal"); |
michael@0 | 301 | |
michael@0 | 302 | todo(false, "Get some download from MetroDownloadsView._progressNotificationInfo, \ |
michael@0 | 303 | confirm that its file exists, then remove it."); |
michael@0 | 304 | |
michael@0 | 305 | // remove is async(?), wait a bit |
michael@0 | 306 | yield waitForMs(0); |
michael@0 | 307 | |
michael@0 | 308 | // get all the downloads from the db |
michael@0 | 309 | downloadRows = null; |
michael@0 | 310 | promisedDownloads = getPromisedDbResult( |
michael@0 | 311 | "SELECT guid " |
michael@0 | 312 | + "FROM moz_downloads " |
michael@0 | 313 | + "ORDER BY startTime DESC" |
michael@0 | 314 | ).then(function(aRows){ |
michael@0 | 315 | downloadRows = aRows; |
michael@0 | 316 | }, function(aError){ |
michael@0 | 317 | throw aError; |
michael@0 | 318 | }); |
michael@0 | 319 | yield promisedDownloads; |
michael@0 | 320 | |
michael@0 | 321 | todo(false, "confirm that the removed download is no longer in the database \ |
michael@0 | 322 | and its file no longer exists."); |
michael@0 | 323 | |
michael@0 | 324 | } catch(e) { |
michael@0 | 325 | info("Remove downloads, some error: " + e); |
michael@0 | 326 | } |
michael@0 | 327 | finally { |
michael@0 | 328 | // Clean up when the test finishes. |
michael@0 | 329 | yield resetDownloads(); |
michael@0 | 330 | } |
michael@0 | 331 | } |
michael@0 | 332 | }); |
michael@0 | 333 | |
michael@0 | 334 | /** |
michael@0 | 335 | * Make sure the cancelled/aborted downloads are handled correctly. |
michael@0 | 336 | */ |
michael@0 | 337 | gTests.push({ |
michael@0 | 338 | desc: "Cancel/Abort Downloads", |
michael@0 | 339 | run: function(){ |
michael@0 | 340 | todo(false, "Ensure that a cancelled/aborted download is in the correct state \ |
michael@0 | 341 | including correct values for state variables (e.g. _downloadCount, _downloadsInProgress) \ |
michael@0 | 342 | and the existence of the downloaded file."); |
michael@0 | 343 | } |
michael@0 | 344 | }); |
michael@0 | 345 | |
michael@0 | 346 | /** |
michael@0 | 347 | * Make sure download notifications are moved when we close tabs. |
michael@0 | 348 | */ |
michael@0 | 349 | gTests.push({ |
michael@0 | 350 | desc: "Download notifications in closed tabs", |
michael@0 | 351 | setUp: function() { |
michael@0 | 352 | // put up a couple notifications on the initial tab |
michael@0 | 353 | let notificationBox = Browser.getNotificationBox(); |
michael@0 | 354 | notificationBox.appendNotification("not important", "low-priority-thing", "", notificationBox.PRIORITY_INFO_LOW, []); |
michael@0 | 355 | notificationBox.appendNotification("so important", "high-priority-thing", "", notificationBox.PRIORITY_CRITICAL_HIGH, []); |
michael@0 | 356 | |
michael@0 | 357 | // open a new tab where we'll conduct the test |
michael@0 | 358 | yield addTab("about:mozilla"); |
michael@0 | 359 | }, |
michael@0 | 360 | run: function(){ |
michael@0 | 361 | let notificationBox = Browser.getNotificationBox(); |
michael@0 | 362 | let notn = MetroDownloadsView.showNotification("download-progress", "test message", [], |
michael@0 | 363 | notificationBox.PRIORITY_WARNING_LOW); |
michael@0 | 364 | Browser.closeTab(Browser.selectedTab); |
michael@0 | 365 | |
michael@0 | 366 | yield waitForEvent(Elements.tabList, "TabRemove"); |
michael@0 | 367 | |
michael@0 | 368 | // expected behavior when a tab is closed while a download notification is showing: |
michael@0 | 369 | // * the notification remains visible as long as a next tab/browser exists |
michael@0 | 370 | // * normal rules about priority apply |
michael@0 | 371 | // * notifications - including any pre-existing ones - display in expected order |
michael@0 | 372 | let nextBox = Browser.getNotificationBox(); |
michael@0 | 373 | let currentNotification; |
michael@0 | 374 | |
michael@0 | 375 | ok(nextBox.getNotificationWithValue("download-progress"), "notification was moved to next tab"); |
michael@0 | 376 | |
michael@0 | 377 | currentNotification = nextBox.currentNotification; |
michael@0 | 378 | is(currentNotification.value, "high-priority-thing", "high priority notification is current"); |
michael@0 | 379 | currentNotification.close(); |
michael@0 | 380 | |
michael@0 | 381 | currentNotification = nextBox.currentNotification; |
michael@0 | 382 | is(currentNotification.value, "download-progress", "download notification is next"); |
michael@0 | 383 | currentNotification.close(); |
michael@0 | 384 | |
michael@0 | 385 | currentNotification = nextBox.currentNotification; |
michael@0 | 386 | is(currentNotification.value, "low-priority-thing", "low priority notification is next"); |
michael@0 | 387 | currentNotification.close(); |
michael@0 | 388 | } |
michael@0 | 389 | }); |