Bug 1165061 - Fix Intermittent test_0102_background_restartNotification_staging.xul by not copying app files when staging for tests and enable the tests for Linux and Mac. r=spohl
authorRobert Strong <robert.bugzilla@gmail.com>
Wed, 03 Jun 2015 11:29:51 -0700
changeset 247129 9b110d0cbbf605497bca4c72989c5da6e349269c
parent 247128 2feb31e63a451392b33f0d7f29ff052f846f5810
child 247130 7969c82a42dfacc6b6a1b19d6e10f7b9e1922ea8
push id60612
push userryanvm@gmail.com
push dateThu, 04 Jun 2015 13:28:04 +0000
treeherdermozilla-inbound@dbc89e025b5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersspohl
bugs1165061
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1165061 - Fix Intermittent test_0102_background_restartNotification_staging.xul by not copying app files when staging for tests and enable the tests for Linux and Mac. r=spohl
toolkit/mozapps/update/tests/chrome/chrome.ini
toolkit/mozapps/update/tests/chrome/test_0017_check_staging_basic.xul
toolkit/mozapps/update/tests/chrome/test_0096_restartNotification_stagedBackground.xul
toolkit/mozapps/update/tests/chrome/test_0097_restartNotification_stagedServiceBackground.xul
toolkit/mozapps/update/tests/chrome/test_0102_background_restartNotification_staging.xul
toolkit/mozapps/update/tests/chrome/test_0103_background_restartNotification_stagingService.xul
toolkit/mozapps/update/tests/chrome/test_9999_cleanup.xul
toolkit/mozapps/update/tests/chrome/utils.js
toolkit/mozapps/update/tests/data/shared.js
toolkit/mozapps/update/updater/archivereader.cpp
toolkit/mozapps/update/updater/updater-xpcshell/moz.build
toolkit/mozapps/update/updater/updater.cpp
toolkit/mozapps/update/updater/updater.rc
--- a/toolkit/mozapps/update/tests/chrome/chrome.ini
+++ b/toolkit/mozapps/update/tests/chrome/chrome.ini
@@ -13,18 +13,18 @@ support-files =
 ; order and not in the order specified in the manifest.
 [test_0011_check_basic.xul]
 [test_0012_check_basic_license.xul]
 [test_0013_check_incompat_basic.xul]
 [test_0014_check_incompat_basic_license.xul]
 [test_0015_check_incompat_basic_addons.xul]
 [test_0016_check_incompat_basic_license_addons.xul]
 [test_0017_check_staging_basic.xul]
-skip-if = os != 'win'
-reason = Bug 918029 and bug 1164560 - timeout caused by copying too many files.
+skip-if = asan
+reason = Bug 1168003
 [test_0021_check_billboard.xul]
 [test_0022_check_billboard_license.xul]
 [test_0023_check_incompat_billboard.xul]
 [test_0024_check_incompat_billboard_license.xul]
 [test_0025_check_incompat_billboard_addons.xul]
 [test_0026_check_incompat_billboard_license_addons.xul]
 [test_0031_available_basic.xul]
 [test_0032_available_basic_license.xul]
@@ -55,21 +55,25 @@ reason = Bug 918029 and bug 1164560 - ti
 [test_0083_error_patchApplyFailure_partial_complete.xul]
 [test_0084_error_patchApplyFailure_verify_failed.xul]
 [test_0091_installed.xul]
 [test_0092_finishedBackground.xul]
 [test_0093_restartNotification.xul]
 [test_0094_restartNotification_remote.xul]
 [test_0095_restartNotification_remoteInvalidNumber.xul]
 [test_0096_restartNotification_stagedBackground.xul]
+skip-if = asan
+reason = Bug 1168003
 [test_0097_restartNotification_stagedServiceBackground.xul]
+skip-if = os != 'win'
+reason = only Windows has the maintenance service.
 [test_0101_background_restartNotification.xul]
 [test_0102_background_restartNotification_staging.xul]
-skip-if = os == 'linux'
-reason = Bug 918029 - timeout caused by copying too many files.
+skip-if = asan
+reason = Bug 1168003
 [test_0103_background_restartNotification_stagingService.xul]
 skip-if = os != 'win'
 reason = only Windows has the maintenance service.
 [test_0104_background_restartNotification_NoIncompatAddons.xul]
 [test_0105_background_restartNotification_VersionCompatAddons.xul]
 [test_0111_neverButton_basic.xul]
 [test_0112_neverButton_billboard.xul]
 [test_0113_showNeverForVersionRemovedWithPref.xul]
--- a/toolkit/mozapps/update/tests/chrome/test_0017_check_staging_basic.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0017_check_staging_basic.xul
@@ -26,16 +26,18 @@ const TESTS = [ {
   buttonClick: "next"
 }, {
   pageid: PAGEID_DOWNLOADING
 }, {
   pageid: PAGEID_FINISHED,
   buttonClick: "extra1"
 } ];
 
+gUseTestUpdater = true;
+
 function runTest() {
   debugDump("entering");
 
   Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, true);
 
   let url = URL_HTTP_UPDATE_XML + "?showDetails=1" + getVersionParams();
   setUpdateURLOverride(url);
 
--- a/toolkit/mozapps/update/tests/chrome/test_0096_restartNotification_stagedBackground.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0096_restartNotification_stagedBackground.xul
@@ -19,16 +19,18 @@
 <script type="application/javascript">
 <![CDATA[
 
 const TESTS = [ {
   pageid: PAGEID_FINISHED_BKGRD,
   buttonClick: "extra1"
 } ];
 
+gUseTestUpdater = true;
+
 function runTest() {
   debugDump("entering");
 
   let patches = getLocalPatchString("complete", null, null, null, null, null,
                                     STATE_APPLIED);
   let updates = getLocalUpdateString(patches, null, null, null,
                                      Services.appinfo.version,
                                      Services.appinfo.platformVersion);
--- a/toolkit/mozapps/update/tests/chrome/test_0097_restartNotification_stagedServiceBackground.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0097_restartNotification_stagedServiceBackground.xul
@@ -19,16 +19,18 @@
 <script type="application/javascript">
 <![CDATA[
 
 const TESTS = [ {
   pageid: PAGEID_FINISHED_BKGRD,
   buttonClick: "extra1"
 } ];
 
+gUseTestUpdater = true;
+
 function runTest() {
   debugDump("entering");
 
   let patches = getLocalPatchString("complete", null, null, null, null, null,
                                     STATE_APPLIED_SVC);
   let updates = getLocalUpdateString(patches, null, null, null,
                                      Services.appinfo.version,
                                      Services.appinfo.platformVersion);
--- a/toolkit/mozapps/update/tests/chrome/test_0102_background_restartNotification_staging.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0102_background_restartNotification_staging.xul
@@ -19,16 +19,18 @@
 <script type="application/javascript">
 <![CDATA[
 
 const TESTS = [ {
   pageid: PAGEID_FINISHED_BKGRD,
   buttonClick: "extra1"
 } ];
 
+gUseTestUpdater = true;
+
 function runTest() {
   debugDump("entering");
 
   Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, true);
   Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 1);
 
   let url = URL_HTTP_UPDATE_XML + "?showDetails=1" + getVersionParams();
   setUpdateURLOverride(url);
--- a/toolkit/mozapps/update/tests/chrome/test_0103_background_restartNotification_stagingService.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_0103_background_restartNotification_stagingService.xul
@@ -19,16 +19,18 @@
 <script type="application/javascript">
 <![CDATA[
 
 const TESTS = [ {
   pageid: PAGEID_FINISHED_BKGRD,
   buttonClick: "extra1"
 } ];
 
+gUseTestUpdater = true;
+
 function runTest() {
   debugDump("entering");
 
   Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, true);
   Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, true);
   Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 1);
 
   let url = URL_HTTP_UPDATE_XML + "?showDetails=1" + getVersionParams();
--- a/toolkit/mozapps/update/tests/chrome/test_9999_cleanup.xul
+++ b/toolkit/mozapps/update/tests/chrome/test_9999_cleanup.xul
@@ -76,17 +76,39 @@ function runTest() {
   try {
     removeDirRecursive(addonPrepDir);
   }
   catch (e) {
     logTestInfo("Unable to remove directory. Path: " + addonPrepDir.path +
                 ", Exception: " + e);
   }
 
-  resetAddons(finishTest);
+  resetAddons(cleanupRestoreUpdaterBackup);
+}
+
+/**
+ * After all tests finish this will repeatedly attempt to restore the real
+ * updater if it exists and then call finishTest after the restore is
+ * successful.
+ */
+function cleanupRestoreUpdaterBackup() {
+  debugDump("entering");
+
+  try {
+    // Windows debug builds keep the updater file in use for a short period of
+    // time after the updater process exits.
+    restoreUpdaterBackup();
+  } catch (e) {
+    logTestInfo("Attempt to restore the backed up updater failed... " +
+                "will try again, Exception: " + e);
+    SimpleTest.executeSoon(cleanupRestoreUpdaterBackup);
+    return;
+  }
+
+  SimpleTest.executeSoon(finishTest);
 }
 
 function finishTest() {
   debugDump("entering");
 
   if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_LOG)) {
     Services.prefs.clearUserPref(PREF_APP_UPDATE_LOG);
   }
--- a/toolkit/mozapps/update/tests/chrome/utils.js
+++ b/toolkit/mozapps/update/tests/chrome/utils.js
@@ -211,16 +211,17 @@ var gExtUpdateURL;                // ext
 
 var gTestCounter = -1;
 var gWin;
 var gDocElem;
 var gPrefToCheck;
 var gDisableNoUpdateAddon = false;
 var gDisableUpdateCompatibilityAddon = false;
 var gDisableUpdateVersionAddon = false;
+var gUseTestUpdater = false;
 
 // Set to true to log additional information for debugging. To log additional
 // information for an individual test set DEBUG_AUS_TEST to true in the test's
 // onload function.
 var DEBUG_AUS_TEST = false;
 
 const DATA_URI_SPEC = "chrome://mochitests/content/chrome/toolkit/mozapps/update/tests/data/";
 Services.scriptloader.loadSubScript(DATA_URI_SPEC + "shared.js", this);
@@ -325,19 +326,20 @@ function runTestDefaultWaitForWindowClos
     SimpleTest.executeSoon(runTestDefaultWaitForWindowClosed);
   } else {
     Services.ww.registerNotification(gWindowObserver);
 
     gCloseWindowTimeoutCounter = 0;
 
     setupFiles();
     setupPrefs();
+    gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1");
     removeUpdateDirsAndFiles();
     reloadUpdateManagerData();
-    setupAddons(runTest);
+    setupAddons(setupTestUpdater);
   }
 }
 
 /**
  * Default test finish function that can be used by most tests. This function
  * uses protective measures to prevent the next test from failing provided by
  * |finishTestDefaultWaitForWindowClosed| helper functions to prevent failure
  * due to an update window being left open.
@@ -353,26 +355,27 @@ function finishTestDefault() {
     debugDump("channel = " + gChannel);
     gChannel = null;
     gPrefRoot.removeObserver(PREF_APP_UPDATE_CHANNEL, observer);
   }
 
   verifyTestsRan();
 
   resetPrefs();
+  gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "");
   resetFiles();
   removeUpdateDirsAndFiles();
   reloadUpdateManagerData();
 
   Services.ww.unregisterNotification(gWindowObserver);
   if (gDocElem) {
     gDocElem.removeEventListener("pageshow", onPageShowDefault, false);
   }
 
-  finishTestDefaultWaitForWindowClosed();
+  finishTestRestoreUpdaterBackup();
 }
 
 /**
  * nsITimerCallback for the timeout timer to cleanly finish a test if the Update
  * Window doesn't close for a test. This allows the next test to run properly if
  * a previous test fails.
  *
  * @param  aTimer
@@ -386,25 +389,48 @@ function finishTestTimeout(aTimer) {
     finishTest();
   }
   catch (e) {
     finishTestDefault();
   }
 }
 
 /**
+ * When a test finishes this will repeatedly attempt to restore the real updater
+ * for tests that use the test updater and then call
+ * finishTestDefaultWaitForWindowClosed after the restore is successful.
+ */
+function finishTestRestoreUpdaterBackup() {
+  if (gUseTestUpdater) {
+    try {
+      // Windows debug builds keep the updater file in use for a short period of
+      // time after the updater process exits.
+      restoreUpdaterBackup();
+    } catch (e) {
+      logTestInfo("Attempt to restore the backed up updater failed... " +
+                  "will try again, Exception: " + e);
+      SimpleTest.executeSoon(finishTestRestoreUpdaterBackup);
+      return;
+    }
+  }
+
+  finishTestDefaultWaitForWindowClosed();
+}
+
+/**
  * If an update window is found SimpleTest.executeSoon can callback before the
  * update window is fully closed especially with debug builds. If an update
  * window is found this function will call itself using SimpleTest.executeSoon
  * up to the amount declared in CLOSE_WINDOW_TIMEOUT_MAXCOUNT until the update
  * window has closed before finishing the test.
  */
 function finishTestDefaultWaitForWindowClosed() {
   gCloseWindowTimeoutCounter++;
   if (gCloseWindowTimeoutCounter > CLOSE_WINDOW_TIMEOUT_MAXCOUNT) {
+    SimpleTest.requestCompleteLog();
     SimpleTest.finish();
     return;
   }
 
   // The update window should not be open at this time. If it is the call to
   // |closeUpdateWindow| will close it and cause the test to fail.
   if (closeUpdateWindow()) {
     SimpleTest.executeSoon(finishTestDefaultWaitForWindowClosed);
@@ -747,17 +773,17 @@ function checkRadioGroupSelectedIndex() 
 /**
  * Checks that only incompatible add-ons (e.g. noupdate_X add-ons) that don't
  * have an update are listed in the add-ons incompatible list.
  */
 function checkIncompatbleList() {
   for (let i = 0; i < gIncompatibleListbox.itemCount; i++) {
     let label = gIncompatibleListbox.getItemAtIndex(i).label;
     // Use indexOf since locales can change the text displayed
-    ok(label.indexOf("noupdate") != -1, "Checking that only incompatible " + 
+    ok(label.indexOf("noupdate") != -1, "Checking that only incompatible " +
        "add-ons that don't have an update are listed in the incompatible list");
   }
 }
 
 /**
  * Compares the return value of prefHasUserValue for the preference specified in
  * gPrefToCheck with the value passed in the aPrefHasValue parameter or the
  * value specified in the current test's prefHasUserValue property if
@@ -871,74 +897,125 @@ function verifyTestsRan() {
     let msg = "Checking if TESTS[" + i + "] test was performed... " +
               "callback function name = " + gCallback.name + ", " +
               "pageid = " + test.pageid;
     ok(test.ranTest, msg);
   }
 }
 
 /**
- * Restore the updater that was backed up.  This is called both in setupFiles
- * and resetFiles.  It is called in setupFiles before the backup is done in
- * case the previous test failed.  It is called in resetFiles to put things
- * back to its original state.
- */
-function resetUpdaterBackup() {
-  let baseAppDir = getAppBaseDir();
-  let updater = baseAppDir.clone();
-  let updaterBackup = baseAppDir.clone();
-  updater.append(FILE_UPDATER_BIN);
-  updaterBackup.append(FILE_UPDATER_BIN_BAK);
-  if (updaterBackup.exists()) {
-    if (updater.exists()) {
-      updater.remove(true);
-    }
-    updaterBackup.moveTo(baseAppDir, FILE_UPDATER_BIN);
-  }
-}
-
-/**
  * Creates a backup of files the tests need to modify so they can be restored to
  * the original file when the test has finished and then modifies the files.
  */
 function setupFiles() {
   // Backup the updater-settings.ini file if it exists by moving it.
   let baseAppDir = getAppBaseDir();
   let updateSettingsIni = baseAppDir.clone();
   updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI);
   if (updateSettingsIni.exists()) {
     updateSettingsIni.moveTo(baseAppDir, FILE_UPDATE_SETTINGS_INI_BAK);
   }
   updateSettingsIni = baseAppDir.clone();
   updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI);
   writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
-
-  // Just in case the last test failed, try to reset.
-  resetUpdaterBackup();
+}
 
-  // Move away the real updater
-  let updater = baseAppDir.clone();
-  updater.append(FILE_UPDATER_BIN);
-  updater.moveTo(baseAppDir, FILE_UPDATER_BIN_BAK);
+/**
+ * For tests that use the test updater restores the backed up real updater if
+ * it exists and tries again on failure since Windows debug builds at times
+ * leave the file in use. After success moveRealUpdater is called to continue
+ * the setup of the test updater. For tests that don't use the test updater
+ * runTest will be called.
+ */
+function setupTestUpdater() {
+  if (!gUseTestUpdater) {
+    runTest();
+    return;
+  }
 
-  // Move in the test only updater
-  let testUpdaterDir = Cc["@mozilla.org/file/directory_service;1"].
-    getService(Ci.nsIProperties).
-    get("CurWorkD", Ci.nsILocalFile);
+  try {
+    restoreUpdaterBackup();
+  } catch (e) {
+    logTestInfo("Attempt to restore the backed up updater failed... " +
+                "will try again, Exception: " + e);
+    SimpleTest.executeSoon(setupTestUpdater);
+    return;
+  }
+  moveRealUpdater();
+}
 
-  let relPath = REL_PATH_DATA;
-  let pathParts = relPath.split("/");
-  for (let i = 0; i < pathParts.length; ++i) {
-    testUpdaterDir.append(pathParts[i]);
+/**
+ * Backs up the real updater and tries again on failure since Windows debug
+ * builds at times leave the file in use. After success it will call
+ * copyTestUpdater to continue the setup of the test updater.
+ */
+function moveRealUpdater() {
+  try {
+    // Move away the real updater
+    let baseAppDir = getAppBaseDir();
+    let updater = baseAppDir.clone();
+    updater.append(FILE_UPDATER_BIN);
+    updater.moveTo(baseAppDir, FILE_UPDATER_BIN_BAK);
+  } catch (e) {
+    logTestInfo("Attempt to move the real updater out of the way failed... " +
+                "will try again, Exception: " + e);
+    SimpleTest.executeSoon(moveRealUpdater);
+    return;
   }
 
-  let testUpdater = testUpdaterDir.clone();
-  testUpdater.append(FILE_UPDATER_BIN);
-  if (testUpdater.exists()) {
+  copyTestUpdater();
+}
+
+/**
+ * Copies the test updater so it can be used by tests and tries again on failure
+ * since Windows debug builds at times leave the file in use. After success it
+ * will call runTest to continue the test.
+ */
+function copyTestUpdater() {
+  try {
+    // Copy the test updater
+    let baseAppDir = getAppBaseDir();
+    let testUpdaterDir = Services.dirsvc.get("CurWorkD", Ci.nsILocalFile);
+    let relPath = REL_PATH_DATA;
+    let pathParts = relPath.split("/");
+    for (let i = 0; i < pathParts.length; ++i) {
+      testUpdaterDir.append(pathParts[i]);
+    }
+
+    let testUpdater = testUpdaterDir.clone();
+    testUpdater.append(FILE_UPDATER_BIN);
     testUpdater.copyToFollowingLinks(baseAppDir, FILE_UPDATER_BIN);
+  } catch (e) {
+    logTestInfo("Attempt to copy the test updater failed... " +
+                "will try again, Exception: " + e);
+    SimpleTest.executeSoon(copyTestUpdater);
+    return;
+  }
+
+  runTest();
+}
+
+/**
+ * Restores the updater that was backed up. This is called in setupTestUpdater
+ * before the backup of the real updater is done in case the previous test
+ * failed to restore the updater, in finishTestDefaultWaitForWindowClosed when
+ * the test has finished, and in test_9999_cleanup.xul after all tests have
+ * finished.
+ */
+function restoreUpdaterBackup() {
+  let baseAppDir = getAppBaseDir();
+  let updater = baseAppDir.clone();
+  let updaterBackup = baseAppDir.clone();
+  updater.append(FILE_UPDATER_BIN);
+  updaterBackup.append(FILE_UPDATER_BIN_BAK);
+  if (updaterBackup.exists()) {
+    if (updater.exists()) {
+      updater.remove(true);
+    }
+    updaterBackup.moveTo(baseAppDir, FILE_UPDATER_BIN);
   }
 }
 
 /**
  * Sets the most common preferences used by tests to values used by the majority
  * of the tests and when necessary saves the preference's original values if
  * present so they can be set back to the original values when the test has
  * finished.
@@ -1017,17 +1094,16 @@ function resetFiles() {
     try {
       removeDirRecursive(updatedDir);
     }
     catch (e) {
       logTestInfo("Unable to remove directory. Path: " + updatedDir.path +
                   ", Exception: " + e);
     }
   }
-  resetUpdaterBackup();
 }
 
 /**
  * Resets the most common preferences used by tests to their original values.
  */
 function resetPrefs() {
   if (gAppUpdateURL !== undefined) {
     Services.prefs.setCharPref(PREF_APP_UPDATE_URL_OVERRIDE, gAppUpdateURL);
--- a/toolkit/mozapps/update/tests/data/shared.js
+++ b/toolkit/mozapps/update/tests/data/shared.js
@@ -125,16 +125,20 @@ XPCOMUtils.defineLazyGetter(this, "gUP",
 XPCOMUtils.defineLazyGetter(this, "gDefaultPrefBranch", function test_gDPB() {
   return Services.prefs.getDefaultBranch(null);
 });
 
 XPCOMUtils.defineLazyGetter(this, "gPrefRoot", function test_gPR() {
   return Services.prefs.getBranch(null);
 });
 
+XPCOMUtils.defineLazyServiceGetter(this, "gEnv",
+                                   "@mozilla.org/process/environment;1",
+                                   "nsIEnvironment");
+
 XPCOMUtils.defineLazyGetter(this, "gZipW", function test_gZipW() {
   return Cc["@mozilla.org/zipwriter;1"].
          createInstance(Ci.nsIZipWriter);
 });
 
 /* Initializes the update service stub */
 function initUpdateServiceStub() {
   Cc["@mozilla.org/updates/update-service-stub;1"].
--- a/toolkit/mozapps/update/updater/archivereader.cpp
+++ b/toolkit/mozapps/update/updater/archivereader.cpp
@@ -13,17 +13,17 @@
 #ifdef XP_WIN
 #include "nsAlgorithm.h" // Needed by nsVersionComparator.cpp
 #include "updatehelper.h"
 #endif
 
 // These are generated at compile time based on the DER file for the channel
 // being used
 #ifdef MOZ_VERIFY_MAR_SIGNATURE
-#ifdef UPDATER_XPCSHELL_CERT
+#ifdef TEST_UPDATER
 #include "../xpcshellCert.h"
 #else
 #include "primaryCert.h"
 #include "secondaryCert.h"
 #endif
 #endif
 
 #define UPDATER_NO_STRING_GLUE_STL
@@ -80,17 +80,17 @@ ArchiveReader::VerifySignature()
 {
   if (!mArchive) {
     return ARCHIVE_NOT_OPEN;
   }
 
 #ifndef MOZ_VERIFY_MAR_SIGNATURE
   return OK;
 #else
-#ifdef UPDATER_XPCSHELL_CERT
+#ifdef TEST_UPDATER
   int rv = VerifyLoadedCert(mArchive, xpcshellCertData);
 #else
   int rv = VerifyLoadedCert(mArchive, primaryCertData);
   if (rv != OK) {
     rv = VerifyLoadedCert(mArchive, secondaryCertData);
   }
 #endif
   return rv;
--- a/toolkit/mozapps/update/updater/updater-xpcshell/moz.build
+++ b/toolkit/mozapps/update/updater/updater-xpcshell/moz.build
@@ -3,11 +3,11 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 Program('updater-xpcshell')
 
 updater_rel_path = '../'
 DIST_INSTALL = False
-DEFINES['UPDATER_XPCSHELL_CERT'] = True
+DEFINES['TEST_UPDATER'] = True
 include('../updater-common.build')
 FAIL_ON_WARNINGS = True
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -2257,17 +2257,28 @@ UpdateThreadFunc(void *param)
 
         rv = gArchiveReader.VerifyProductInformation(MARStrings.MARChannelID,
                                                      MOZ_APP_VERSION);
       }
     }
 #endif
 
     if (rv == OK && sStagedUpdate && !sIsOSUpdate) {
+#ifdef TEST_UPDATER
+      // The MOZ_TEST_SKIP_UPDATE_STAGE environment variable prevents copying
+      // the files in dist/bin in the test updater when staging an update since
+      // this can cause tests to timeout.
+      if (getenv("MOZ_TEST_SKIP_UPDATE_STAGE")) {
+        rv = OK;
+      } else {
+        rv = CopyInstallDirToDestDir();
+      }
+#else
       rv = CopyInstallDirToDestDir();
+#endif
     }
 
     if (rv == OK) {
       rv = DoUpdate();
       gArchiveReader.Close();
       NS_tchar updatingDir[MAXPATHLEN];
       NS_tsnprintf(updatingDir, sizeof(updatingDir)/sizeof(updatingDir[0]),
                    NS_T("%s/updating"), gWorkingDirPath);
--- a/toolkit/mozapps/update/updater/updater.rc
+++ b/toolkit/mozapps/update/updater/updater.rc
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Microsoft Visual C++ generated resource script.
 //
-#ifdef UPDATER_XPCSHELL_CERT
+#ifdef TEST_UPDATER
 #include "../resource.h"
 #define MANIFEST_PATH "../updater.exe.manifest"
 #define COMCTL32_MANIFEST_PATH "../updater.exe.comctl32.manifest"
 #define ICON_PATH "../updater.ico"
 #else
 #include "resource.h"
 #define MANIFEST_PATH "updater.exe.manifest"
 #define COMCTL32_MANIFEST_PATH "updater.exe.comctl32.manifest"