toolkit/components/jsdownloads/test/unit/test_DownloadList.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* Any copyright is dedicated to the Public Domain.
     4  * http://creativecommons.org/publicdomain/zero/1.0/ */
     6 /**
     7  * Tests the DownloadList object.
     8  */
    10 "use strict";
    12 ////////////////////////////////////////////////////////////////////////////////
    13 //// Globals
    15 /**
    16  * Returns a PRTime in the past usable to add expirable visits.
    17  *
    18  * @note Expiration ignores any visit added in the last 7 days, but it's
    19  *       better be safe against DST issues, by going back one day more.
    20  */
    21 function getExpirablePRTime()
    22 {
    23   let dateObj = new Date();
    24   // Normalize to midnight
    25   dateObj.setHours(0);
    26   dateObj.setMinutes(0);
    27   dateObj.setSeconds(0);
    28   dateObj.setMilliseconds(0);
    29   dateObj = new Date(dateObj.getTime() - 8 * 86400000);
    30   return dateObj.getTime() * 1000;
    31 }
    33 /**
    34  * Adds an expirable history visit for a download.
    35  *
    36  * @param aSourceUrl
    37  *        String containing the URI for the download source, or null to use
    38  *        httpUrl("source.txt").
    39  *
    40  * @return {Promise}
    41  * @rejects JavaScript exception.
    42  */
    43 function promiseExpirableDownloadVisit(aSourceUrl)
    44 {
    45   let deferred = Promise.defer();
    46   PlacesUtils.asyncHistory.updatePlaces(
    47     {
    48       uri: NetUtil.newURI(aSourceUrl || httpUrl("source.txt")),
    49       visits: [{
    50         transitionType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
    51         visitDate: getExpirablePRTime(),
    52       }]
    53     },
    54     {
    55       handleError: function handleError(aResultCode, aPlaceInfo) {
    56         let ex = new Components.Exception("Unexpected error in adding visits.",
    57                                           aResultCode);
    58         deferred.reject(ex);
    59       },
    60       handleResult: function () {},
    61       handleCompletion: function handleCompletion() {
    62         deferred.resolve();
    63       }
    64     });
    65   return deferred.promise;
    66 }
    68 ////////////////////////////////////////////////////////////////////////////////
    69 //// Tests
    71 /**
    72  * Checks the testing mechanism used to build different download lists.
    73  */
    74 add_task(function test_construction()
    75 {
    76   let downloadListOne = yield promiseNewList();
    77   let downloadListTwo = yield promiseNewList();
    78   let privateDownloadListOne = yield promiseNewList(true);
    79   let privateDownloadListTwo = yield promiseNewList(true);
    81   do_check_neq(downloadListOne, downloadListTwo);
    82   do_check_neq(privateDownloadListOne, privateDownloadListTwo);
    83   do_check_neq(downloadListOne, privateDownloadListOne);
    84 });
    86 /**
    87  * Checks the methods to add and retrieve items from the list.
    88  */
    89 add_task(function test_add_getAll()
    90 {
    91   let list = yield promiseNewList();
    93   let downloadOne = yield promiseNewDownload();
    94   yield list.add(downloadOne);
    96   let itemsOne = yield list.getAll();
    97   do_check_eq(itemsOne.length, 1);
    98   do_check_eq(itemsOne[0], downloadOne);
   100   let downloadTwo = yield promiseNewDownload();
   101   yield list.add(downloadTwo);
   103   let itemsTwo = yield list.getAll();
   104   do_check_eq(itemsTwo.length, 2);
   105   do_check_eq(itemsTwo[0], downloadOne);
   106   do_check_eq(itemsTwo[1], downloadTwo);
   108   // The first snapshot should not have been modified.
   109   do_check_eq(itemsOne.length, 1);
   110 });
   112 /**
   113  * Checks the method to remove items from the list.
   114  */
   115 add_task(function test_remove()
   116 {
   117   let list = yield promiseNewList();
   119   yield list.add(yield promiseNewDownload());
   120   yield list.add(yield promiseNewDownload());
   122   let items = yield list.getAll();
   123   yield list.remove(items[0]);
   125   // Removing an item that was never added should not raise an error.
   126   yield list.remove(yield promiseNewDownload());
   128   items = yield list.getAll();
   129   do_check_eq(items.length, 1);
   130 });
   132 /**
   133  * Tests that the "add", "remove", and "getAll" methods on the global
   134  * DownloadCombinedList object combine the contents of the global DownloadList
   135  * objects for public and private downloads.
   136  */
   137 add_task(function test_DownloadCombinedList_add_remove_getAll()
   138 {
   139   let publicList = yield promiseNewList();
   140   let privateList = yield Downloads.getList(Downloads.PRIVATE);
   141   let combinedList = yield Downloads.getList(Downloads.ALL);
   143   let publicDownload = yield promiseNewDownload();
   144   let privateDownload = yield Downloads.createDownload({
   145     source: { url: httpUrl("source.txt"), isPrivate: true },
   146     target: getTempFile(TEST_TARGET_FILE_NAME).path,
   147   });
   149   yield publicList.add(publicDownload);
   150   yield privateList.add(privateDownload);
   152   do_check_eq((yield combinedList.getAll()).length, 2);
   154   yield combinedList.remove(publicDownload);
   155   yield combinedList.remove(privateDownload);
   157   do_check_eq((yield combinedList.getAll()).length, 0);
   159   yield combinedList.add(publicDownload);
   160   yield combinedList.add(privateDownload);
   162   do_check_eq((yield publicList.getAll()).length, 1);
   163   do_check_eq((yield privateList.getAll()).length, 1);
   164   do_check_eq((yield combinedList.getAll()).length, 2);
   166   yield publicList.remove(publicDownload);
   167   yield privateList.remove(privateDownload);
   169   do_check_eq((yield combinedList.getAll()).length, 0);
   170 });
   172 /**
   173  * Checks that views receive the download add and remove notifications, and that
   174  * adding and removing views works as expected, both for a normal and a combined
   175  * list.
   176  */
   177 add_task(function test_notifications_add_remove()
   178 {
   179   for (let isCombined of [false, true]) {
   180     // Force creating a new list for both the public and combined cases.
   181     let list = yield promiseNewList();
   182     if (isCombined) {
   183       list = yield Downloads.getList(Downloads.ALL);
   184     }
   186     let downloadOne = yield promiseNewDownload();
   187     let downloadTwo = yield Downloads.createDownload({
   188       source: { url: httpUrl("source.txt"), isPrivate: true },
   189       target: getTempFile(TEST_TARGET_FILE_NAME).path,
   190     });
   191     yield list.add(downloadOne);
   192     yield list.add(downloadTwo);
   194     // Check that we receive add notifications for existing elements.
   195     let addNotifications = 0;
   196     let viewOne = {
   197       onDownloadAdded: function (aDownload) {
   198         // The first download to be notified should be the first that was added.
   199         if (addNotifications == 0) {
   200           do_check_eq(aDownload, downloadOne);
   201         } else if (addNotifications == 1) {
   202           do_check_eq(aDownload, downloadTwo);
   203         }
   204         addNotifications++;
   205       },
   206     };
   207     yield list.addView(viewOne);
   208     do_check_eq(addNotifications, 2);
   210     // Check that we receive add notifications for new elements.
   211     yield list.add(yield promiseNewDownload());
   212     do_check_eq(addNotifications, 3);
   214     // Check that we receive remove notifications.
   215     let removeNotifications = 0;
   216     let viewTwo = {
   217       onDownloadRemoved: function (aDownload) {
   218         do_check_eq(aDownload, downloadOne);
   219         removeNotifications++;
   220       },
   221     };
   222     yield list.addView(viewTwo);
   223     yield list.remove(downloadOne);
   224     do_check_eq(removeNotifications, 1);
   226     // We should not receive remove notifications after the view is removed.
   227     yield list.removeView(viewTwo);
   228     yield list.remove(downloadTwo);
   229     do_check_eq(removeNotifications, 1);
   231     // We should not receive add notifications after the view is removed.
   232     yield list.removeView(viewOne);
   233     yield list.add(yield promiseNewDownload());
   234     do_check_eq(addNotifications, 3);
   235   }
   236 });
   238 /**
   239  * Checks that views receive the download change notifications, both for a
   240  * normal and a combined list.
   241  */
   242 add_task(function test_notifications_change()
   243 {
   244   for (let isCombined of [false, true]) {
   245     // Force creating a new list for both the public and combined cases.
   246     let list = yield promiseNewList();
   247     if (isCombined) {
   248       list = yield Downloads.getList(Downloads.ALL);
   249     }
   251     let downloadOne = yield promiseNewDownload();
   252     let downloadTwo = yield Downloads.createDownload({
   253       source: { url: httpUrl("source.txt"), isPrivate: true },
   254       target: getTempFile(TEST_TARGET_FILE_NAME).path,
   255     });
   256     yield list.add(downloadOne);
   257     yield list.add(downloadTwo);
   259     // Check that we receive change notifications.
   260     let receivedOnDownloadChanged = false;
   261     yield list.addView({
   262       onDownloadChanged: function (aDownload) {
   263         do_check_eq(aDownload, downloadOne);
   264         receivedOnDownloadChanged = true;
   265       },
   266     });
   267     yield downloadOne.start();
   268     do_check_true(receivedOnDownloadChanged);
   270     // We should not receive change notifications after a download is removed.
   271     receivedOnDownloadChanged = false;
   272     yield list.remove(downloadTwo);
   273     yield downloadTwo.start();
   274     do_check_false(receivedOnDownloadChanged);
   275   }
   276 });
   278 /**
   279  * Checks that the reference to "this" is correct in the view callbacks.
   280  */
   281 add_task(function test_notifications_this()
   282 {
   283   let list = yield promiseNewList();
   285   // Check that we receive change notifications.
   286   let receivedOnDownloadAdded = false;
   287   let receivedOnDownloadChanged = false;
   288   let receivedOnDownloadRemoved = false;
   289   let view = {
   290     onDownloadAdded: function () {
   291       do_check_eq(this, view);
   292       receivedOnDownloadAdded = true;
   293     },
   294     onDownloadChanged: function () {
   295       // Only do this check once.
   296       if (!receivedOnDownloadChanged) {
   297         do_check_eq(this, view);
   298         receivedOnDownloadChanged = true;
   299       }
   300     },
   301     onDownloadRemoved: function () {
   302       do_check_eq(this, view);
   303       receivedOnDownloadRemoved = true;
   304     },
   305   };
   306   yield list.addView(view);
   308   let download = yield promiseNewDownload();
   309   yield list.add(download);
   310   yield download.start();
   311   yield list.remove(download);
   313   // Verify that we executed the checks.
   314   do_check_true(receivedOnDownloadAdded);
   315   do_check_true(receivedOnDownloadChanged);
   316   do_check_true(receivedOnDownloadRemoved);
   317 });
   319 /**
   320  * Checks that download is removed on history expiration.
   321  */
   322 add_task(function test_history_expiration()
   323 {
   324   mustInterruptResponses();
   326   function cleanup() {
   327     Services.prefs.clearUserPref("places.history.expiration.max_pages");
   328   }
   329   do_register_cleanup(cleanup);
   331   // Set max pages to 0 to make the download expire.
   332   Services.prefs.setIntPref("places.history.expiration.max_pages", 0);
   334   let list = yield promiseNewList();
   335   let downloadOne = yield promiseNewDownload();
   336   let downloadTwo = yield promiseNewDownload(httpUrl("interruptible.txt"));
   338   let deferred = Promise.defer();
   339   let removeNotifications = 0;
   340   let downloadView = {
   341     onDownloadRemoved: function (aDownload) {
   342       if (++removeNotifications == 2) {
   343         deferred.resolve();
   344       }
   345     },
   346   };
   347   yield list.addView(downloadView);
   349   // Work with one finished download and one canceled download.
   350   yield downloadOne.start();
   351   downloadTwo.start();
   352   yield downloadTwo.cancel();
   354   // We must replace the visits added while executing the downloads with visits
   355   // that are older than 7 days, otherwise they will not be expired.
   356   yield promiseClearHistory();
   357   yield promiseExpirableDownloadVisit();
   358   yield promiseExpirableDownloadVisit(httpUrl("interruptible.txt"));
   360   // After clearing history, we can add the downloads to be removed to the list.
   361   yield list.add(downloadOne);
   362   yield list.add(downloadTwo);
   364   // Force a history expiration.
   365   Cc["@mozilla.org/places/expiration;1"]
   366     .getService(Ci.nsIObserver).observe(null, "places-debug-start-expiration", -1);
   368   // Wait for both downloads to be removed.
   369   yield deferred.promise;
   371   cleanup();
   372 });
   374 /**
   375  * Checks all downloads are removed after clearing history.
   376  */
   377 add_task(function test_history_clear()
   378 {
   379   let list = yield promiseNewList();
   380   let downloadOne = yield promiseNewDownload();
   381   let downloadTwo = yield promiseNewDownload();
   382   yield list.add(downloadOne);
   383   yield list.add(downloadTwo);
   385   let deferred = Promise.defer();
   386   let removeNotifications = 0;
   387   let downloadView = {
   388     onDownloadRemoved: function (aDownload) {
   389       if (++removeNotifications == 2) {
   390         deferred.resolve();
   391       }
   392     },
   393   };
   394   yield list.addView(downloadView);
   396   yield downloadOne.start();
   397   yield downloadTwo.start();
   399   yield promiseClearHistory();
   401   // Wait for the removal notifications that may still be pending.
   402   yield deferred.promise;
   403 });
   405 /**
   406  * Tests the removeFinished method to ensure that it only removes
   407  * finished downloads.
   408  */
   409 add_task(function test_removeFinished()
   410 {
   411   let list = yield promiseNewList();
   412   let downloadOne = yield promiseNewDownload();
   413   let downloadTwo = yield promiseNewDownload();
   414   let downloadThree = yield promiseNewDownload();
   415   let downloadFour = yield promiseNewDownload();
   416   yield list.add(downloadOne);
   417   yield list.add(downloadTwo);
   418   yield list.add(downloadThree);
   419   yield list.add(downloadFour);
   421   let deferred = Promise.defer();
   422   let removeNotifications = 0;
   423   let downloadView = {
   424     onDownloadRemoved: function (aDownload) {
   425       do_check_true(aDownload == downloadOne ||
   426                     aDownload == downloadTwo ||
   427                     aDownload == downloadThree);
   428       do_check_true(removeNotifications < 3);
   429       if (++removeNotifications == 3) {
   430         deferred.resolve();
   431       }
   432     },
   433   };
   434   yield list.addView(downloadView);
   436   // Start three of the downloads, but don't start downloadTwo, then set
   437   // downloadFour to have partial data. All downloads except downloadFour
   438   // should be removed.
   439   yield downloadOne.start();
   440   yield downloadThree.start();
   441   yield downloadFour.start();
   442   downloadFour.hasPartialData = true;
   444   list.removeFinished();
   445   yield deferred.promise;
   447   let downloads = yield list.getAll()
   448   do_check_eq(downloads.length, 1);
   449 });
   451 /**
   452  * Tests the global DownloadSummary objects for the public, private, and
   453  * combined download lists.
   454  */
   455 add_task(function test_DownloadSummary()
   456 {
   457   mustInterruptResponses();
   459   let publicList = yield promiseNewList();
   460   let privateList = yield Downloads.getList(Downloads.PRIVATE);
   462   let publicSummary = yield Downloads.getSummary(Downloads.PUBLIC);
   463   let privateSummary = yield Downloads.getSummary(Downloads.PRIVATE);
   464   let combinedSummary = yield Downloads.getSummary(Downloads.ALL);
   466   // Add a public download that has succeeded.
   467   let succeededPublicDownload = yield promiseNewDownload();
   468   yield succeededPublicDownload.start();
   469   yield publicList.add(succeededPublicDownload);
   471   // Add a public download that has been canceled midway.
   472   let canceledPublicDownload =
   473       yield promiseNewDownload(httpUrl("interruptible.txt"));
   474   canceledPublicDownload.start();
   475   yield promiseDownloadMidway(canceledPublicDownload);
   476   yield canceledPublicDownload.cancel();
   477   yield publicList.add(canceledPublicDownload);
   479   // Add a public download that is in progress.
   480   let inProgressPublicDownload =
   481       yield promiseNewDownload(httpUrl("interruptible.txt"));
   482   inProgressPublicDownload.start();
   483   yield promiseDownloadMidway(inProgressPublicDownload);
   484   yield publicList.add(inProgressPublicDownload);
   486   // Add a private download that is in progress.
   487   let inProgressPrivateDownload = yield Downloads.createDownload({
   488     source: { url: httpUrl("interruptible.txt"), isPrivate: true },
   489     target: getTempFile(TEST_TARGET_FILE_NAME).path,
   490   });
   491   inProgressPrivateDownload.start();
   492   yield promiseDownloadMidway(inProgressPrivateDownload);
   493   yield privateList.add(inProgressPrivateDownload);
   495   // Verify that the summary includes the total number of bytes and the
   496   // currently transferred bytes only for the downloads that are not stopped.
   497   // For simplicity, we assume that after a download is added to the list, its
   498   // current state is immediately propagated to the summary object, which is
   499   // true in the current implementation, though it is not guaranteed as all the
   500   // download operations may happen asynchronously.
   501   do_check_false(publicSummary.allHaveStopped);
   502   do_check_eq(publicSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
   503   do_check_eq(publicSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
   505   do_check_false(privateSummary.allHaveStopped);
   506   do_check_eq(privateSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
   507   do_check_eq(privateSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
   509   do_check_false(combinedSummary.allHaveStopped);
   510   do_check_eq(combinedSummary.progressTotalBytes, TEST_DATA_SHORT.length * 4);
   511   do_check_eq(combinedSummary.progressCurrentBytes, TEST_DATA_SHORT.length * 2);
   513   yield inProgressPublicDownload.cancel();
   515   // Stopping the download should have excluded it from the summary.
   516   do_check_true(publicSummary.allHaveStopped);
   517   do_check_eq(publicSummary.progressTotalBytes, 0);
   518   do_check_eq(publicSummary.progressCurrentBytes, 0);
   520   do_check_false(privateSummary.allHaveStopped);
   521   do_check_eq(privateSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
   522   do_check_eq(privateSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
   524   do_check_false(combinedSummary.allHaveStopped);
   525   do_check_eq(combinedSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
   526   do_check_eq(combinedSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
   528   yield inProgressPrivateDownload.cancel();
   530   // All the downloads should be stopped now.
   531   do_check_true(publicSummary.allHaveStopped);
   532   do_check_eq(publicSummary.progressTotalBytes, 0);
   533   do_check_eq(publicSummary.progressCurrentBytes, 0);
   535   do_check_true(privateSummary.allHaveStopped);
   536   do_check_eq(privateSummary.progressTotalBytes, 0);
   537   do_check_eq(privateSummary.progressCurrentBytes, 0);
   539   do_check_true(combinedSummary.allHaveStopped);
   540   do_check_eq(combinedSummary.progressTotalBytes, 0);
   541   do_check_eq(combinedSummary.progressCurrentBytes, 0);
   542 });
   544 /**
   545  * Checks that views receive the summary change notification.  This is tested on
   546  * the combined summary when adding a public download, as we assume that if we
   547  * pass the test in this case we will also pass it in the others.
   548  */
   549 add_task(function test_DownloadSummary_notifications()
   550 {
   551   let list = yield promiseNewList();
   552   let summary = yield Downloads.getSummary(Downloads.ALL);
   554   let download = yield promiseNewDownload();
   555   yield list.add(download);
   557   // Check that we receive change notifications.
   558   let receivedOnSummaryChanged = false;
   559   yield summary.addView({
   560     onSummaryChanged: function () {
   561       receivedOnSummaryChanged = true;
   562     },
   563   });
   564   yield download.start();
   565   do_check_true(receivedOnSummaryChanged);
   566 });
   568 ////////////////////////////////////////////////////////////////////////////////
   569 //// Termination
   571 let tailFile = do_get_file("tail.js");
   572 Services.scriptloader.loadSubScript(NetUtil.newURI(tailFile).spec);

mercurial