Bug 606415: Tests for bug 1009668. r=spohl
authorRobert Strong <robert.strong.bugs@gmail.com>
Fri, 23 May 2014 10:42:58 -0400
changeset 184719 a13a734672f12d25018588fd7ed6e4b2d00e48c8
parent 184718 c745965a2213baf4dd857e7ba22af544a2c09f97
child 184720 1475788f1869fb8790ddda5d7491a5bbcc65df4f
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersspohl
bugs606415, 1009668
milestone32.0a1
Bug 606415: Tests for bug 1009668. r=spohl
toolkit/mozapps/update/tests/Makefile.in
toolkit/mozapps/update/tests/TestAUSHelper.cpp
toolkit/mozapps/update/tests/unit_aus_update/head_update.js
toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js
toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js
toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js
toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js
toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js
toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js
toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js
toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js
toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js
toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js
toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js
toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js
toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js
toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js
toolkit/mozapps/update/updater/launchchild_osx.mm
toolkit/mozapps/update/updater/updater.cpp
--- a/toolkit/mozapps/update/tests/Makefile.in
+++ b/toolkit/mozapps/update/tests/Makefile.in
@@ -59,16 +59,17 @@ chrome-data_DEST    := $(CHROMETESTROOT)
 chrome-data_FILES   := $(addprefix $(srcdir)/data/,$(_CHROME_DATA))
 
 INI_TEST_FILES = \
   TestAUSReadStrings1.ini \
   TestAUSReadStrings2.ini \
   TestAUSReadStrings3.ini \
   $(NULL)
 
+MOZ_GLUE_LDFLAGS =
 MOZ_WINCONSOLE = 1
 
 ifeq ($(OS_ARCH),WINNT)
 LIBS += $(call EXPAND_LIBNAME_PATH,updatecommon-standalone,../common-standalone)
 else
 LIBS += $(call EXPAND_LIBNAME_PATH,updatecommon,../common)
 endif
 
--- a/toolkit/mozapps/update/tests/TestAUSHelper.cpp
+++ b/toolkit/mozapps/update/tests/TestAUSHelper.cpp
@@ -159,28 +159,60 @@ VerifyCertificateTrustForFile(LPCWSTR fi
   // Check if the file is signed by something that is trusted.
   return WinVerifyTrust(nullptr, &policyGUID, &trustData);
 }
 
 #endif
 
 int NS_main(int argc, NS_tchar **argv)
 {
+  if (argc == 2) {
+    if (!NS_tstrcmp(argv[1], NS_T("post-update-async")) ||
+        !NS_tstrcmp(argv[1], NS_T("post-update-sync"))) {
+      NS_tchar exePath[MAXPATHLEN];
+#ifdef XP_WIN
+      if (!::GetModuleFileNameW(0, exePath, MAXPATHLEN)) {
+        return 1;
+      }
+#else
+      strcpy(exePath, argv[0]);
+#endif
+      NS_tchar runFilePath[MAXPATHLEN];
+      NS_tsnprintf(runFilePath, sizeof(runFilePath)/sizeof(runFilePath[0]),
+                   NS_T("%s.running"), exePath);
+      WriteMsg(runFilePath, "running");
+
+      if (!NS_tstrcmp(argv[1], NS_T("post-update-sync"))) {
+#ifdef XP_WIN
+        Sleep(2000);
+#else
+        sleep(2);
+#endif
+      }
+
+      NS_tchar logFilePath[MAXPATHLEN];
+      NS_tsnprintf(logFilePath, sizeof(logFilePath)/sizeof(logFilePath[0]),
+                   NS_T("%s.log"), exePath);
+      WriteMsg(logFilePath, "post-update");
+      return 0;
+    }
+  }
 
   if (argc < 3) {
     fprintf(stderr, \
             "\n" \
             "Application Update Service Test Helper\n" \
             "\n" \
             "Usage: WORKINGDIR INFILE OUTFILE -s SECONDS [FILETOLOCK]\n"  \
             "   or: WORKINGDIR LOGFILE [ARG2 ARG3...]\n" \
             "   or: signature-check filepath\n" \
             "   or: setup-symlink dir1 dir2 file symlink\n" \
             "   or: remove-symlink dir1 dir2 file symlink\n" \
             "   or: check-symlink symlink\n" \
+            "   or: post-update\n" \
             "\n" \
             "  WORKINGDIR  \tThe relative path to the working directory to use.\n" \
             "  INFILE      \tThe relative path from the working directory for the file to\n" \
             "              \tread actions to perform such as finish.\n" \
             "  OUTFILE     \tThe relative path from the working directory for the file to\n" \
             "              \twrite status information.\n" \
             "  SECONDS     \tThe number of seconds to sleep.\n" \
             "  FILETOLOCK  \tThe relative path from the working directory to an existing\n" \
@@ -301,18 +333,16 @@ int NS_main(int argc, NS_tchar **argv)
       return 2;
     }
 #else 
     // Not implemented on non-Windows platforms
     return 1;
 #endif
   }
 
-  int i = 0;
-
   if (NS_tchdir(argv[1]) != 0) {
     return 1;
   }
 
   // File in use test helper section
   if (!NS_tstrcmp(argv[4], NS_T("-s"))) {
     NS_tchar *cwd = NS_tgetcwd(nullptr, 0);
     NS_tchar inFilePath[MAXPATHLEN];
@@ -331,40 +361,44 @@ int NS_main(int argc, NS_tchar **argv)
                           nullptr, OPEN_EXISTING, 0, nullptr);
       if (hFile == INVALID_HANDLE_VALUE) {
         WriteMsg(outFilePath, "error_locking");
         return 1;
       }
     }
 
     WriteMsg(outFilePath, "sleeping");
+    int i = 0;
     while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds)  {
       Sleep(1000);
     }
 
     if (argc == 7) {
       CloseHandle(hFile);
     }
 #else
     WriteMsg(outFilePath, "sleeping");
+    int i = 0;
     while (!CheckMsg(inFilePath, "finish\n") && i++ <= seconds)  {
       sleep(1);
     }
 #endif
     WriteMsg(outFilePath, "finished");
     return 0;
   }
 
-  // Command line argument test helper section
-  NS_tchar logFilePath[MAXPATHLEN];
-  NS_tsnprintf(logFilePath, sizeof(logFilePath)/sizeof(logFilePath[0]),
-               NS_T("%s"), argv[2]);
+  {
+    // Command line argument test helper section
+    NS_tchar logFilePath[MAXPATHLEN];
+    NS_tsnprintf(logFilePath, sizeof(logFilePath)/sizeof(logFilePath[0]),
+                 NS_T("%s"), argv[2]);
 
-  FILE* logFP = NS_tfopen(logFilePath, NS_T("wb"));
-  for (i = 1; i < argc; ++i) {
-    fprintf(logFP, LOG_S "\n", argv[i]);
+    FILE* logFP = NS_tfopen(logFilePath, NS_T("wb"));
+    for (int i = 1; i < argc; ++i) {
+      fprintf(logFP, LOG_S "\n", argv[i]);
+    }
+
+    fclose(logFP);
+    logFP = nullptr;
   }
 
-  fclose(logFP);
-  logFP = nullptr;
-
   return 0;
 } 
--- a/toolkit/mozapps/update/tests/unit_aus_update/head_update.js
+++ b/toolkit/mozapps/update/tests/unit_aus_update/head_update.js
@@ -79,17 +79,16 @@ const FILE_COMPLETE_EXE = "complete.exe"
 const FILE_COMPLETE_MAR = "complete.mar";
 const FILE_HELPER_BIN = "TestAUSHelper" + BIN_SUFFIX;
 const FILE_MAINTENANCE_SERVICE_BIN = "maintenanceservice.exe";
 const FILE_MAINTENANCE_SERVICE_INSTALLER_BIN = "maintenanceservice_installer.exe";
 const FILE_OLD_VERSION_MAR = "old_version.mar";
 const FILE_PARTIAL_EXE = "partial.exe";
 const FILE_PARTIAL_MAR = "partial.mar";
 const FILE_UPDATER_BIN = "updater" + BIN_SUFFIX;
-const FILE_UPDATER_INI_BAK = "updater.ini.bak";
 const FILE_WRONG_CHANNEL_MAR = "wrong_product_channel.mar";
 
 const LOG_COMPLETE_SUCCESS = "complete_log_success";
 const LOG_PARTIAL_SUCCESS  = "partial_log_success";
 const LOG_PARTIAL_FAILURE  = "partial_log_failure";
 
 const LOG_SWITCH_SUCCESS = "rename_file: proceeding to rename the directory\n" +
                            "rename_file: proceeding to rename the directory\n" +
@@ -164,16 +163,17 @@ var gAppDirOrig;
 
 var gServiceLaunchedCallbackLog = null;
 var gServiceLaunchedCallbackArgs = null;
 
 // Variables are used instead of contants so tests can override these values if
 // necessary.
 var gCallbackBinFile = "callback_app" + BIN_SUFFIX;
 var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"];
+var gPostUpdateBinFile = "postup_app" + BIN_SUFFIX;
 var gStageUpdate = false;
 var gSwitchApp = false;
 var gDisableReplaceFallback = false;
 var gUseTestAppDir = true;
 
 var gTimeoutRuns = 0;
 
 // Environment related globals
@@ -2182,16 +2182,21 @@ function setupUpdaterTest(aMarFile, aUpd
     updatesPatchDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
   }
   // Copy the mar that will be applied
   let mar = getTestDirFile(aMarFile);
   mar.copyToFollowingLinks(updatesPatchDir, FILE_UPDATE_ARCHIVE);
 
   createUpdateSettingsINI();
 
+  let helperBin = getTestDirFile(FILE_HELPER_BIN);
+  let afterApplyBinDir = getApplyDirFile("a/b/", true);
+  helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile);
+  helperBin.copyToFollowingLinks(afterApplyBinDir, gPostUpdateBinFile);
+
   let applyToDir = getApplyDirFile(null, true);
   gTestFiles.forEach(function SUT_TF_FE(aTestFile) {
     if (aTestFile.originalFile || aTestFile.originalContents) {
       let testDir = getApplyDirFile(aTestFile.relPathDir, true);
       if (!testDir.exists())
         testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
 
       let testFile;
@@ -2213,20 +2218,16 @@ function setupUpdaterTest(aMarFile, aUpd
         // setting the permissions.
         if (!aTestFile.comparePerms) {
           aTestFile.comparePerms = testFile.permissions;
         }
       }
     }
   });
 
-  let helperBin = getTestDirFile(FILE_HELPER_BIN);
-  let afterApplyBinDir = getApplyDirFile("a/b/", true);
-  helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile);
-
   // Add the test directory that will be updated for a successful update or left
   // in the initial state for a failed update.
   gTestDirs.forEach(function SUT_TD_FE(aTestDir) {
     let testDir = getApplyDirFile(aTestDir.relPathDir, true);
     if (!testDir.exists()) {
       testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
     }
 
@@ -2262,26 +2263,62 @@ function setupUpdaterTest(aMarFile, aUpd
   gTestExtraDirs[1].dirExists = IS_WIN ? aToBeDeletedDirExists : false;
 }
 
 /**
  * Helper function for updater binary tests that creates the update-settings.ini
  * file.
  */
 function createUpdateSettingsINI() {
-  updateSettingsIni = getApplyDirFile(null, true);
+  let updateSettingsIni = getApplyDirFile(null, true);
   if (IS_MACOSX) {
     updateSettingsIni.append("Contents");
     updateSettingsIni.append("MacOS");
   }
   updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI);
   writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
 }
 
 /**
+ * Helper function for updater binary tests that creates the updater.ini
+ * file.
+ *
+ * @param   aIsExeAsync
+ *          True or undefined if the post update process should be async. If
+ *          undefined ExeAsync will not be added to the updater.ini file in
+ *          order to test the default launch behavior which is async.
+ */
+function createUpdaterINI(aIsExeAsync) {
+  let exeArg = "ExeArg=post-update-async\n";
+  let exeAsync = "";
+  if (aIsExeAsync !== undefined)
+  {
+    if (aIsExeAsync) {
+      exeAsync = "ExeAsync=true\n";
+    } else {
+      exeArg = "ExeArg=post-update-sync\n";
+      exeAsync = "ExeAsync=false\n";
+    }
+  }
+
+  let updaterIniContents = "[PostUpdateMac]\n" +
+                           "ExeRelPath=a/b/" + gPostUpdateBinFile + "\n" +
+                           exeArg +
+                           exeAsync +
+                           "\n" +
+                           "[PostUpdateWin]\n" +
+                           "ExeRelPath=a/b/" + gPostUpdateBinFile + "\n" +
+                           exeArg +
+                           exeAsync;
+  let updaterIni = getApplyDirFile((IS_MACOSX ? "Contents/MacOS/" : "") +
+                                    FILE_UPDATER_INI, true);
+  writeFile(updaterIni, updaterIniContents);
+}
+
+/**
  * Helper function for updater binary tests for verifying the contents of the
  * update log after a successful update.
  *
  * @param   aCompareLogFile
  *          The log file to compare the update log with.
  */
 function checkUpdateLogContents(aCompareLogFile) {
   let updateLog = getUpdatesPatchDir();
@@ -2647,16 +2684,67 @@ function checkCallbackAppLog() {
     logTestInfo("callback log file contents are not correct");
     do_check_eq(logContents, expectedLogContents);
   }
 
   waitForFilesInUse();
 }
 
 /**
+ * Helper function for updater binary tests for getting the log and running
+ * files created by the test helper binary file when called with the post-update
+ * command line argument.
+ *
+ * @param   aSuffix
+ *          The string to append to the post update test helper binary path.
+ */
+function getPostUpdateFile(aSuffix) {
+  return getApplyDirFile("a/b/" + gPostUpdateBinFile + aSuffix, true);
+}
+
+/**
+ * Helper function for updater binary tests for verifying the contents of the
+ * updater post update binary log.
+ */
+function checkPostUpdateAppLog() {
+  gTimeoutRuns++;
+  let postUpdateLog = getPostUpdateFile(".log");
+  if (!postUpdateLog.exists()) {
+    logTestInfo("postUpdateLog does not exist");
+    if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
+      do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the post update " +
+               "process to create the post update log. Path: " +
+               postUpdateLog.path);
+    }
+    do_timeout(TEST_HELPER_TIMEOUT, checkPostUpdateAppLog);
+    return;
+  }
+
+  let logContents = readFile(postUpdateLog);
+  // It is possible for the log file contents check to occur before the log file
+  // contents are completely written so wait until the contents are the expected
+  // value. If the contents are never the expected value then the test will
+  // fail by timing out.
+  if (logContents != "post-update\n") {
+    if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
+      do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the post update " +
+               "process to create the expected contents in the post update log. Path: " +
+               postUpdateLog.path);
+    }
+    do_timeout(TEST_HELPER_TIMEOUT, checkPostUpdateAppLog);
+    return;
+  }
+
+  logTestInfo("post update app log file contents are correct");
+  do_check_true(true);
+
+  gCheckFunc();
+}
+
+/**
  * Helper function for updater service tests for verifying the contents of the
  * updater callback application log which should contain the arguments passed to
  * the callback application.
  */
 function checkCallbackServiceLog() {
   do_check_neq(gServiceLaunchedCallbackLog, null);
 
   let expectedLogContents = gServiceLaunchedCallbackArgs.join("\n") + "\n";
--- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js
@@ -14,16 +14,18 @@ function run_test() {
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI(false);
+
   let channel = Services.prefs.getCharPref(PREF_APP_UPDATE_CHANNEL);
   let patches = getLocalPatchString(null, null, null, null, null, "true",
                                     STATE_PENDING);
   let updates = getLocalUpdateString(patches, null, null, null, null, null,
                                      null, null, null, null, null, null,
                                      null, "true", channel);
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
   writeVersionFile(getAppVersion());
@@ -60,12 +62,17 @@ function checkUpdateApplied() {
     }
     return;
   }
 
   do_timeout(TEST_CHECK_TIMEOUT, finishTest);
 }
 
 function finishTest() {
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   do_check_eq(readStatusState(), STATE_PENDING);
   unlockDirectory(getAppBaseDir());
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js
@@ -15,16 +15,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI(true);
+
   if (IS_WIN) {
     Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, false);
   }
 
   let channel = Services.prefs.getCharPref(PREF_APP_UPDATE_CHANNEL);
   let patches = getLocalPatchString(null, null, null, null, null, "true",
                                     STATE_PENDING);
   let updates = getLocalUpdateString(patches, null, null, null, null, null,
@@ -126,16 +128,22 @@ function checkUpdateApplied() {
       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update log " +
                "to be created. Path: " + log.path);
     } else {
       do_timeout(TEST_CHECK_TIMEOUT, checkUpdateApplied);
     }
     return;
   }
 
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched when staging an
+    // update.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   let updatedDir = getUpdatedDir();
   logTestInfo("testing " + updatedDir.path + " should exist");
   do_check_true(updatedDir.exists());
 
   // On Windows, make sure not to use the maintenance service for switching
   // the app.
   if (IS_WIN) {
     writeStatusFile(STATE_APPLIED);
@@ -199,20 +207,33 @@ function checkUpdateApplied() {
   }
 
   // Switch the application to the staged application that was updated by
   // launching the application.
   do_timeout(TEST_CHECK_TIMEOUT, launchAppToApplyUpdate);
 }
 
 /**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
+function checkUpdateFinished() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateFinished;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateFinished();
+  }
+}
+
+/**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
-function checkUpdateFinished() {
+function finishCheckUpdateFinished() {
   gTimeoutRuns++;
   // Don't proceed until the update's status state is the expected value.
   let state = readStatusState();
   if (state != STATE_SUCCEEDED) {
     if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update " +
                "status state to equal: " +
                STATE_SUCCEEDED +
--- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js
@@ -13,16 +13,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI(false);
+
   if (IS_WIN) {
     Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, false);
   }
 
   let channel = Services.prefs.getCharPref(PREF_APP_UPDATE_CHANNEL);
   let patches = getLocalPatchString(null, null, null, null, null, "true",
                                     STATE_PENDING);
   let updates = getLocalUpdateString(patches, null, null, null, null, null,
@@ -101,16 +103,22 @@ function checkUpdateApplied() {
       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update log " +
                "to be created. Path: " + log.path);
     } else {
       do_timeout(TEST_CHECK_TIMEOUT, checkUpdateApplied);
     }
     return;
   }
 
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched when staging an
+    // update.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   let updatedDir = getUpdatedDir();
   logTestInfo("testing " + updatedDir.path + " should exist");
   do_check_true(updatedDir.exists());
 
   // On Windows, make sure not to use the maintenance service for switching
   // the app.
   if (IS_WIN) {
     writeStatusFile(STATE_APPLIED);
@@ -174,20 +182,33 @@ function checkUpdateApplied() {
   }
 
   // Switch the application to the staged application that was updated by
   // launching the application.
   do_timeout(TEST_CHECK_TIMEOUT, launchAppToApplyUpdate);
 }
 
 /**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
+function checkUpdateFinished() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateApplied;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateApplied();
+  }
+}
+
+/**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
-function checkUpdateFinished() {
+function finishCheckUpdateApplied() {
   gTimeoutRuns++;
   // Don't proceed until the update's status state is the expected value.
   let state = readStatusState();
   if (state != STATE_SUCCEEDED) {
     if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update " +
                "status state to equal: " + STATE_SUCCEEDED +
                ", current status state: " + state);
--- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js
@@ -13,16 +13,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI();
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on a successful
   // update (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
@@ -41,20 +43,33 @@ function run_test() {
   setupAppFilesAsync();
 }
 
 function setupAppFilesFinished() {
   do_timeout(TEST_CHECK_TIMEOUT, launchAppToApplyUpdate);
 }
 
 /**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
+function checkUpdateFinished() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateFinished;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateFinished();
+  }
+}
+
+/**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
-function checkUpdateFinished() {
+function finishCheckUpdateFinished() {
   gTimeoutRuns++;
   // Don't proceed until the update's status state is the expected value.
   let state = readStatusState();
   if (state != STATE_SUCCEEDED) {
     if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update " +
                "status state to equal: " + STATE_SUCCEEDED +
                ", current status state: " + state);
--- a/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js
@@ -8,31 +8,42 @@
 function run_test() {
   setupTestCommon();
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[11].originalFile = "partial.png";
   gTestDirs = gTestDirsPartialSuccess;
   setTestFilesAndDirsForFailure();
   setupUpdaterTest(FILE_PARTIAL_MAR, false, false);
 
+  createUpdaterINI();
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on all updates since
   // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
   }
 
   // Note that on platforms where we use execv, we cannot trust the return code.
   runUpdate((USE_EXECV ? 0 : 1), STATE_FAILED_UNEXPECTED_FILE_OPERATION_ERROR);
 }
 
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
 function checkUpdateApplied() {
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
--- a/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js
@@ -9,30 +9,41 @@ function run_test() {
   gStageUpdate = true;
   setupTestCommon();
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[11].originalFile = "partial.png";
   gTestDirs = gTestDirsPartialSuccess;
   setTestFilesAndDirsForFailure();
   setupUpdaterTest(FILE_PARTIAL_MAR, true, false);
 
+  createUpdaterINI(true);
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on all updates since
   // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
   }
 
   runUpdate(1, STATE_FAILED_UNEXPECTED_FILE_OPERATION_ERROR);
 }
 
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
 function checkUpdateApplied() {
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
--- a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
@@ -45,16 +45,18 @@ function run_test() {
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestFiles[gTestFiles.length - 1].originalContents = null;
   gTestFiles[gTestFiles.length - 1].compareContents = "FromComplete\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI(false);
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on a successful
   // update (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
@@ -89,30 +91,53 @@ function run_test() {
   }
 
   checkFilesAfterUpdateSuccess();
   // Sorting on Linux is different so skip this check for now.
   if (!IS_UNIX) {
     checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
   }
 
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched when staging an
+    // update.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   // Switch the application to the staged application that was updated.
   gStageUpdate = false;
   gSwitchApp = true;
   do_timeout(TEST_CHECK_TIMEOUT, function() {
     runUpdate(0, STATE_SUCCEEDED);
   });
 }
 
+/**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
 function checkUpdateApplied() {
-  let applyToDir = getApplyDirFile();
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateApplied;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateApplied();
+  }
+}
+
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
+function finishCheckUpdateApplied() {
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
+    let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
 
   checkFilesAfterUpdateSuccess();
   if (IS_UNIX) {
     checkSymlink();
   } else {
--- a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js
@@ -10,16 +10,18 @@ function run_test() {
   setupTestCommon();
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[gTestFiles.length - 2].originalContents = null;
   gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
   gTestDirs = gTestDirsPartialSuccess;
   setupUpdaterTest(FILE_PARTIAL_MAR, false, false);
 
+  createUpdaterINI(false);
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on all updates since
   // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
@@ -37,25 +39,48 @@ function run_test() {
   }
 
   checkFilesAfterUpdateSuccess();
   // Sorting on Linux is different so skip this check for now.
   if (!IS_UNIX) {
     checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
   }
 
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched when staging an
+    // update.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   // Switch the application to the staged application that was updated.
   gStageUpdate = false;
   gSwitchApp = true;
   do_timeout(TEST_CHECK_TIMEOUT, function() {
     runUpdate(0, STATE_SUCCEEDED);
   });
 }
 
+/**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
 function checkUpdateApplied() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateApplied;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateApplied();
+  }
+}
+
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
+function finishCheckUpdateApplied() {
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
--- a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js
@@ -6,30 +6,49 @@
 /* General Complete MAR File Patch Apply Test */
 
 function run_test() {
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI();
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on a successful
   // update (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
   }
 
   runUpdate(0, STATE_SUCCEEDED);
 }
 
+/**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
 function checkUpdateApplied() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateApplied;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateApplied();
+  }
+}
+
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
+function finishCheckUpdateApplied() {
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
--- a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js
@@ -12,30 +12,49 @@ function run_test() {
   gTestFiles[gTestFiles.length - 1].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestFiles[gTestFiles.length - 2].originalContents = null;
   gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
   gTestDirs = gTestDirsPartialSuccess;
   setupUpdaterTest(FILE_PARTIAL_MAR, false, false);
 
+  createUpdaterINI(true);
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on all updates since
   // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
   }
 
   runUpdate(0, STATE_SUCCEEDED);
 }
 
+/**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
 function checkUpdateApplied() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateApplied;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateApplied();
+  }
+}
+
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
+function finishCheckUpdateApplied() {
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
--- a/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js
@@ -12,19 +12,30 @@ function run_test() {
 
   setupTestCommon();
   // We don't actually care if the MAR has any data, we only care about the
   // application return code and update.status result.
   gTestFiles = gTestFilesCommon;
   gTestDirs = [];
   setupUpdaterTest(FILE_OLD_VERSION_MAR, false, false);
 
+  createUpdaterINI(true);
+
   // Apply the MAR
   // Note that if execv is used, the updater process will turn into the
   // callback process, so its return code will be that of the callback
   // app.
   runUpdate((USE_EXECV ? 0 : 1), STATE_FAILED_VERSION_DOWNGRADE_ERROR);
 }
 
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
 function checkUpdateApplied() {
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   checkFilesAfterUpdateSuccess();
   doTestFinish();
 }
--- a/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js
@@ -12,19 +12,30 @@ function run_test() {
 
   setupTestCommon();
   // We don't actually care if the MAR has any data, we only care about the
   // application return code and update.status result.
   gTestFiles = gTestFilesCommon;
   gTestDirs = [];
   setupUpdaterTest(FILE_WRONG_CHANNEL_MAR, false, false);
 
+  createUpdaterINI();
+
   // Apply the MAR
   // Note that if execv is used, the updater process will turn into the
   // callback process, so its return code will be that of the callback
   // app.
   runUpdate((USE_EXECV ? 0 : 1), STATE_FAILED_CHANNEL_MISMATCH_ERROR);
 }
 
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
 function checkUpdateApplied() {
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   checkFilesAfterUpdateSuccess();
   doTestFinish();
 }
--- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js
@@ -18,16 +18,18 @@ function run_test() {
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI(false);
+
   let channel = Services.prefs.getCharPref(PREF_APP_UPDATE_CHANNEL);
   let patches = getLocalPatchString(null, null, null, null, null, "true",
                                     STATE_PENDING_SVC);
   let updates = getLocalUpdateString(patches, null, null, null, null, null,
                                      null, null, null, null, null, null,
                                      null, "true", channel);
   writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
   writeVersionFile(getAppVersion());
@@ -64,12 +66,17 @@ function checkUpdateApplied() {
     }
     return;
   }
 
   do_timeout(TEST_CHECK_TIMEOUT, finishTest);
 }
 
 function finishTest() {
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   do_check_eq(readStatusState(), STATE_PENDING);
   unlockDirectory(getAppBaseDir());
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js
@@ -19,16 +19,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI(true);
+
   if (IS_WIN) {
     Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, true);
   }
 
   let channel = Services.prefs.getCharPref(PREF_APP_UPDATE_CHANNEL);
   let patches = getLocalPatchString(null, null, null, null, null, "true",
                                     STATE_PENDING_SVC);
   let updates = getLocalUpdateString(patches, null, null, null, null, null,
@@ -130,16 +132,22 @@ function checkUpdateApplied() {
       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update log " +
                "to be created. Path: " + log.path);
     } else {
       do_timeout(TEST_CHECK_TIMEOUT, checkUpdateApplied);
     }
     return;
   }
 
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched when staging an
+    // update.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   let updatedDir = getUpdatedDir();
   logTestInfo("testing " + updatedDir.path + " should exist");
   do_check_true(updatedDir.exists());
 
   // On Windows, make sure not to use the maintenance service for switching
   // the app.
   if (IS_WIN) {
     writeStatusFile(STATE_APPLIED);
@@ -196,20 +204,33 @@ function checkUpdateApplied() {
   do_check_false(updatesDir.exists());
 
   // Switch the application to the staged application that was updated by
   // launching the application.
   do_timeout(TEST_CHECK_TIMEOUT, launchAppToApplyUpdate);
 }
 
 /**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
+function checkUpdateFinished() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateFinished;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateFinished();
+  }
+}
+
+/**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
-function checkUpdateFinished() {
+function finishCheckUpdateFinished() {
   gTimeoutRuns++;
   // Don't proceed until the update's status state is the expected value.
   let state = readStatusState();
   if (state != STATE_SUCCEEDED) {
     if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update " +
                "status state to equal: " +
                STATE_SUCCEEDED +
--- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js
@@ -17,16 +17,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI(false);
+
   if (IS_WIN) {
     Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, true);
   }
 
   let channel = Services.prefs.getCharPref(PREF_APP_UPDATE_CHANNEL);
   let patches = getLocalPatchString(null, null, null, null, null, "true",
                                     STATE_PENDING_SVC);
   let updates = getLocalUpdateString(patches, null, null, null, null, null,
@@ -105,16 +107,22 @@ function checkUpdateApplied() {
       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update log " +
                "to be created. Path: " + log.path);
     } else {
       do_timeout(TEST_CHECK_TIMEOUT, checkUpdateApplied);
     }
     return;
   }
 
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched when staging an
+    // update.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   let updatedDir = getUpdatedDir();
   logTestInfo("testing " + updatedDir.path + " should exist");
   do_check_true(updatedDir.exists());
 
   log = getUpdatesDir();
   log.append("0");
   log.append(FILE_UPDATE_LOG);
   logTestInfo("testing " + log.path + " shouldn't exist");
@@ -171,20 +179,33 @@ function checkUpdateApplied() {
   }
 
   // Switch the application to the staged application that was updated by
   // launching the application.
   do_timeout(TEST_CHECK_TIMEOUT, launchAppToApplyUpdate);
 }
 
 /**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
+function checkUpdateFinished() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateApplied;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateApplied();
+  }
+}
+
+/**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
-function checkUpdateFinished() {
+function finishCheckUpdateApplied() {
   gTimeoutRuns++;
   // Don't proceed until the update's status state is the expected value.
   let state = readStatusState();
   if (state != STATE_SUCCEEDED) {
     if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update " +
                "status state to equal: " + STATE_SUCCEEDED +
                ", current status state: " + state);
--- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js
@@ -17,16 +17,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI();
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on a successful
   // update (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
@@ -45,20 +47,33 @@ function run_test() {
   setupAppFilesAsync();
 }
 
 function setupAppFilesFinished() {
   runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED);
 }
 
 /**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
+function checkUpdateFinished() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateFinished;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateFinished();
+  }
+}
+
+/**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
-function checkUpdateFinished() {
+function finishCheckUpdateFinished() {
   gTimeoutRuns++;
   // Don't proceed until the update's status state is the expected value.
   let state = readStatusState();
   if (state != STATE_SUCCEEDED) {
     if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update " +
                "status state to equal: " + STATE_SUCCEEDED +
                ", current status state: " + state);
--- a/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js
@@ -12,16 +12,18 @@ function run_test() {
 
   setupTestCommon();
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[11].originalFile = "partial.png";
   gTestDirs = gTestDirsPartialSuccess;
   setTestFilesAndDirsForFailure();
   setupUpdaterTest(FILE_PARTIAL_MAR, false, false);
 
+  createUpdaterINI();
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on all updates since
   // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
@@ -29,17 +31,26 @@ function run_test() {
 
   setupAppFilesAsync();
 }
 
 function setupAppFilesFinished() {
   runUpdateUsingService(STATE_PENDING_SVC, STATE_FAILED);
 }
 
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
 function checkUpdateFinished() {
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
--- a/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js
@@ -13,16 +13,18 @@ function run_test() {
   gStageUpdate = true;
   setupTestCommon();
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[11].originalFile = "partial.png";
   gTestDirs = gTestDirsPartialSuccess;
   setTestFilesAndDirsForFailure();
   setupUpdaterTest(FILE_PARTIAL_MAR, true, false);
 
+  createUpdaterINI(true);
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on all updates since
   // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
@@ -30,17 +32,26 @@ function run_test() {
 
   setupAppFilesAsync();
 }
 
 function setupAppFilesFinished() {
   runUpdateUsingService(STATE_PENDING_SVC, STATE_FAILED);
 }
 
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
 function checkUpdateFinished() {
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
--- a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js
@@ -49,16 +49,18 @@ function run_test() {
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestFiles[gTestFiles.length - 1].originalContents = null;
   gTestFiles[gTestFiles.length - 1].compareContents = "FromComplete\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI(false);
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on a successful
   // update (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
@@ -99,28 +101,51 @@ function checkUpdateFinished() {
   }
 
   checkFilesAfterUpdateSuccess();
   // Sorting on Linux is different so skip this check for now.
   if (!IS_UNIX) {
     checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
   }
 
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched when staging an
+    // update.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   // Switch the application to the staged application that was updated.
   gStageUpdate = false;
   gSwitchApp = true;
   runUpdate(0, STATE_SUCCEEDED);
 }
 
+/**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
 function checkUpdateApplied() {
-  let applyToDir = getApplyDirFile();
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateApplied;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateApplied();
+  }
+}
+
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
+function finishCheckUpdateApplied() {
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
+    let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
 
   checkFilesAfterUpdateSuccess();
   if (IS_UNIX) {
     checkSymlink();
   } else {
--- a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js
@@ -14,16 +14,18 @@ function run_test() {
   setupTestCommon();
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[gTestFiles.length - 2].originalContents = null;
   gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
   gTestDirs = gTestDirsPartialSuccess;
   setupUpdaterTest(FILE_PARTIAL_MAR, false, false);
 
+  createUpdaterINI(false);
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on all updates since
   // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
@@ -47,23 +49,46 @@ function checkUpdateFinished() {
   }
 
   checkFilesAfterUpdateSuccess();
   // Sorting on Linux is different so skip this check for now.
   if (!IS_UNIX) {
     checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
   }
 
+  if (IS_MACOSX || IS_WIN) {
+    // Check that the post update process was not launched when staging an
+    // update.
+    do_check_false(getPostUpdateFile(".running").exists());
+  }
+
   // Switch the application to the staged application that was updated.
   gStageUpdate = false;
   gSwitchApp = true;
   runUpdate(0, STATE_SUCCEEDED);
 }
 
+/**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
 function checkUpdateApplied() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateApplied;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateApplied();
+  }
+}
+
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
+function finishCheckUpdateApplied() {
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
--- a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js
@@ -10,16 +10,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR, false, false);
 
+  createUpdaterINI();
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on a successful
   // update (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
@@ -27,17 +29,34 @@ function run_test() {
 
   setupAppFilesAsync();
 }
 
 function setupAppFilesFinished() {
   runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED);
 }
 
+/**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
 function checkUpdateFinished() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateFinished;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateFinished();
+  }
+}
+
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
+function finishCheckUpdateFinished() {
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
--- a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js
@@ -16,16 +16,18 @@ function run_test() {
   gTestFiles[gTestFiles.length - 1].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestFiles[gTestFiles.length - 2].originalContents = null;
   gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
   gTestDirs = gTestDirsPartialSuccess;
   setupUpdaterTest(FILE_PARTIAL_MAR, false, false);
 
+  createUpdaterINI(true);
+
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on all updates since
   // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
     applyToDir.lastModifiedTime = yesterday;
@@ -33,17 +35,34 @@ function run_test() {
 
   setupAppFilesAsync();
 }
 
 function setupAppFilesFinished() {
   runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED);
 }
 
+/**
+ * Checks if the post update binary was properly launched for the platforms that
+ * support launching post update process.
+ */
 function checkUpdateFinished() {
+  if (IS_MACOSX || IS_WIN) {
+    gCheckFunc = finishCheckUpdateFinished;
+    checkPostUpdateAppLog();
+  } else {
+    finishCheckUpdateFinished();
+  }
+}
+
+/**
+ * Checks if the update has finished and if it has finished performs checks for
+ * the test.
+ */
+function finishCheckUpdateFinished() {
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
--- a/toolkit/mozapps/update/updater/launchchild_osx.mm
+++ b/toolkit/mozapps/update/updater/launchchild_osx.mm
@@ -90,38 +90,49 @@ LaunchMacPostProcess(const char* aAppBun
     // the file does not exist; there is nothing to run
     [pool release];
     return;
   }
 
   int readResult;
   char values[2][MAX_TEXT_LEN];
   readResult = ReadStrings([iniPath UTF8String],
-                           "ExeArg\0ExeRelPath\0",
+                           "ExeRelPath\0ExeArg\0",
                            2,
                            values,
                            "PostUpdateMac");
   if (readResult) {
     [pool release];
     return;
   }
 
-  NSString *exeArg = [NSString stringWithUTF8String:values[0]];
-  NSString *exeRelPath = [NSString stringWithUTF8String:values[1]];
+  NSString *exeRelPath = [NSString stringWithUTF8String:values[0]];
+  NSString *exeArg = [NSString stringWithUTF8String:values[1]];
   if (!exeArg || !exeRelPath) {
     [pool release];
     return;
   }
 
   NSString* exeFullPath = [NSString stringWithUTF8String:aAppBundle];
   exeFullPath = [exeFullPath stringByAppendingPathComponent:exeRelPath];
 
+  char optVals[1][MAX_TEXT_LEN];
+  readResult = ReadStrings([iniPath UTF8String],
+                           "ExeAsync\0",
+                           1,
+                           optVals,
+                           "PostUpdateMac");
+
   NSTask *task = [[NSTask alloc] init];
   [task setLaunchPath:exeFullPath];
   [task setArguments:[NSArray arrayWithObject:exeArg]];
   [task launch];
-  [task waitUntilExit];
+  if (!readResult) {
+    NSString *exeAsync = [NSString stringWithUTF8String:optVals[0]];
+    if ([exeAsync isEqualToString:@"false"]) {
+      [task waitUntilExit];
+    }
+  }
   // ignore the return value of the task, there's nothing we can do with it
   [task release];
 
   [pool release];
 }
-
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -64,17 +64,17 @@
 
 // Amount of time in ms to wait for the parent process to close
 #define PARENT_WAIT 5000
 #define IMMERSIVE_PARENT_WAIT 15000
 
 #if defined(XP_MACOSX)
 // These functions are defined in launchchild_osx.mm
 void LaunchChild(int argc, char **argv);
-void LaunchMacPostProcess(const char* aAppExe);
+void LaunchMacPostProcess(const char* aAppBundle);
 #endif
 
 #ifndef _O_BINARY
 # define _O_BINARY 0
 #endif
 
 #ifndef NULL
 # define NULL (0)