Bug 1094312 - Fix browser_bug553455.js:test_cancel_restart by pausing the download for long enough for the progress notification to show reliably. r=Gijs, a=sledru
authorDave Townsend <dtownsend@oxymoronical.com>
Fri, 26 Dec 2014 14:06:43 -0800
changeset 242843 b71146fc0e37
parent 242842 fc494bb31bec
child 242844 6796cf5b59b1
push id4320
push userryanvm@gmail.com
push date2015-01-14 14:48 +0000
treeherdermozilla-beta@06bb4d89e2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs, sledru
bugs1094312, 553455
milestone36.0
Bug 1094312 - Fix browser_bug553455.js:test_cancel_restart by pausing the download for long enough for the progress notification to show reliably. r=Gijs, a=sledru test_cancel_restart needs the progress notification to show reliably so it can cancel the pending install. To ensure this a sjs script is used to asynchronously deliever the XPI data. It starts responding but sends no data until a second request is made to tell it to complete. So the test can start the install, then do what it likes with the progress dialog before finally telling the server to complete the download for the install.
browser/base/content/test/general/browser_bug553455.js
toolkit/mozapps/extensions/test/xpinstall/browser.ini
toolkit/mozapps/extensions/test/xpinstall/slowinstall.sjs
--- a/browser/base/content/test/general/browser_bug553455.js
+++ b/browser/base/content/test/general/browser_bug553455.js
@@ -665,82 +665,101 @@ function test_renotify_installed() {
 
   var triggers = encodeURIComponent(JSON.stringify({
     "XPI": "unsigned.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
-/*function test_cancel_restart() {
+function test_cancel_restart() {
+  function complete_install(callback) {
+    let url = TESTROOT + "slowinstall.sjs?continue=true"
+    NetUtil.asyncFetch(url, callback || (() => {}));
+  }
+
   // Wait for the progress notification
   wait_for_notification(PROGRESS_NOTIFICATION, function(aPanel) {
     let notification = aPanel.childNodes[0];
     // Close the notification
     let anchor = document.getElementById("addons-notification-icon");
     anchor.click();
     // Reopen the notification
     anchor.click();
 
     ok(PopupNotifications.isPanelOpen, "Notification should still be open");
     is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
     isnot(notification, aPanel.childNodes[0], "Should have reconstructed the notification UI");
     notification = aPanel.childNodes[0];
     is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
     let button = document.getAnonymousElementByAttribute(notification, "anonid", "cancel");
 
-    // Cancel the download
-    EventUtils.synthesizeMouse(button, 2, 2, {});
+    // Wait for the install to fully cancel
+    let install = notification.notification.options.installs[0];
+    install.addListener({
+      onDownloadCancelled: function() {
+        install.removeListener(this);
 
-    // Notification should have changed to cancelled
-    notification = aPanel.childNodes[0];
-    is(notification.id, "addon-install-cancelled-notification", "Should have seen the cancelled notification");
+        executeSoon(function() {
+          ok(PopupNotifications.isPanelOpen, "Notification should still be open");
+          is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
+          isnot(notification, aPanel.childNodes[0], "Should have reconstructed the notification UI");
+          notification = aPanel.childNodes[0];
+          is(notification.id, "addon-install-cancelled-notification", "Should have seen the cancelled notification");
+
+          // Wait for the install confirmation dialog
+          wait_for_install_dialog(function(aWindow) {
+            // Wait for the complete notification
+            wait_for_notification("addon-install-complete-notification", function(aPanel) {
+              let notification = aPanel.childNodes[0];
+              is(notification.button.label, "Restart Now", "Should have seen the right button");
+              is(notification.getAttribute("label"),
+                 "XPI Test will be installed after you restart " + gApp + ".",
+                 "Should have seen the right message");
 
-    // Wait for the install confirmation dialog
-    wait_for_install_dialog(function(aWindow) {
-      // Wait for the complete notification
-      wait_for_notification("addon-install-complete-notification", function(aPanel) {
-        let notification = aPanel.childNodes[0];
-        is(notification.button.label, "Restart Now", "Should have seen the right button");
-        is(notification.getAttribute("label"),
-           "XPI Test will be installed after you restart " + gApp + ".",
-           "Should have seen the right message");
+              AddonManager.getAllInstalls(function(aInstalls) {
+                is(aInstalls.length, 1, "Should be one pending install");
+                aInstalls[0].cancel();
+
+                Services.perms.remove("example.com", "install");
+                wait_for_notification_close(runNextTest);
+                gBrowser.removeTab(gBrowser.selectedTab);
+              });
+            });
+
+            aWindow.document.documentElement.acceptDialog();
+          });
 
-        AddonManager.getAllInstalls(function(aInstalls) {
-          is(aInstalls.length, 1, "Should be one pending install");
-          aInstalls[0].cancel();
+          // Restart the download
+          EventUtils.synthesizeMouseAtCenter(notification.button, {});
 
-          Services.perms.remove("example.com", "install");
-          wait_for_notification_close(runNextTest);
-          gBrowser.removeTab(gBrowser.selectedTab);
+          // Should be back to a progress notification
+          ok(PopupNotifications.isPanelOpen, "Notification should still be open");
+          is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
+          notification = aPanel.childNodes[0];
+          is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
+
+          complete_install();
         });
-      });
-
-      aWindow.document.documentElement.acceptDialog();
+      }
     });
 
-    // Restart the download
-    EventUtils.synthesizeMouse(notification.button, 20, 10, {});
-
-    // Should be back to a progress notification
-    ok(PopupNotifications.isPanelOpen, "Notification should still be open");
-    is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
-    notification = aPanel.childNodes[0];
-    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
+    // Cancel the download
+    EventUtils.synthesizeMouseAtCenter(button, {});
   });
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
-    "XPI": "unsigned.xpi"
+    "XPI": "slowinstall.sjs?file=unsigned.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-},*/
+},
 
 function test_failed_security() {
   Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
 
   setup_redirect({
     "Location": TESTROOT + "unsigned.xpi"
   });
 
--- a/toolkit/mozapps/extensions/test/xpinstall/browser.ini
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser.ini
@@ -19,16 +19,17 @@ support-files =
   redirect.sjs
   restartless.xpi
   signed-no-cn.xpi
   signed-no-o.xpi
   signed-tampered.xpi
   signed-untrusted.xpi
   signed.xpi
   signed2.xpi
+  slowinstall.sjs
   startsoftwareupdate.html
   theme.xpi
   triggerredirect.html
   unsigned.xpi
 
 [browser_auth.js]
 [browser_auth2.js]
 [browser_auth3.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/slowinstall.sjs
@@ -0,0 +1,101 @@
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+const RELATIVE_PATH = "browser/toolkit/mozapps/extensions/test/xpinstall"
+const NOTIFICATION_TOPIC = "slowinstall-complete";
+
+/**
+ * Helper function to create a JS object representing the url parameters from
+ * the request's queryString.
+ *
+ * @param  aQueryString
+ *         The request's query string.
+ * @return A JS object representing the url parameters from the request's
+ *         queryString.
+ */
+function parseQueryString(aQueryString) {
+  var paramArray = aQueryString.split("&");
+  var regex = /^([^=]+)=(.*)$/;
+  var params = {};
+  for (var i = 0, sz = paramArray.length; i < sz; i++) {
+    var match = regex.exec(paramArray[i]);
+    if (!match)
+      throw "Bad parameter in queryString!  '" + paramArray[i] + "'";
+    params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
+  }
+
+  return params;
+}
+
+function handleRequest(aRequest, aResponse) {
+  let id = +getState("ID");
+  setState("ID", "" + (id + 1));
+
+  function LOG(str) {
+    dump("slowinstall.sjs[" + id + "]: " + str + "\n");
+  }
+
+  aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");
+
+  var params = { };
+  if (aRequest.queryString)
+    params = parseQueryString(aRequest.queryString);
+
+  if (params.file) {
+    let xpiFile = "";
+
+    function complete_download() {
+      LOG("Completing download");
+      downloadPaused = false;
+
+      try {
+        // Doesn't seem to be a sane way to read using OS.File and write to an
+        // nsIOutputStream so here we are.
+        let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+        file.initWithPath(xpiFile);
+        let stream = Cc["@mozilla.org/network/file-input-stream;1"].
+                     createInstance(Ci.nsIFileInputStream);
+        stream.init(file, -1, -1, stream.DEFER_OPEN + stream.CLOSE_ON_EOF);
+
+        NetUtil.asyncCopy(stream, aResponse.bodyOutputStream, () => {
+          LOG("Download complete");
+          aResponse.finish();
+        });
+      }
+      catch (e) {
+        LOG("Exception " + e);
+      }
+    }
+
+    let waitForComplete = new Promise(resolve => {
+      function complete() {
+        Services.obs.removeObserver(complete, NOTIFICATION_TOPIC);
+        resolve();
+      }
+
+      Services.obs.addObserver(complete, NOTIFICATION_TOPIC, false);
+    });
+
+    aResponse.processAsync();
+
+    OS.File.getCurrentDirectory().then(dir => {
+      xpiFile = OS.Path.join(dir, ...RELATIVE_PATH.split("/"), params.file);
+      LOG("Starting slow download of " + xpiFile);
+
+      OS.File.stat(xpiFile).then(info => {
+        aResponse.setHeader("Content-Type", "binary/octet-stream");
+        aResponse.setHeader("Content-Length", info.size.toString());
+
+        LOG("Download paused");
+        waitForComplete.then(complete_download);
+      });
+    });
+  }
+  else if (params.continue) {
+    dump("slowinstall.sjs: Received signal to complete all current downloads.\n");
+    Services.obs.notifyObservers(null, NOTIFICATION_TOPIC, null);
+  }
+}