summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/update/tests
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/update/tests')
-rw-r--r--toolkit/mozapps/update/tests/Makefile.in39
-rw-r--r--toolkit/mozapps/update/tests/TestAUSHelper.cpp423
-rw-r--r--toolkit/mozapps/update/tests/TestAUSReadStrings.cpp172
-rw-r--r--toolkit/mozapps/update/tests/TestAUSReadStrings1.ini47
-rw-r--r--toolkit/mozapps/update/tests/TestAUSReadStrings2.ini39
-rw-r--r--toolkit/mozapps/update/tests/TestAUSReadStrings3.ini39
-rw-r--r--toolkit/mozapps/update/tests/chrome/.eslintrc.js7
-rw-r--r--toolkit/mozapps/update/tests/chrome/chrome.ini64
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0010_background_basic.xul50
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0011_check_basic.xul51
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0012_check_basic_staging.xul55
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0013_check_no_updates.xul46
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0014_check_error_xml_malformed.xul46
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0061_check_verifyFailPartial_noComplete.xul52
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0062_check_verifyFailComplete_noPartial.xul52
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0063_check_verifyFailPartialComplete.xul52
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0064_check_verifyFailPartial_successComplete.xul52
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0071_notify_verifyFailPartial_noComplete.xul53
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0072_notify_verifyFailComplete_noPartial.xul52
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0073_notify_verifyFailPartialComplete.xul55
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0074_notify_verifyFailPartial_successComplete.xul55
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0081_error_patchApplyFailure_partial_only.xul53
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0082_error_patchApplyFailure_complete_only.xul52
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0083_error_patchApplyFailure_partial_complete.xul67
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0084_error_patchApplyFailure_verify_failed.xul68
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0085_error_patchApplyFailure_partial_complete_staging.xul94
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0092_finishedBackground.xul55
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0093_restartNotification.xul60
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0094_restartNotification_remote.xul60
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0095_restartNotification_remoteInvalidNumber.xul66
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0096_restartNotification_stagedBackground.xul65
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0097_restartNotification_stagedServiceBackground.xul65
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0101_background_restartNotification.xul46
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0102_background_restartNotification_staging.xul49
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0103_background_restartNotification_stagingService.xul50
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0111_neverButton_basic.xul61
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0113_showNeverForVersionRemovedWithPref.xul58
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0151_notify_backgroundCheckError.xul50
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0152_notify_backgroundCheckOfflineRetry.xul96
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0161_check_unsupported.xul50
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0162_notify_unsupported.xul44
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0171_check_noPerms_manual.xul64
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_0172_notify_noPerms_manual.xul63
-rw-r--r--toolkit/mozapps/update/tests/chrome/test_9999_cleanup.xul112
-rw-r--r--toolkit/mozapps/update/tests/chrome/update.sjs194
-rw-r--r--toolkit/mozapps/update/tests/chrome/utils.js1011
-rw-r--r--toolkit/mozapps/update/tests/data/complete.exebin0 -> 79872 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/complete.marbin0 -> 97888 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/complete.pngbin0 -> 878 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/complete_log_success_mac332
-rw-r--r--toolkit/mozapps/update/tests/data/complete_log_success_win320
-rw-r--r--toolkit/mozapps/update/tests/data/complete_mac.marbin0 -> 98454 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/complete_precomplete18
-rw-r--r--toolkit/mozapps/update/tests/data/complete_precomplete_mac21
-rw-r--r--toolkit/mozapps/update/tests/data/complete_removed-files41
-rw-r--r--toolkit/mozapps/update/tests/data/complete_removed-files_mac41
-rw-r--r--toolkit/mozapps/update/tests/data/complete_update_manifest59
-rw-r--r--toolkit/mozapps/update/tests/data/old_version.marbin0 -> 421 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/partial.exebin0 -> 79872 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/partial.marbin0 -> 10645 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/partial.pngbin0 -> 776 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/partial_log_failure_mac192
-rw-r--r--toolkit/mozapps/update/tests/data/partial_log_failure_win192
-rw-r--r--toolkit/mozapps/update/tests/data/partial_log_success_mac279
-rw-r--r--toolkit/mozapps/update/tests/data/partial_log_success_win279
-rw-r--r--toolkit/mozapps/update/tests/data/partial_mac.marbin0 -> 11172 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/partial_precomplete19
-rw-r--r--toolkit/mozapps/update/tests/data/partial_precomplete_mac22
-rw-r--r--toolkit/mozapps/update/tests/data/partial_removed-files41
-rw-r--r--toolkit/mozapps/update/tests/data/partial_removed-files_mac41
-rw-r--r--toolkit/mozapps/update/tests/data/partial_update_manifest63
-rw-r--r--toolkit/mozapps/update/tests/data/replace_log_success6
-rw-r--r--toolkit/mozapps/update/tests/data/shared.js632
-rw-r--r--toolkit/mozapps/update/tests/data/sharedUpdateXML.js364
-rw-r--r--toolkit/mozapps/update/tests/data/simple.marbin0 -> 1031 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/wrong_product_channel.marbin0 -> 421 bytes
-rw-r--r--toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js53
-rw-r--r--toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js4047
-rw-r--r--toolkit/mozapps/update/tests/moz.build101
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/.eslintrc.js7
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/canCheckForAndCanApplyUpdates.js138
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js46
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js29
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js30
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js35
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js37
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/cleanupSuccessLogMove.js37
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/cleanupSuccessLogsFIFO.js45
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js161
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/downloadCompleteAfterPartialFailure.js66
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/downloadInterruptedRecovery.js225
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js37
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/head_update.js8
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/remoteUpdateXML.js285
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/uiAutoPref.js75
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/uiSilentPref.js76
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/uiUnsupportedAlreadyNotified.js74
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/updateManagerXML.js177
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/urlConstruction.js305
-rw-r--r--toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini27
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/.eslintrc.js7
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/head_update.js8
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js37
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js45
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js47
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js44
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js38
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js43
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js38
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js38
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js37
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js41
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js83
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js88
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js82
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js65
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js70
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js113
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js62
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js61
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js61
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js48
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js48
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js47
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js67
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js67
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js63
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js63
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js57
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js56
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js71
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js70
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js72
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js71
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js63
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js62
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js41
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js132
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js112
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js96
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js79
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js51
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js51
-rw-r--r--toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini136
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/.eslintrc.js7
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/bootstrapSvc.js35
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/checkUpdaterSigSvc.js39
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/head_update.js8
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js47
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js44
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js38
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js43
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js38
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js38
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js37
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js41
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js83
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js82
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js65
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js70
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js62
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js61
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js61
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js48
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js48
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js47
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js67
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js67
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js63
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js63
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js57
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js56
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js71
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js70
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js72
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js71
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js63
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js62
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js41
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js132
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js112
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js96
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js79
-rw-r--r--toolkit/mozapps/update/tests/unit_service_updater/xpcshell.ini156
184 files changed, 18408 insertions, 0 deletions
diff --git a/toolkit/mozapps/update/tests/Makefile.in b/toolkit/mozapps/update/tests/Makefile.in
new file mode 100644
index 000000000..0b8d19aa2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/Makefile.in
@@ -0,0 +1,39 @@
+# 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/.
+
+XPCSHELLTESTROOT = $(topobjdir)/_tests/xpcshell/$(relativesrcdir)
+
+pp_const_file = $(srcdir)/data/xpcshellConstantsPP.js
+
+PP_TARGETS += aus-test-const
+aus-test-const := $(pp_const_file)
+aus-test-const_PATH := $(XPCSHELLTESTROOT)/data
+aus-test-const_FLAGS := -Fsubstitution $(DEFINES) $(ACDEFINES)
+aus-test-const_TARGET := misc
+
+INI_TEST_FILES = \
+ TestAUSReadStrings1.ini \
+ TestAUSReadStrings2.ini \
+ TestAUSReadStrings3.ini \
+ $(NULL)
+
+MOZ_WINCONSOLE = 1
+
+include $(topsrcdir)/config/rules.mk
+
+# TestAUSReadStrings runs during check in the following directory with a Unicode
+# char in order to test bug 473417 on Windows.
+ifeq ($(OS_ARCH),WINNT)
+bug473417dir = test_bug473417-ó
+else
+bug473417dir = test_bug473417
+endif
+
+check::
+ $(RM) -rf $(DEPTH)/_tests/updater/ && $(NSINSTALL) -D $(DEPTH)/_tests/updater/$(bug473417dir)/
+ for i in $(INI_TEST_FILES); do \
+ $(INSTALL) $(srcdir)/$$i $(DEPTH)/_tests/updater/$(bug473417dir)/; \
+ done
+ $(INSTALL) $(FINAL_TARGET)/TestAUSReadStrings$(BIN_SUFFIX) $(DEPTH)/_tests/updater/$(bug473417dir)/
+ @$(RUN_TEST_PROGRAM) $(DEPTH)/_tests/updater/$(bug473417dir)/TestAUSReadStrings$(BIN_SUFFIX)
diff --git a/toolkit/mozapps/update/tests/TestAUSHelper.cpp b/toolkit/mozapps/update/tests/TestAUSHelper.cpp
new file mode 100644
index 000000000..f71103b7a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/TestAUSHelper.cpp
@@ -0,0 +1,423 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#ifdef XP_WIN
+# include <windows.h>
+# include <wintrust.h>
+# include <tlhelp32.h>
+# include <softpub.h>
+# include <direct.h>
+# include <io.h>
+ typedef WCHAR NS_tchar;
+# define NS_main wmain
+# ifndef F_OK
+# define F_OK 00
+# endif
+# ifndef W_OK
+# define W_OK 02
+# endif
+# ifndef R_OK
+# define R_OK 04
+# endif
+# if defined(_MSC_VER) && _MSC_VER < 1900
+# define stat _stat
+# endif
+# define NS_T(str) L ## str
+# define NS_tsnprintf(dest, count, fmt, ...) \
+ { \
+ int _count = count - 1; \
+ _snwprintf(dest, _count, fmt, ##__VA_ARGS__); \
+ dest[_count] = L'\0'; \
+ }
+# define NS_taccess _waccess
+# define NS_tchdir _wchdir
+# define NS_tfopen _wfopen
+# define NS_tstrcmp wcscmp
+# define NS_ttoi _wtoi
+# define NS_tstat _wstat
+# define NS_tgetcwd _wgetcwd
+# define LOG_S "%S"
+
+#include "../common/updatehelper.h"
+#include "../common/certificatecheck.h"
+
+#else
+# include <unistd.h>
+# define NS_main main
+ typedef char NS_tchar;
+# define NS_T(str) str
+# define NS_tsnprintf snprintf
+# define NS_taccess access
+# define NS_tchdir chdir
+# define NS_tfopen fopen
+# define NS_tstrcmp strcmp
+# define NS_ttoi atoi
+# define NS_tstat stat
+# define NS_tgetcwd getcwd
+# define NS_tfputs fputs
+# define LOG_S "%s"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef MAXPATHLEN
+# ifdef PATH_MAX
+# define MAXPATHLEN PATH_MAX
+# elif defined(MAX_PATH)
+# define MAXPATHLEN MAX_PATH
+# elif defined(_MAX_PATH)
+# define MAXPATHLEN _MAX_PATH
+# elif defined(CCHMAXPATH)
+# define MAXPATHLEN CCHMAXPATH
+# else
+# define MAXPATHLEN 1024
+# endif
+#endif
+
+static void
+WriteMsg(const NS_tchar *path, const char *status)
+{
+ FILE* outFP = NS_tfopen(path, NS_T("wb"));
+ if (!outFP) {
+ return;
+ }
+
+ fprintf(outFP, "%s\n", status);
+ fclose(outFP);
+ outFP = nullptr;
+}
+
+static bool
+CheckMsg(const NS_tchar *path, const char *expected)
+{
+ if (NS_taccess(path, F_OK)) {
+ return false;
+ }
+
+ FILE *inFP = NS_tfopen(path, NS_T("rb"));
+ if (!inFP) {
+ return false;
+ }
+
+ struct stat ms;
+ if (fstat(fileno(inFP), &ms)) {
+ fclose(inFP);
+ inFP = nullptr;
+ return false;
+ }
+
+ char *mbuf = (char *) malloc(ms.st_size + 1);
+ if (!mbuf) {
+ fclose(inFP);
+ inFP = nullptr;
+ return false;
+ }
+
+ size_t r = ms.st_size;
+ char *rb = mbuf;
+ size_t c = fread(rb, sizeof(char), 50, inFP);
+ r -= c;
+ rb += c;
+ if (c == 0 && r) {
+ free(mbuf);
+ fclose(inFP);
+ inFP = nullptr;
+ return false;
+ }
+ mbuf[ms.st_size] = '\0';
+ rb = mbuf;
+
+ bool isMatch = strcmp(rb, expected) == 0;
+ free(mbuf);
+ fclose(inFP);
+ inFP = nullptr;
+ return isMatch;
+}
+
+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);
+#ifdef XP_WIN
+ if (!NS_taccess(runFilePath, F_OK)) {
+ // This makes it possible to check if the post update process was
+ // launched twice which happens when the service performs an update.
+ NS_tchar runFilePathBak[MAXPATHLEN];
+ NS_tsnprintf(runFilePathBak, sizeof(runFilePathBak)/sizeof(runFilePathBak[0]),
+ NS_T("%s.bak"), runFilePath);
+ MoveFileExW(runFilePath, runFilePathBak, MOVEFILE_REPLACE_EXISTING);
+ }
+#endif
+ 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" \
+ " \tfile to open exlusively.\n" \
+ " \tOnly available on Windows platforms and silently ignored on\n" \
+ " \tother platforms.\n" \
+ " LOGFILE \tThe relative path from the working directory to log the\n" \
+ " \tcommand line arguments.\n" \
+ " ARG2 ARG3...\tArguments to write to the LOGFILE after the preceding command\n" \
+ " \tline arguments.\n" \
+ "\n" \
+ "Note: All paths must be relative.\n" \
+ "\n");
+ return 1;
+ }
+
+ if (!NS_tstrcmp(argv[1], NS_T("check-signature"))) {
+#if defined(XP_WIN) && defined(MOZ_MAINTENANCE_SERVICE)
+ if (ERROR_SUCCESS == VerifyCertificateTrustForFile(argv[2])) {
+ return 0;
+ } else {
+ return 1;
+ }
+#else
+ // Not implemented on non-Windows platforms
+ return 1;
+#endif
+ }
+
+ if (!NS_tstrcmp(argv[1], NS_T("setup-symlink"))) {
+#ifdef XP_UNIX
+ NS_tchar path[MAXPATHLEN];
+ NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+ NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
+ mkdir(path, 0755);
+ NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+ NS_T("%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3]);
+ mkdir(path, 0755);
+ NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+ NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3], argv[4]);
+ FILE * file = NS_tfopen(path, NS_T("w"));
+ if (file) {
+ NS_tfputs(NS_T("test"), file);
+ fclose(file);
+ }
+ if (symlink(path, argv[5]) != 0) {
+ return 1;
+ }
+ NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+ NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
+ if (argc > 6 && !NS_tstrcmp(argv[6], NS_T("change-perm"))) {
+ chmod(path, 0644);
+ }
+ return 0;
+#else
+ // Not implemented on non-Unix platforms
+ return 1;
+#endif
+ }
+
+ if (!NS_tstrcmp(argv[1], NS_T("remove-symlink"))) {
+#ifdef XP_UNIX
+ NS_tchar path[MAXPATHLEN];
+ NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+ NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
+ chmod(path, 0755);
+ NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+ NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3], argv[4]);
+ unlink(path);
+ NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+ NS_T("%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3]);
+ rmdir(path);
+ NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
+ NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
+ rmdir(path);
+ return 0;
+#else
+ // Not implemented on non-Unix platforms
+ return 1;
+#endif
+ }
+
+ if (!NS_tstrcmp(argv[1], NS_T("check-symlink"))) {
+#ifdef XP_UNIX
+ struct stat ss;
+ lstat(argv[2], &ss);
+ return S_ISLNK(ss.st_mode) ? 0 : 1;
+#else
+ // Not implemented on non-Unix platforms
+ return 1;
+#endif
+ }
+
+ if (!NS_tstrcmp(argv[1], NS_T("wait-for-service-stop"))) {
+#ifdef XP_WIN
+ const int maxWaitSeconds = NS_ttoi(argv[3]);
+ LPCWSTR serviceName = argv[2];
+ DWORD serviceState = WaitForServiceStop(serviceName, maxWaitSeconds);
+ if (SERVICE_STOPPED == serviceState) {
+ return 0;
+ } else {
+ return serviceState;
+ }
+#else
+ // Not implemented on non-Windows platforms
+ return 1;
+#endif
+ }
+
+ if (!NS_tstrcmp(argv[1], NS_T("wait-for-application-exit"))) {
+#ifdef XP_WIN
+ const int maxWaitSeconds = NS_ttoi(argv[3]);
+ LPCWSTR application = argv[2];
+ DWORD ret = WaitForProcessExit(application, maxWaitSeconds);
+ if (ERROR_SUCCESS == ret) {
+ return 0;
+ } else if (WAIT_TIMEOUT == ret) {
+ return 1;
+ } else {
+ return 2;
+ }
+#else
+ // Not implemented on non-Windows platforms
+ return 1;
+#endif
+ }
+
+ if (!NS_tstrcmp(argv[1], NS_T("is-process-running"))) {
+#ifdef XP_WIN
+ LPCWSTR application = argv[2];
+ return (ERROR_NOT_FOUND == IsProcessRunning(application)) ? 0 : 1;
+#else
+ // Not implemented on non-Windows platforms
+ return 1;
+#endif
+ }
+
+ if (!NS_tstrcmp(argv[1], NS_T("launch-service"))) {
+#ifdef XP_WIN
+ DWORD ret = LaunchServiceSoftwareUpdateCommand(argc - 2, (LPCWSTR *)argv + 2);
+ if (ret != ERROR_SUCCESS) {
+ // 192 is used to avoid reusing a possible return value from the call to
+ // WaitForServiceStop
+ return 0x000000C0;
+ }
+ // Wait a maximum of 120 seconds.
+ DWORD lastState = WaitForServiceStop(SVC_NAME, 120);
+ if (SERVICE_STOPPED == lastState) {
+ return 0;
+ }
+ return lastState;
+#else
+ // Not implemented on non-Windows platforms
+ return 1;
+#endif
+ }
+
+ 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];
+ NS_tsnprintf(inFilePath, sizeof(inFilePath)/sizeof(inFilePath[0]),
+ NS_T("%s/%s"), cwd, argv[2]);
+ NS_tchar outFilePath[MAXPATHLEN];
+ NS_tsnprintf(outFilePath, sizeof(outFilePath)/sizeof(outFilePath[0]),
+ NS_T("%s/%s"), cwd, argv[3]);
+
+ int seconds = NS_ttoi(argv[5]);
+#ifdef XP_WIN
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ if (argc == 7) {
+ hFile = CreateFileW(argv[6],
+ DELETE | GENERIC_WRITE, 0,
+ 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]);
+
+ 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;
+ }
+
+ return 0;
+}
diff --git a/toolkit/mozapps/update/tests/TestAUSReadStrings.cpp b/toolkit/mozapps/update/tests/TestAUSReadStrings.cpp
new file mode 100644
index 000000000..c1de44f8e
--- /dev/null
+++ b/toolkit/mozapps/update/tests/TestAUSReadStrings.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/**
+ * This binary tests the updater's ReadStrings ini parser and should run in a
+ * directory with a Unicode character to test bug 473417.
+ */
+#ifdef XP_WIN
+ #include <windows.h>
+ #define NS_main wmain
+ #define NS_tstrrchr wcsrchr
+ #define NS_T(str) L ## str
+ #define PATH_SEPARATOR_CHAR L'\\'
+ // On Windows, argv[0] can also have forward slashes instead
+ #define ALT_PATH_SEPARATOR_CHAR L'/'
+#else
+ #include <unistd.h>
+ #define NS_main main
+ #define NS_tstrrchr strrchr
+ #define NS_T(str) str
+ #define PATH_SEPARATOR_CHAR '/'
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "updater/resource.h"
+#include "updater/progressui.h"
+#include "common/readstrings.h"
+#include "common/errors.h"
+#include "mozilla/ArrayUtils.h"
+
+#ifndef MAXPATHLEN
+# ifdef PATH_MAX
+# define MAXPATHLEN PATH_MAX
+# elif defined(MAX_PATH)
+# define MAXPATHLEN MAX_PATH
+# elif defined(_MAX_PATH)
+# define MAXPATHLEN _MAX_PATH
+# elif defined(CCHMAXPATH)
+# define MAXPATHLEN CCHMAXPATH
+# else
+# define MAXPATHLEN 1024
+# endif
+#endif
+
+#define TEST_NAME "Updater ReadStrings"
+
+using namespace mozilla;
+
+static int gFailCount = 0;
+
+/**
+ * Prints the given failure message and arguments using printf, prepending
+ * "TEST-UNEXPECTED-FAIL " for the benefit of the test harness and
+ * appending "\n" to eliminate having to type it at each call site.
+ */
+void fail(const char* msg, ...)
+{
+ va_list ap;
+
+ printf("TEST-UNEXPECTED-FAIL | ");
+
+ va_start(ap, msg);
+ vprintf(msg, ap);
+ va_end(ap);
+
+ putchar('\n');
+ ++gFailCount;
+}
+
+int NS_main(int argc, NS_tchar **argv)
+{
+ printf("Running TestAUSReadStrings tests\n");
+
+ int rv = 0;
+ int retval;
+ NS_tchar inifile[MAXPATHLEN];
+ StringTable testStrings;
+
+ NS_tchar *slash = NS_tstrrchr(argv[0], PATH_SEPARATOR_CHAR);
+#ifdef ALT_PATH_SEPARATOR_CHAR
+ NS_tchar *altslash = NS_tstrrchr(argv[0], ALT_PATH_SEPARATOR_CHAR);
+ slash = (slash > altslash) ? slash : altslash;
+#endif // ALT_PATH_SEPARATOR_CHAR
+
+ if (!slash) {
+ fail("%s | unable to find platform specific path separator (check 1)", TEST_NAME);
+ return 20;
+ }
+
+ *(++slash) = '\0';
+ // Test success when the ini file exists with both Title and Info in the
+ // Strings section and the values for Title and Info.
+ NS_tsnprintf(inifile, ArrayLength(inifile), NS_T("%sTestAUSReadStrings1.ini"), argv[0]);
+ retval = ReadStrings(inifile, &testStrings);
+ if (retval == OK) {
+ if (strcmp(testStrings.title, "Title Test - \xD0\x98\xD1\x81\xD0\xBF\xD1\x8B" \
+ "\xD1\x82\xD0\xB0\xD0\xBD\xD0\xB8\xD0\xB5 " \
+ "\xCE\x94\xCE\xBF\xCE\xBA\xCE\xB9\xCE\xBC\xCE\xAE " \
+ "\xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88 " \
+ "\xE6\xB8\xAC\xE8\xA9\xA6 " \
+ "\xE6\xB5\x8B\xE8\xAF\x95") != 0) {
+ rv = 21;
+ fail("%s | Title ini value incorrect (check 3)", TEST_NAME);
+ }
+
+ if (strcmp(testStrings.info, "Info Test - \xD0\x98\xD1\x81\xD0\xBF\xD1\x8B" \
+ "\xD1\x82\xD0\xB0\xD0\xBD\xD0\xB8\xD0\xB5 " \
+ "\xCE\x94\xCE\xBF\xCE\xBA\xCE\xB9\xCE\xBC\xCE\xAE " \
+ "\xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88 " \
+ "\xE6\xB8\xAC\xE8\xA9\xA6 " \
+ "\xE6\xB5\x8B\xE8\xAF\x95\xE2\x80\xA6") != 0) {
+ rv = 22;
+ fail("%s | Info ini value incorrect (check 4)", TEST_NAME);
+ }
+ } else {
+ fail("%s | ReadStrings returned %i (check 2)", TEST_NAME, retval);
+ rv = 23;
+ }
+
+ // Test failure when the ini file exists without Title and with Info in the
+ // Strings section.
+ NS_tsnprintf(inifile, ArrayLength(inifile), NS_T("%sTestAUSReadStrings2.ini"), argv[0]);
+ retval = ReadStrings(inifile, &testStrings);
+ if (retval != PARSE_ERROR) {
+ rv = 24;
+ fail("%s | ReadStrings returned %i (check 5)", TEST_NAME, retval);
+ }
+
+ // Test failure when the ini file exists with Title and without Info in the
+ // Strings section.
+ NS_tsnprintf(inifile, ArrayLength(inifile), NS_T("%sTestAUSReadStrings3.ini"), argv[0]);
+ retval = ReadStrings(inifile, &testStrings);
+ if (retval != PARSE_ERROR) {
+ rv = 25;
+ fail("%s | ReadStrings returned %i (check 6)", TEST_NAME, retval);
+ }
+
+ // Test failure when the ini file doesn't exist
+ NS_tsnprintf(inifile, ArrayLength(inifile), NS_T("%sTestAUSReadStringsBogus.ini"), argv[0]);
+ retval = ReadStrings(inifile, &testStrings);
+ if (retval != READ_ERROR) {
+ rv = 26;
+ fail("%s | ini file doesn't exist (check 7)", TEST_NAME);
+ }
+
+ // Test reading a non-default section name
+ NS_tsnprintf(inifile, ArrayLength(inifile), NS_T("%sTestAUSReadStrings3.ini"), argv[0]);
+ retval = ReadStrings(inifile, "Title\0", 1, &testStrings.title, "BogusSection2");
+ if (retval == OK) {
+ if (strcmp(testStrings.title, "Bogus Title") != 0) {
+ rv = 27;
+ fail("%s | Title ini value incorrect (check 9)", TEST_NAME);
+ }
+ } else {
+ fail("%s | ReadStrings returned %i (check 8)", TEST_NAME, retval);
+ rv = 28;
+ }
+
+
+ if (rv == 0) {
+ printf("TEST-PASS | %s | all checks passed\n", TEST_NAME);
+ } else {
+ fail("%s | %i out of 9 checks failed", TEST_NAME, gFailCount);
+ }
+
+ return rv;
+}
diff --git a/toolkit/mozapps/update/tests/TestAUSReadStrings1.ini b/toolkit/mozapps/update/tests/TestAUSReadStrings1.ini
new file mode 100644
index 000000000..5ab13c185
--- /dev/null
+++ b/toolkit/mozapps/update/tests/TestAUSReadStrings1.ini
@@ -0,0 +1,47 @@
+; This file is in the UTF-8 encoding
+
+[BogusSection1]
+
+; Comment
+
+Title=Bogus Title
+
+; Comment
+
+Info=Bogus Info
+
+; Comment
+
+[Strings]
+
+Bogus1=Bogus1
+
+; Comment
+
+Title=Title Test - ИÑпытание Δοκιμή テスト 測試 测试
+
+; Comment
+
+Bogus2=Bogus2
+
+; Comment
+
+Info=Info Test - ИÑпытание Δοκιμή テスト 測試 测试…
+
+; Comment
+
+Bogus3=Bogus3
+
+; Comment
+
+[BogusSection2]
+
+; Comment
+
+Title=Bogus Title
+
+; Comment
+
+Info=Bogus Info
+
+; Comment
diff --git a/toolkit/mozapps/update/tests/TestAUSReadStrings2.ini b/toolkit/mozapps/update/tests/TestAUSReadStrings2.ini
new file mode 100644
index 000000000..8291a7c94
--- /dev/null
+++ b/toolkit/mozapps/update/tests/TestAUSReadStrings2.ini
@@ -0,0 +1,39 @@
+; This file is in the UTF-8 encoding
+
+[BogusSection1]
+
+; Comment
+
+Title=Bogus Title
+
+; Comment
+
+Info=Bogus Info
+
+; Comment
+
+[Strings]
+
+Bogus1=Bogus1
+
+; Comment
+
+Info=Info
+
+; Comment
+
+Bogus2=Bogus2
+
+; Comment
+
+[BogusSection2]
+
+; Comment
+
+Title=Bogus Title
+
+; Comment
+
+Info=Bogus Info
+
+; Comment
diff --git a/toolkit/mozapps/update/tests/TestAUSReadStrings3.ini b/toolkit/mozapps/update/tests/TestAUSReadStrings3.ini
new file mode 100644
index 000000000..a64d1232e
--- /dev/null
+++ b/toolkit/mozapps/update/tests/TestAUSReadStrings3.ini
@@ -0,0 +1,39 @@
+; This file is in the UTF-8 encoding
+
+[BogusSection1]
+
+; Comment
+
+Title=Bogus Title
+
+; Comment
+
+Info=Bogus Info
+
+; Comment
+
+[Strings]
+
+Bogus1=Bogus1
+
+; Comment
+
+Title=Title
+
+; Comment
+
+Bogus2=Bogus2
+
+; Comment
+
+[BogusSection2]
+
+; Comment
+
+Title=Bogus Title
+
+; Comment
+
+Info=Bogus Info
+
+; Comment
diff --git a/toolkit/mozapps/update/tests/chrome/.eslintrc.js b/toolkit/mozapps/update/tests/chrome/.eslintrc.js
new file mode 100644
index 000000000..8c0f4f574
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/mochitest/chrome.eslintrc.js"
+ ]
+};
diff --git a/toolkit/mozapps/update/tests/chrome/chrome.ini b/toolkit/mozapps/update/tests/chrome/chrome.ini
new file mode 100644
index 000000000..88e3dd4e8
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/chrome.ini
@@ -0,0 +1,64 @@
+; 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/.
+
+[DEFAULT]
+tags = appupdate
+support-files =
+ utils.js
+ update.sjs
+
+; mochitest-chrome tests must start with "test_" and are executed in sorted
+; order and not in the order specified in the manifest.
+[test_0010_background_basic.xul]
+[test_0011_check_basic.xul]
+[test_0012_check_basic_staging.xul]
+skip-if = asan
+reason = Bug 1168003
+[test_0013_check_no_updates.xul]
+[test_0014_check_error_xml_malformed.xul]
+[test_0061_check_verifyFailPartial_noComplete.xul]
+[test_0062_check_verifyFailComplete_noPartial.xul]
+[test_0063_check_verifyFailPartialComplete.xul]
+[test_0064_check_verifyFailPartial_successComplete.xul]
+[test_0071_notify_verifyFailPartial_noComplete.xul]
+[test_0072_notify_verifyFailComplete_noPartial.xul]
+[test_0073_notify_verifyFailPartialComplete.xul]
+[test_0074_notify_verifyFailPartial_successComplete.xul]
+[test_0081_error_patchApplyFailure_partial_only.xul]
+[test_0082_error_patchApplyFailure_complete_only.xul]
+[test_0083_error_patchApplyFailure_partial_complete.xul]
+[test_0084_error_patchApplyFailure_verify_failed.xul]
+[test_0085_error_patchApplyFailure_partial_complete_staging.xul]
+skip-if = asan
+reason = Bug 1168003
+[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 = asan
+reason = Bug 1168003
+[test_0103_background_restartNotification_stagingService.xul]
+skip-if = os != 'win'
+reason = only Windows has the maintenance service.
+[test_0111_neverButton_basic.xul]
+[test_0113_showNeverForVersionRemovedWithPref.xul]
+[test_0151_notify_backgroundCheckError.xul]
+[test_0152_notify_backgroundCheckOfflineRetry.xul]
+[test_0161_check_unsupported.xul]
+[test_0162_notify_unsupported.xul]
+[test_0171_check_noPerms_manual.xul]
+skip-if = os != 'win'
+reason = test must be able to prevent file deletion.
+[test_0172_notify_noPerms_manual.xul]
+skip-if = os != 'win'
+reason = test must be able to prevent file deletion.
+[test_9999_cleanup.xul]
diff --git a/toolkit/mozapps/update/tests/chrome/test_0010_background_basic.xul b/toolkit/mozapps/update/tests/chrome/test_0010_background_basic.xul
new file mode 100644
index 000000000..8d088cc8a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0010_background_basic.xul
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: basic, download, and finished"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_FOUND_BASIC,
+ buttonClick: "next"
+}, {
+ pageid: PAGEID_DOWNLOADING
+}, {
+ pageid: PAGEID_FINISHED,
+ buttonClick: "extra1"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1&showPrompt=1" +
+ getVersionParams();
+ setUpdateURL(url);
+
+ gAUS.checkForBackgroundUpdates();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0011_check_basic.xul b/toolkit/mozapps/update/tests/chrome/test_0011_check_basic.xul
new file mode 100644
index 000000000..12b5302a4
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0011_check_basic.xul
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: update check, basic, download, and finished"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_FOUND_BASIC,
+ buttonClick: "next"
+}, {
+ pageid: PAGEID_DOWNLOADING
+}, {
+ pageid: PAGEID_FINISHED,
+ buttonClick: "extra1"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1" + getVersionParams();
+ setUpdateURL(url);
+
+ gUP.checkForUpdates();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0012_check_basic_staging.xul b/toolkit/mozapps/update/tests/chrome/test_0012_check_basic_staging.xul
new file mode 100644
index 000000000..d910adc08
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0012_check_basic_staging.xul
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: update check, basic, download with staging, and finished"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_FOUND_BASIC,
+ 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();
+ setUpdateURL(url);
+
+ gUP.checkForUpdates();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0013_check_no_updates.xul b/toolkit/mozapps/update/tests/chrome/test_0013_check_no_updates.xul
new file mode 100644
index 000000000..c3f024c73
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0013_check_no_updates.xul
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: update check and no updates found"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_NO_UPDATES_FOUND,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?noUpdates=1";
+ setUpdateURL(url);
+
+ gUP.checkForUpdates();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0014_check_error_xml_malformed.xul b/toolkit/mozapps/update/tests/chrome/test_0014_check_error_xml_malformed.xul
new file mode 100644
index 000000000..f399a0096
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0014_check_error_xml_malformed.xul
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: update check and error (xml malformed)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_ERRORS,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?xmlMalformed=1";
+ setUpdateURL(url);
+
+ gUP.checkForUpdates();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0061_check_verifyFailPartial_noComplete.xul b/toolkit/mozapps/update/tests/chrome/test_0061_check_verifyFailPartial_noComplete.xul
new file mode 100644
index 000000000..1040c19e3
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0061_check_verifyFailPartial_noComplete.xul
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: update check, basic, download, and errors (partial patch with an invalid size)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_FOUND_BASIC,
+ buttonClick: "next"
+}, {
+ pageid: PAGEID_DOWNLOADING
+}, {
+ pageid: PAGEID_ERRORS,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1&partialPatchOnly=1" +
+ "&invalidPartialSize=1" + getVersionParams();
+ setUpdateURL(url);
+
+ gUP.checkForUpdates();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0062_check_verifyFailComplete_noPartial.xul b/toolkit/mozapps/update/tests/chrome/test_0062_check_verifyFailComplete_noPartial.xul
new file mode 100644
index 000000000..9221a4b98
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0062_check_verifyFailComplete_noPartial.xul
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: update check, basic, download, and errors (complete patch with an invalid size)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_FOUND_BASIC,
+ buttonClick: "next"
+}, {
+ pageid: PAGEID_DOWNLOADING
+}, {
+ pageid: PAGEID_ERRORS,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1&completePatchOnly=1" +
+ "&invalidCompleteSize=1" + getVersionParams();
+ setUpdateURL(url);
+
+ gUP.checkForUpdates();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0063_check_verifyFailPartialComplete.xul b/toolkit/mozapps/update/tests/chrome/test_0063_check_verifyFailPartialComplete.xul
new file mode 100644
index 000000000..8da5c7e97
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0063_check_verifyFailPartialComplete.xul
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: update check, basic, download, and errors (partial and complete patches with invalid sizes)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_FOUND_BASIC,
+ buttonClick: "next"
+}, {
+ pageid: PAGEID_DOWNLOADING
+}, {
+ pageid: PAGEID_ERRORS,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1&invalidPartialSize=1" +
+ "&invalidCompleteSize=1" + getVersionParams();
+ setUpdateURL(url);
+
+ gUP.checkForUpdates();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0064_check_verifyFailPartial_successComplete.xul b/toolkit/mozapps/update/tests/chrome/test_0064_check_verifyFailPartial_successComplete.xul
new file mode 100644
index 000000000..db0f33d2f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0064_check_verifyFailPartial_successComplete.xul
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: update check, basic, download, and finished (partial patch with an invalid size and successful complete patch)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_FOUND_BASIC,
+ buttonClick: "next"
+}, {
+ pageid: PAGEID_DOWNLOADING
+}, {
+ pageid: PAGEID_FINISHED,
+ buttonClick: "extra1"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1&invalidPartialSize=1" +
+ getVersionParams();
+ setUpdateURL(url);
+
+ gUP.checkForUpdates();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0071_notify_verifyFailPartial_noComplete.xul b/toolkit/mozapps/update/tests/chrome/test_0071_notify_verifyFailPartial_noComplete.xul
new file mode 100644
index 000000000..736df13a3
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0071_notify_verifyFailPartial_noComplete.xul
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: errors (partial patch with an invalid size)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_ERRORS,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let patches = getLocalPatchString("partial", null, null, null, "1234", null,
+ STATE_DOWNLOADING);
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version,
+ Services.appinfo.platformVersion);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_DOWNLOADING);
+ reloadUpdateManagerData();
+
+ testPostUpdateProcessing();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0072_notify_verifyFailComplete_noPartial.xul b/toolkit/mozapps/update/tests/chrome/test_0072_notify_verifyFailComplete_noPartial.xul
new file mode 100644
index 000000000..cafab4d27
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0072_notify_verifyFailComplete_noPartial.xul
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: errors (complete patch with an invalid size)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_ERRORS,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let patches = getLocalPatchString("complete", null, null, null, "1234", null,
+ STATE_DOWNLOADING);
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_DOWNLOADING);
+ reloadUpdateManagerData();
+
+ testPostUpdateProcessing();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0073_notify_verifyFailPartialComplete.xul b/toolkit/mozapps/update/tests/chrome/test_0073_notify_verifyFailPartialComplete.xul
new file mode 100644
index 000000000..c1db983a3
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0073_notify_verifyFailPartialComplete.xul
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: errors (partial and complete patches with invalid sizes)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_ERRORS,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let patches = getLocalPatchString("partial", null, null, null, "1234", null,
+ STATE_DOWNLOADING) +
+ getLocalPatchString("complete", null, null, null, "1234",
+ "false");
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version, null,
+ null, null, null, null, "false");
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_DOWNLOADING);
+ reloadUpdateManagerData();
+
+ testPostUpdateProcessing();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0074_notify_verifyFailPartial_successComplete.xul b/toolkit/mozapps/update/tests/chrome/test_0074_notify_verifyFailPartial_successComplete.xul
new file mode 100644
index 000000000..2c28da768
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0074_notify_verifyFailPartial_successComplete.xul
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: finishedBackground (partial patch with an invalid size and successful complete patch)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_FINISHED_BKGRD,
+ buttonClick: "extra1"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let patches = getLocalPatchString("partial", null, null, null, "1234", null,
+ STATE_DOWNLOADING) +
+ getLocalPatchString("complete", null, null, null, null,
+ "false");
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version, null,
+ null, null, null, null, "false");
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_DOWNLOADING);
+ reloadUpdateManagerData();
+
+ testPostUpdateProcessing();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0081_error_patchApplyFailure_partial_only.xul b/toolkit/mozapps/update/tests/chrome/test_0081_error_patchApplyFailure_partial_only.xul
new file mode 100644
index 000000000..10c34f63b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0081_error_patchApplyFailure_partial_only.xul
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: errors (partial only patch apply failure)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_ERRORS,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let patches = getLocalPatchString("partial", null, null, null, null, null,
+ STATE_PENDING);
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version, null,
+ null, null, null, null, "false");
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_FAILED_CRC_ERROR);
+ reloadUpdateManagerData();
+
+ testPostUpdateProcessing();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0082_error_patchApplyFailure_complete_only.xul b/toolkit/mozapps/update/tests/chrome/test_0082_error_patchApplyFailure_complete_only.xul
new file mode 100644
index 000000000..2c4b389f4
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0082_error_patchApplyFailure_complete_only.xul
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: errors (complete only patch apply failure)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_ERRORS,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let patches = getLocalPatchString("complete", null, null, null, null, null,
+ STATE_PENDING);
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_FAILED_CRC_ERROR);
+ reloadUpdateManagerData();
+
+ testPostUpdateProcessing();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0083_error_patchApplyFailure_partial_complete.xul b/toolkit/mozapps/update/tests/chrome/test_0083_error_patchApplyFailure_partial_complete.xul
new file mode 100644
index 000000000..01adb1c3d
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0083_error_patchApplyFailure_partial_complete.xul
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: error patching, download, and finished (partial failed and download complete)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_ERROR_PATCHING,
+ buttonClick: "next"
+}, {
+ pageid: PAGEID_DOWNLOADING,
+ extraStartFunction: createContinueFile
+}, {
+ pageid: PAGEID_FINISHED,
+ buttonClick: "extra1",
+ extraStartFunction: removeContinueFile
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ removeContinueFile();
+
+ // Specify the url to update.sjs with a slowDownloadMar param so the ui can
+ // load before the download completes.
+ let slowDownloadURL = URL_HTTP_UPDATE_XML + "?slowDownloadMar=1";
+ let patches = getLocalPatchString("partial", null, null, null, null, null,
+ STATE_PENDING) +
+ getLocalPatchString("complete", slowDownloadURL, null, null,
+ null, "false");
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version, null,
+ null, null, null, null, "false");
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_FAILED_CRC_ERROR);
+ reloadUpdateManagerData();
+
+ testPostUpdateProcessing();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0084_error_patchApplyFailure_verify_failed.xul b/toolkit/mozapps/update/tests/chrome/test_0084_error_patchApplyFailure_verify_failed.xul
new file mode 100644
index 000000000..2e0c2b41e
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0084_error_patchApplyFailure_verify_failed.xul
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: error patching, download, and errors (partial failed and download complete verification failure)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_ERROR_PATCHING,
+ buttonClick: "next"
+}, {
+ pageid: PAGEID_DOWNLOADING,
+ extraStartFunction: createContinueFile
+}, {
+ pageid: PAGEID_ERRORS,
+ buttonClick: "finish",
+ extraStartFunction: removeContinueFile
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ removeContinueFile();
+
+ // Specify the url to update.sjs with a slowDownloadMar param so the ui can
+ // load before the download completes.
+ let slowDownloadURL = URL_HTTP_UPDATE_XML + "?slowDownloadMar=1";
+ let patches = getLocalPatchString("partial", null, null, null, null, null,
+ STATE_PENDING) +
+ getLocalPatchString("complete", slowDownloadURL, "MD5",
+ null, "1234",
+ "false");
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version, null,
+ null, null, null, null, "false");
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_FAILED_CRC_ERROR);
+ reloadUpdateManagerData();
+
+ testPostUpdateProcessing();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0085_error_patchApplyFailure_partial_complete_staging.xul b/toolkit/mozapps/update/tests/chrome/test_0085_error_patchApplyFailure_partial_complete_staging.xul
new file mode 100644
index 000000000..fc83505f9
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0085_error_patchApplyFailure_partial_complete_staging.xul
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: error patching, download with staging, and finished (partial failed and download complete), with fast MAR download"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+// This test forces the download to complete before the "next" button on the
+// errorpatching wizard page is clicked. This is done by creating the continue
+// file when the wizard loads to start the download, then clicking the "next"
+// button in the download's onStopRequest event listener.
+
+const testDownloadListener = {
+ onStartRequest(aRequest, aContext) { },
+
+ onProgress(aRequest, aContext, aProgress, aMaxProgress) { },
+
+ onStatus(aRequest, aContext, aStatus, aStatusText) { },
+
+ onStopRequest(aRequest, aContext, aStatus) {
+ debugDump("clicking errorpatching page next button");
+ gDocElem.getButton("next").click();
+ gAUS.removeDownloadListener(this);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver,
+ Ci.nsIProgressEventSink])
+};
+
+let TESTS = [ {
+ pageid: PAGEID_ERROR_PATCHING,
+ extraCheckFunction: createContinueFile
+}, {
+ pageid: PAGEID_DOWNLOADING
+}, {
+ pageid: PAGEID_FINISHED,
+ buttonClick: "extra1",
+ extraStartFunction: removeContinueFile
+} ];
+
+gUseTestUpdater = true;
+
+function runTest() {
+ debugDump("entering");
+
+ removeContinueFile();
+
+ // Specify the url to update.sjs with a slowDownloadMar param so the ui can
+ // load before the download completes.
+ let slowDownloadURL = URL_HTTP_UPDATE_XML + "?slowDownloadMar=1";
+ let patches = getLocalPatchString("partial", null, null, null, null, null,
+ STATE_PENDING) +
+ getLocalPatchString("complete", slowDownloadURL, null, null,
+ null, "false");
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version, null,
+ null, null, null, null, "false");
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_FAILED_READ_ERROR);
+
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, true);
+
+ reloadUpdateManagerData();
+
+ testPostUpdateProcessing();
+
+ gAUS.addDownloadListener(testDownloadListener);
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0092_finishedBackground.xul b/toolkit/mozapps/update/tests/chrome/test_0092_finishedBackground.xul
new file mode 100644
index 000000000..a0b29ddea
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0092_finishedBackground.xul
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: finished background"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_FINISHED_BKGRD,
+ buttonClick: "extra1"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let patches = getLocalPatchString("complete", null, null, null, null, null,
+ STATE_PENDING);
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version,
+ Services.appinfo.platformVersion);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_SUCCEEDED);
+
+ reloadUpdateManagerData();
+
+ is(gUpdateManager.activeUpdate.state, "pending",
+ "The active update should have a state of pending");
+
+ gUP.showUpdateDownloaded(gUpdateManager.activeUpdate);
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0093_restartNotification.xul b/toolkit/mozapps/update/tests/chrome/test_0093_restartNotification.xul
new file mode 100644
index 000000000..6db5b9897
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0093_restartNotification.xul
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: restart notification pref promptWaitTime"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_FINISHED_BKGRD,
+ buttonClick: "extra1"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let patches = getLocalPatchString("complete", null, null, null, null, null,
+ STATE_PENDING);
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_SUCCEEDED);
+
+ Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 1);
+
+ reloadUpdateManagerData();
+
+ is(gUpdateManager.activeUpdate.state, STATE_PENDING,
+ "The active update should have a state of " + STATE_PENDING);
+
+ ok(gUpdateManager.activeUpdate.promptWaitTime == 1, "Checking that the " +
+ "update's promptWaitTime attribute value was set from the " +
+ PREF_APP_UPDATE_PROMPTWAITTIME + " preference");
+
+ gUP.showUpdateDownloaded(gUpdateManager.activeUpdate, true);
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0094_restartNotification_remote.xul b/toolkit/mozapps/update/tests/chrome/test_0094_restartNotification_remote.xul
new file mode 100644
index 000000000..6e72a42c1
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0094_restartNotification_remote.xul
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: restart notification xml promptWaitTime"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_FINISHED_BKGRD,
+ buttonClick: "extra1"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let patches = getLocalPatchString("complete", null, null, null, null, null,
+ STATE_PENDING);
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version, null,
+ null, null, null, null, false,
+ null, false, false, false, 1);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_SUCCEEDED);
+
+ reloadUpdateManagerData();
+
+ is(gUpdateManager.activeUpdate.state, STATE_PENDING,
+ "The active update should have a state of " + STATE_PENDING);
+
+ ok(gUpdateManager.activeUpdate.promptWaitTime == 1, "Checking that the " +
+ "update's promptWaitTime attribute value was set by the XML");
+
+ gUP.showUpdateDownloaded(gUpdateManager.activeUpdate, true);
+
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0095_restartNotification_remoteInvalidNumber.xul b/toolkit/mozapps/update/tests/chrome/test_0095_restartNotification_remoteInvalidNumber.xul
new file mode 100644
index 000000000..5b1b826a5
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0095_restartNotification_remoteInvalidNumber.xul
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: restart notification xml promptWaitTime with invalid number"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_FINISHED_BKGRD,
+ buttonClick: "extra1"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let patches = getLocalPatchString("complete", null, null, null, null, null,
+ STATE_PENDING);
+ let updates = getLocalUpdateString(patches, null, null, null,
+ Services.appinfo.version,
+ null, null,
+ null, null, null,
+ null, false, null,
+ false, false,
+ false, "invalidNumber");
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_SUCCEEDED);
+
+ Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 1);
+
+ reloadUpdateManagerData();
+
+ is(gUpdateManager.activeUpdate.state, STATE_PENDING,
+ "The active update should have a state of " + STATE_PENDING);
+
+ ok(gUpdateManager.activeUpdate.promptWaitTime == 1, "Checking that the " +
+ "update's promptWaitTime attribute value was set from the " +
+ PREF_APP_UPDATE_PROMPTWAITTIME + " preference");
+
+ gUP.showUpdateDownloaded(gUpdateManager.activeUpdate, true);
+
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0096_restartNotification_stagedBackground.xul b/toolkit/mozapps/update/tests/chrome/test_0096_restartNotification_stagedBackground.xul
new file mode 100644
index 000000000..b86861012
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0096_restartNotification_stagedBackground.xul
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: restart notification staged w/o service"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<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);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_APPLIED);
+
+ Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 1);
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, true);
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, false);
+
+ reloadUpdateManagerData();
+
+ is(gUpdateManager.activeUpdate.state, STATE_APPLIED,
+ "The active update should have a state of " + STATE_APPLIED);
+
+ ok(gUpdateManager.activeUpdate.promptWaitTime == 1, "Checking that the " +
+ "update's promptWaitTime attribute value was set from the " +
+ PREF_APP_UPDATE_PROMPTWAITTIME + " preference");
+
+ gUpdateManager.refreshUpdateStatus();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0097_restartNotification_stagedServiceBackground.xul b/toolkit/mozapps/update/tests/chrome/test_0097_restartNotification_stagedServiceBackground.xul
new file mode 100644
index 000000000..9f7a602c4
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0097_restartNotification_stagedServiceBackground.xul
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: restart notification staged with service"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<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);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ writeStatusFile(STATE_APPLIED_SVC);
+
+ Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 1);
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, true);
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, true);
+
+ reloadUpdateManagerData();
+
+ is(gUpdateManager.activeUpdate.state, STATE_APPLIED_SVC,
+ "The active update should have a state of " + STATE_APPLIED_SVC);
+
+ ok(gUpdateManager.activeUpdate.promptWaitTime == 1, "Checking that the " +
+ "update's promptWaitTime attribute value was set from the " +
+ PREF_APP_UPDATE_PROMPTWAITTIME + " preference");
+
+ gUpdateManager.refreshUpdateStatus();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0101_background_restartNotification.xul b/toolkit/mozapps/update/tests/chrome/test_0101_background_restartNotification.xul
new file mode 100644
index 000000000..faa60c08b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0101_background_restartNotification.xul
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: background finish with a background download"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_FINISHED_BKGRD,
+ buttonClick: "extra1"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 1);
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1" + getVersionParams();
+ setUpdateURL(url);
+
+ gAUS.notify(null);
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0102_background_restartNotification_staging.xul b/toolkit/mozapps/update/tests/chrome/test_0102_background_restartNotification_staging.xul
new file mode 100644
index 000000000..3e6f0fec8
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0102_background_restartNotification_staging.xul
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: background finish with a background download and update staging"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<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();
+ setUpdateURL(url);
+
+ gAUS.notify(null);
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0103_background_restartNotification_stagingService.xul b/toolkit/mozapps/update/tests/chrome/test_0103_background_restartNotification_stagingService.xul
new file mode 100644
index 000000000..c60a9fe49
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0103_background_restartNotification_stagingService.xul
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: background finish with a background download and update staging and servicefs"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<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();
+ setUpdateURL(url);
+
+ gAUS.notify(null);
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0111_neverButton_basic.xul b/toolkit/mozapps/update/tests/chrome/test_0111_neverButton_basic.xul
new file mode 100644
index 000000000..adca621d9
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0111_neverButton_basic.xul
@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: update check and basic (never button test)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+gPrefToCheck = PREFBRANCH_APP_UPDATE_NEVER + Services.appinfo.version;
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_FOUND_BASIC,
+ extraDelayedCheckFunction: checkPrefHasUserValue,
+ prefHasUserValue: false,
+ neverButton: true,
+ buttonClick: "extra2"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?showNever=1&showDetails=1" +
+ getVersionParams();
+ setUpdateURL(url);
+
+ // add the never preference for this version to verify that checking for
+ // updates clears the preference.
+ Services.prefs.setBoolPref(gPrefToCheck, true)
+
+ gUP.checkForUpdates();
+}
+
+function finishTest() {
+ checkPrefHasUserValue(true);
+ finishTestDefault();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0113_showNeverForVersionRemovedWithPref.xul b/toolkit/mozapps/update/tests/chrome/test_0113_showNeverForVersionRemovedWithPref.xul
new file mode 100644
index 000000000..89dd55ea1
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0113_showNeverForVersionRemovedWithPref.xul
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: update available with never pref and without showNeverForVersion"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+gPrefToCheck = PREFBRANCH_APP_UPDATE_NEVER + Services.appinfo.version;
+
+const TESTS = [ {
+ pageid: PAGEID_FOUND_BASIC,
+ extraDelayedCheckFunction: checkPrefHasUserValue,
+ prefHasUserValue: true,
+ buttonClick: "extra1"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1&showPrompt=1" +
+ getVersionParams();
+ setUpdateURL(url);
+
+ // add the never preference for this version to verify that checking for
+ // updates clears the preference.
+ Services.prefs.setBoolPref(gPrefToCheck, true)
+
+ gAUS.notify(null);
+}
+
+function finishTest() {
+ Services.prefs.clearUserPref(gPrefToCheck)
+ finishTestDefault();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0151_notify_backgroundCheckError.xul b/toolkit/mozapps/update/tests/chrome/test_0151_notify_backgroundCheckError.xul
new file mode 100644
index 000000000..13798e5c9
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0151_notify_backgroundCheckError.xul
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Test notification when multiple background check errors occur (bug 595455)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_ERROR_EXTRA,
+ extraDelayedCheckFunction: checkErrorExtraPage,
+ shouldBeHidden: false,
+ displayedTextElem: "bgErrorLabel",
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?xmlMalformed=1";
+ setUpdateURL(url);
+
+ errorsPrefObserver.init(PREF_APP_UPDATE_BACKGROUNDERRORS,
+ PREF_APP_UPDATE_BACKGROUNDMAXERRORS);
+
+ gAUS.notify(null);
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0152_notify_backgroundCheckOfflineRetry.xul b/toolkit/mozapps/update/tests/chrome/test_0152_notify_backgroundCheckOfflineRetry.xul
new file mode 100644
index 000000000..04e613418
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0152_notify_backgroundCheckOfflineRetry.xul
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Test that an update check that fails due to being offline is performed after going online"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_FOUND_BASIC,
+ buttonClick: "extra1"
+} ];
+
+const NETWORK_ERROR_OFFLINE = 111;
+var gProxyPrefValue;
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1" + getVersionParams();
+ setUpdateURL(url);
+
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true);
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_AUTO, false);
+
+ Services.io.offline = true;
+ gProxyPrefValue = Services.prefs.getIntPref("network.proxy.type");
+ Services.prefs.setIntPref("network.proxy.type", 0);
+
+ gUpdateChecker.checkForUpdates(updateCheckListener, true);
+}
+
+function resetOffline() {
+ Services.prefs.setIntPref("network.proxy.type", gProxyPrefValue);
+ Services.io.offline = false;
+}
+
+/* Update check listener */
+const updateCheckListener = {
+ onProgress: function UCL_onProgress(aRequest, aPosition, aTotalSize) {
+ },
+
+ onCheckComplete: function UCL_onCheckComplete(aRequest, aUpdates, aUpdateCount) {
+ let status = aRequest.status;
+ if (status == 0) {
+ status = aRequest.channel.QueryInterface(Ci.nsIRequest).status;
+ }
+ debugDump("url = " + aRequest.channel.originalURI.spec + ", " +
+ "request.status = " + status + ", " +
+ "updateCount = " + aUpdateCount);
+ ok(false, "Unexpected updateCheckListener::onCheckComplete called");
+ },
+
+ onError: function UCL_onError(aRequest, aUpdate) {
+ let status = aRequest.status;
+ if (status == 0) {
+ status = aRequest.channel.QueryInterface(Ci.nsIRequest).status;
+ }
+ is(status, Cr.NS_ERROR_OFFLINE,
+ "checking the request status value");
+ is(aUpdate.errorCode, NETWORK_ERROR_OFFLINE,
+ "checking the update error code");
+ debugDump("url = " + aRequest.channel.originalURI.spec + ", " +
+ "request.status = " + status + ", " +
+ "update.statusText = " +
+ (aUpdate.statusText ? aUpdate.statusText : "null"));
+ gAUS.onError(aRequest, aUpdate);
+ // Use a timeout to allow the XHR to complete
+ SimpleTest.executeSoon(resetOffline);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateCheckListener])
+};
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0161_check_unsupported.xul b/toolkit/mozapps/update/tests/chrome/test_0161_check_unsupported.xul
new file mode 100644
index 000000000..c8e8d837b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0161_check_unsupported.xul
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Test checking for updates when system is no longer supported (bug 843497)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_UNSUPPORTED,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ // When checking manually the unsupported page should still be shown even if
+ // it was shown previously.
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED, true);
+
+ let url = URL_HTTP_UPDATE_XML + "?unsupported=1";
+ setUpdateURL(url);
+
+ gUP.checkForUpdates();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0162_notify_unsupported.xul b/toolkit/mozapps/update/tests/chrome/test_0162_notify_unsupported.xul
new file mode 100644
index 000000000..d88d2092d
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0162_notify_unsupported.xul
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Test notification of updates when system is no longer supported (bug 843497)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_UNSUPPORTED,
+ buttonClick: "finish"
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let url = URL_HTTP_UPDATE_XML + "?unsupported=1";
+ setUpdateURL(url);
+
+ gAUS.notify(null);
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0171_check_noPerms_manual.xul b/toolkit/mozapps/update/tests/chrome/test_0171_check_noPerms_manual.xul
new file mode 100644
index 000000000..142c02baa
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0171_check_noPerms_manual.xul
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: manual"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_CHECKING
+}, {
+ pageid: PAGEID_MANUAL_UPDATE,
+ buttonClick: "finish",
+ extraCheckFunction: getWriteTestFile
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let file = getWriteTestFile();
+ file.create(file.NORMAL_FILE_TYPE, 0o444);
+ file.fileAttributesWin |= file.WFA_READONLY;
+ file.fileAttributesWin &= ~file.WFA_READWRITE;
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1" + getVersionParams();
+ setUpdateURL(url);
+
+ gUP.checkForUpdates();
+}
+
+function getWriteTestFile() {
+ let file = getAppBaseDir();
+ file.append(FILE_UPDATE_TEST);
+ file.QueryInterface(Ci.nsILocalFileWin);
+ if (file.exists()) {
+ file.fileAttributesWin |= file.WFA_READWRITE;
+ file.fileAttributesWin &= ~file.WFA_READONLY;
+ file.remove(true);
+ }
+ return file;
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_0172_notify_noPerms_manual.xul b/toolkit/mozapps/update/tests/chrome/test_0172_notify_noPerms_manual.xul
new file mode 100644
index 000000000..6784b9c90
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_0172_notify_noPerms_manual.xul
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Update Wizard pages: manual"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTestDefault();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+const TESTS = [ {
+ pageid: PAGEID_MANUAL_UPDATE,
+ buttonClick: "finish",
+ extraCheckFunction: getWriteTestFile
+} ];
+
+function runTest() {
+ debugDump("entering");
+
+ let file = getWriteTestFile();
+ file.create(file.NORMAL_FILE_TYPE, 0o444);
+ file.fileAttributesWin |= file.WFA_READONLY;
+ file.fileAttributesWin &= ~file.WFA_READWRITE;
+
+ let url = URL_HTTP_UPDATE_XML + "?showDetails=1&showPrompt=1" +
+ getVersionParams();
+ setUpdateURL(url);
+
+ gAUS.checkForBackgroundUpdates();
+}
+
+function getWriteTestFile() {
+ let file = getAppBaseDir();
+ file.append(FILE_UPDATE_TEST);
+ file.QueryInterface(Ci.nsILocalFileWin);
+ if (file.exists()) {
+ file.fileAttributesWin |= file.WFA_READWRITE;
+ file.fileAttributesWin &= ~file.WFA_READONLY;
+ file.remove(true);
+ }
+ return file;
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/test_9999_cleanup.xul b/toolkit/mozapps/update/tests/chrome/test_9999_cleanup.xul
new file mode 100644
index 000000000..a55263eb2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/test_9999_cleanup.xul
@@ -0,0 +1,112 @@
+<?xml version="1.0"?>
+<!--
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Removes files and preferences for previous application update tests in case
+ * any of them had a fatal error. The test name ensures that it will run after
+ * all other tests as long as the test naming uses the same format as the
+ * existing tests.
+ */
+-->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Application Update test cleanup"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTest();">
+<script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<script type="application/javascript"
+ src="utils.js"/>
+
+<script type="application/javascript">
+<![CDATA[
+
+/**
+ * If the application update tests left behind any of the files it uses it could
+ * be a very bad thing. The purpose of this test is to prevent that from
+ * happening.
+ */
+function runTest() {
+ debugDump("entering");
+
+ SimpleTest.waitForExplicitFinish();
+
+ if (DEBUG_AUS_TEST) {
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true);
+ }
+
+ closeUpdateWindow();
+
+ // Always leave the app.update.enabled and app.update.staging.enabled
+ // preferences set to false when cleaning up.
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, false);
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, false);
+
+ resetFiles();
+ removeUpdateDirsAndFiles();
+ reloadUpdateManagerData();
+
+ let file = getUpdatesXMLFile(true);
+ ok(!file.exists(), file.path + " should not exist");
+
+ file = getUpdatesXMLFile(false);
+ ok(!file.exists(), file.path + " should not exist");
+
+ let dir = getUpdatesDir();
+
+ file = dir.clone();
+ file.append(FILE_UPDATE_STATUS);
+ ok(!file.exists(), file.path + " should not exist");
+
+ file = dir.clone();
+ file.append(FILE_UPDATE_MAR);
+ ok(!file.exists(), file.path + " should not exist");
+
+ 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);
+ }
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+</body>
+</window>
diff --git a/toolkit/mozapps/update/tests/chrome/update.sjs b/toolkit/mozapps/update/tests/chrome/update.sjs
new file mode 100644
index 000000000..78bb1b93f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/update.sjs
@@ -0,0 +1,194 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Server side http server script for application update tests.
+ */
+
+const { classes: Cc, interfaces: Ci } = Components;
+
+const REL_PATH_DATA = "chrome/toolkit/mozapps/update/tests/data/";
+
+function getTestDataFile(aFilename) {
+ let file = Cc["@mozilla.org/file/directory_service;1"].
+ getService(Ci.nsIProperties).get("CurWorkD", Ci.nsILocalFile);
+ let pathParts = REL_PATH_DATA.split("/");
+ for (let i = 0; i < pathParts.length; ++i) {
+ file.append(pathParts[i]);
+ }
+ if (aFilename) {
+ file.append(aFilename);
+ }
+ return file;
+}
+
+function loadHelperScript() {
+ let scriptFile = getTestDataFile("sharedUpdateXML.js");
+ let io = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService2);
+ let scriptSpec = io.newFileURI(scriptFile).spec;
+ let scriptloader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+ getService(Ci.mozIJSSubScriptLoader);
+ scriptloader.loadSubScript(scriptSpec, this);
+}
+loadHelperScript();
+
+const URL_HOST = "http://example.com";
+const URL_PATH_UPDATE_XML = "/chrome/toolkit/mozapps/update/tests/chrome/update.sjs";
+const URL_HTTP_UPDATE_SJS = URL_HOST + URL_PATH_UPDATE_XML;
+const SERVICE_URL = URL_HOST + "/" + REL_PATH_DATA + FILE_SIMPLE_MAR;
+
+const SLOW_MAR_DOWNLOAD_INTERVAL = 100;
+var gTimer;
+
+function handleRequest(aRequest, aResponse) {
+ let params = { };
+ if (aRequest.queryString) {
+ params = parseQueryString(aRequest.queryString);
+ }
+
+ let statusCode = params.statusCode ? parseInt(params.statusCode) : 200;
+ let statusReason = params.statusReason ? params.statusReason : "OK";
+ aResponse.setStatusLine(aRequest.httpVersion, statusCode, statusReason);
+ aResponse.setHeader("Cache-Control", "no-cache", false);
+
+ // When a mar download is started by the update service it can finish
+ // downloading before the ui has loaded. By specifying a serviceURL for the
+ // update patch that points to this file and has a slowDownloadMar param the
+ // mar will be downloaded asynchronously which will allow the ui to load
+ // before the download completes.
+ if (params.slowDownloadMar) {
+ aResponse.processAsync();
+ aResponse.setHeader("Content-Type", "binary/octet-stream");
+ aResponse.setHeader("Content-Length", SIZE_SIMPLE_MAR);
+ var continueFile = getTestDataFile("continue");
+ var contents = readFileBytes(getTestDataFile(FILE_SIMPLE_MAR));
+ gTimer = Cc["@mozilla.org/timer;1"].
+ createInstance(Ci.nsITimer);
+ gTimer.initWithCallback(function(aTimer) {
+ if (continueFile.exists()) {
+ gTimer.cancel();
+ aResponse.write(contents);
+ aResponse.finish();
+ }
+ }, SLOW_MAR_DOWNLOAD_INTERVAL, Ci.nsITimer.TYPE_REPEATING_SLACK);
+ return;
+ }
+
+ if (params.uiURL) {
+ let remoteType = "";
+ if (!params.remoteNoTypeAttr && params.uiURL == "BILLBOARD") {
+ remoteType = " " + params.uiURL.toLowerCase() + "=\"1\"";
+ }
+ aResponse.write("<html><head><meta http-equiv=\"content-type\" content=" +
+ "\"text/html; charset=utf-8\"></head><body" +
+ remoteType + ">" + params.uiURL +
+ "<br><br>this is a test mar that will not affect your " +
+ "build.</body></html>");
+ return;
+ }
+
+ if (params.xmlMalformed) {
+ aResponse.write("xml error");
+ return;
+ }
+
+ if (params.noUpdates) {
+ aResponse.write(getRemoteUpdatesXMLString(""));
+ return;
+ }
+
+ if (params.unsupported) {
+ aResponse.write(getRemoteUpdatesXMLString(" <update type=\"major\" " +
+ "unsupported=\"true\" " +
+ "detailsURL=\"" + URL_HOST +
+ "\"></update>\n"));
+ return;
+ }
+
+ let size;
+ let patches = "";
+ if (!params.partialPatchOnly) {
+ size = SIZE_SIMPLE_MAR + (params.invalidCompleteSize ? "1" : "");
+ patches += getRemotePatchString("complete", SERVICE_URL, "SHA512",
+ SHA512_HASH_SIMPLE_MAR, size);
+ }
+
+ if (!params.completePatchOnly) {
+ size = SIZE_SIMPLE_MAR + (params.invalidPartialSize ? "1" : "");
+ patches += getRemotePatchString("partial", SERVICE_URL, "SHA512",
+ SHA512_HASH_SIMPLE_MAR, size);
+ }
+
+ let type = params.type ? params.type : "major";
+ let name = params.name ? params.name : "App Update Test";
+ let appVersion = params.appVersion ? params.appVersion : "999999.9";
+ let displayVersion = params.displayVersion ? params.displayVersion
+ : "version " + appVersion;
+ let buildID = params.buildID ? params.buildID : "01234567890123";
+ // XXXrstrong - not specifying a detailsURL will cause a leak due to bug 470244
+// let detailsURL = params.showDetails ? URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS" : null;
+ let detailsURL = URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS";
+ let showPrompt = params.showPrompt ? "true" : null;
+ let showNever = params.showNever ? "true" : null;
+ let promptWaitTime = params.promptWaitTime ? params.promptWaitTime : null;
+
+ let updates = getRemoteUpdateString(patches, type, "App Update Test",
+ displayVersion, appVersion, buildID,
+ detailsURL, showPrompt, showNever,
+ promptWaitTime);
+ aResponse.write(getRemoteUpdatesXMLString(updates));
+}
+
+/**
+ * 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) {
+ let paramArray = aQueryString.split("&");
+ let regex = /^([^=]+)=(.*)$/;
+ let params = {};
+ for (let i = 0, sz = paramArray.length; i < sz; i++) {
+ let match = regex.exec(paramArray[i]);
+ if (!match) {
+ throw "Bad parameter in queryString! '" + paramArray[i] + "'";
+ }
+ params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
+ }
+
+ return params;
+}
+
+/**
+ * Reads the binary contents of a file and returns it as a string.
+ *
+ * @param aFile
+ * The file to read from.
+ * @return The contents of the file as a string.
+ */
+function readFileBytes(aFile) {
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ fis.init(aFile, -1, -1, false);
+ let bis = Cc["@mozilla.org/binaryinputstream;1"].
+ createInstance(Ci.nsIBinaryInputStream);
+ bis.setInputStream(fis);
+ let data = [];
+ let count = fis.available();
+ while (count > 0) {
+ let bytes = bis.readByteArray(Math.min(65535, count));
+ data.push(String.fromCharCode.apply(null, bytes));
+ count -= bytes.length;
+ if (bytes.length == 0) {
+ throw "Nothing read from input stream!";
+ }
+ }
+ data.join('');
+ fis.close();
+ return data.toString();
+}
diff --git a/toolkit/mozapps/update/tests/chrome/utils.js b/toolkit/mozapps/update/tests/chrome/utils.js
new file mode 100644
index 000000000..31d0d2e5a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/chrome/utils.js
@@ -0,0 +1,1011 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test Definition
+ *
+ * Most tests can use an array named TESTS that will perform most if not all of
+ * the necessary checks. Each element in the array must be an object with the
+ * following possible properties. Additional properties besides the ones listed
+ * below can be added as needed.
+ *
+ * overrideCallback (optional)
+ * The function to call for the next test. This is typically called when the
+ * wizard page changes but can also be called for other events by the previous
+ * test. If this property isn't defined then the defaultCallback function will
+ * be called. If this property is defined then all other properties are
+ * optional.
+ *
+ * pageid (required unless overrideCallback is specified)
+ * The expected pageid for the wizard. This property is required unless the
+ * overrideCallback property is defined.
+ *
+ * extraStartFunction (optional)
+ * The function to call at the beginning of the defaultCallback function. If
+ * the function returns true the defaultCallback function will return early
+ * which allows waiting for a specific condition to be evaluated in the
+ * function specified in the extraStartFunction property before continuing
+ * with the test.
+ *
+ * extraCheckFunction (optional)
+ * The function to call to perform extra checks in the defaultCallback
+ * function.
+ *
+ * extraDelayedCheckFunction (optional)
+ * The function to call to perform extra checks in the delayedDefaultCallback
+ * function.
+ *
+ * buttonStates (optional)
+ * A javascript object representing the expected hidden and disabled attribute
+ * values for the buttons of the current wizard page. The values are checked
+ * in the delayedDefaultCallback function. For information about the structure
+ * of this object refer to the getExpectedButtonStates and checkButtonStates
+ * functions.
+ *
+ * buttonClick (optional)
+ * The current wizard page button to click at the end of the
+ * delayedDefaultCallback function. If the buttonClick property is defined
+ * then the extraDelayedFinishFunction property can't be specified due to race
+ * conditions in some of the tests and if both of them are specified the test
+ * will intentionally throw.
+ *
+ * extraDelayedFinishFunction (optional)
+ * The function to call at the end of the delayedDefaultCallback function.
+ * If the extraDelayedFinishFunction property is defined then the buttonClick
+ * property can't be specified due to race conditions in some of the tests and
+ * if both of them are specified the test will intentionally throw.
+ *
+ * ranTest (should not be specified)
+ * When delayedDefaultCallback is called a property named ranTest is added to
+ * the current test so it is possible to verify that each test in the TESTS
+ * array has ran.
+ *
+ * prefHasUserValue (optional)
+ * For comparing the expected value defined by this property with the return
+ * value of prefHasUserValue using gPrefToCheck for the preference name in the
+ * checkPrefHasUserValue function.
+ */
+
+'use strict';
+
+/* globals TESTS, runTest, finishTest */
+
+const { classes: Cc, interfaces: Ci, manager: Cm, results: Cr,
+ utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+const IS_MACOSX = ("nsILocalFileMac" in Ci);
+const IS_WIN = ("@mozilla.org/windows-registry-key;1" in Cc);
+
+// The tests have to use the pageid instead of the pageIndex due to the
+// app update wizard's access method being random.
+const PAGEID_DUMMY = "dummy"; // Done
+const PAGEID_CHECKING = "checking"; // Done
+const PAGEID_NO_UPDATES_FOUND = "noupdatesfound"; // Done
+const PAGEID_MANUAL_UPDATE = "manualUpdate"; // Done
+const PAGEID_UNSUPPORTED = "unsupported"; // Done
+const PAGEID_FOUND_BASIC = "updatesfoundbasic"; // Done
+const PAGEID_DOWNLOADING = "downloading"; // Done
+const PAGEID_ERRORS = "errors"; // Done
+const PAGEID_ERROR_EXTRA = "errorextra"; // Done
+const PAGEID_ERROR_PATCHING = "errorpatching"; // Done
+const PAGEID_FINISHED = "finished"; // Done
+const PAGEID_FINISHED_BKGRD = "finishedBackground"; // Done
+
+const UPDATE_WINDOW_NAME = "Update:Wizard";
+
+const URL_HOST = "http://example.com";
+const URL_PATH_UPDATE_XML = "/chrome/toolkit/mozapps/update/tests/chrome/update.sjs";
+const REL_PATH_DATA = "chrome/toolkit/mozapps/update/tests/data";
+
+// These two URLs must not contain parameters since tests add their own
+// test specific parameters.
+const URL_HTTP_UPDATE_XML = URL_HOST + URL_PATH_UPDATE_XML;
+const URL_HTTPS_UPDATE_XML = "https://example.com" + URL_PATH_UPDATE_XML;
+
+const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
+
+const PREF_APP_UPDATE_INTERVAL = "app.update.interval";
+const PREF_APP_UPDATE_LASTUPDATETIME = "app.update.lastUpdateTime.background-update-timer";
+
+const LOG_FUNCTION = info;
+
+const BIN_SUFFIX = (IS_WIN ? ".exe" : "");
+const FILE_UPDATER_BIN = "updater" + (IS_MACOSX ? ".app" : BIN_SUFFIX);
+const FILE_UPDATER_BIN_BAK = FILE_UPDATER_BIN + ".bak";
+
+var gURLData = URL_HOST + "/" + REL_PATH_DATA + "/";
+
+var gTestTimeout = 240000; // 4 minutes
+var gTimeoutTimer;
+
+// The number of SimpleTest.executeSoon calls to perform when waiting on an
+// update window to close before giving up.
+const CLOSE_WINDOW_TIMEOUT_MAXCOUNT = 10;
+// Counter for the SimpleTest.executeSoon when waiting on an update window to
+// close before giving up.
+var gCloseWindowTimeoutCounter = 0;
+
+// The following vars are for restoring previous preference values (if present)
+// when the test finishes.
+var gAppUpdateEnabled; // app.update.enabled
+var gAppUpdateServiceEnabled; // app.update.service.enabled
+var gAppUpdateStagingEnabled; // app.update.staging.enabled
+var gAppUpdateURLDefault; // app.update.url (default prefbranch)
+
+var gTestCounter = -1;
+var gWin;
+var gDocElem;
+var gPrefToCheck;
+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 = true;
+
+const DATA_URI_SPEC = "chrome://mochitests/content/chrome/toolkit/mozapps/update/tests/data/";
+/* import-globals-from ../data/shared.js */
+Services.scriptloader.loadSubScript(DATA_URI_SPEC + "shared.js", this);
+
+/**
+ * The current test in TESTS array.
+ */
+this.__defineGetter__("gTest", function() {
+ return TESTS[gTestCounter];
+});
+
+/**
+ * The current test's callback. This will either return the callback defined in
+ * the test's overrideCallback property or defaultCallback if the
+ * overrideCallback property is undefined.
+ */
+this.__defineGetter__("gCallback", function() {
+ return gTest.overrideCallback ? gTest.overrideCallback
+ : defaultCallback;
+});
+
+/**
+ * nsIObserver for receiving window open and close notifications.
+ */
+const gWindowObserver = {
+ observe: function WO_observe(aSubject, aTopic, aData) {
+ let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
+
+ if (aTopic == "domwindowclosed") {
+ if (win.location != URI_UPDATE_PROMPT_DIALOG) {
+ debugDump("domwindowclosed event for window not being tested - " +
+ "location: " + win.location + "... returning early");
+ return;
+ }
+ // Allow tests the ability to provide their own function (it must be
+ // named finishTest) for finishing the test.
+ try {
+ finishTest();
+ }
+ catch (e) {
+ finishTestDefault();
+ }
+ return;
+ }
+
+ win.addEventListener("load", function WO_observe_onLoad() {
+ win.removeEventListener("load", WO_observe_onLoad, false);
+ // Ignore windows other than the update UI window.
+ if (win.location != URI_UPDATE_PROMPT_DIALOG) {
+ debugDump("load event for window not being tested - location: " +
+ win.location + "... returning early");
+ return;
+ }
+
+ // The first wizard page should always be the dummy page.
+ let pageid = win.document.documentElement.currentPage.pageid;
+ if (pageid != PAGEID_DUMMY) {
+ // This should never happen but if it does this will provide a clue
+ // for diagnosing the cause.
+ ok(false, "Unexpected load event - pageid got: " + pageid +
+ ", expected: " + PAGEID_DUMMY + "... returning early");
+ return;
+ }
+
+ gWin = win;
+ gDocElem = gWin.document.documentElement;
+ gDocElem.addEventListener("pageshow", onPageShowDefault, false);
+ }, false);
+ }
+};
+
+/**
+ * Default test run function that can be used by most tests. This function uses
+ * protective measures to prevent the test from failing provided by
+ * |runTestDefaultWaitForWindowClosed| helper functions to prevent failure due
+ * to a previous test failure.
+ */
+function runTestDefault() {
+ debugDump("entering");
+
+ if (!("@mozilla.org/zipwriter;1" in Cc)) {
+ ok(false, "nsIZipWriter is required to run these tests");
+ return;
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ runTestDefaultWaitForWindowClosed();
+}
+
+/**
+ * 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 continuing the test.
+ */
+function runTestDefaultWaitForWindowClosed() {
+ gCloseWindowTimeoutCounter++;
+ if (gCloseWindowTimeoutCounter > CLOSE_WINDOW_TIMEOUT_MAXCOUNT) {
+ try {
+ finishTest();
+ }
+ catch (e) {
+ finishTestDefault();
+ }
+ 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(runTestDefaultWaitForWindowClosed);
+ } else {
+ Services.ww.registerNotification(gWindowObserver);
+
+ gCloseWindowTimeoutCounter = 0;
+
+ setupFiles();
+ setupPrefs();
+ gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1");
+ removeUpdateDirsAndFiles();
+ reloadUpdateManagerData();
+ setupTimer(gTestTimeout);
+ SimpleTest.executeSoon(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.
+ */
+function finishTestDefault() {
+ debugDump("entering");
+ if (gTimeoutTimer) {
+ gTimeoutTimer.cancel();
+ gTimeoutTimer = null;
+ }
+
+ if (gChannel) {
+ 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);
+ }
+
+ 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
+ * The nsITimer that fired.
+ */
+function finishTestTimeout(aTimer) {
+ ok(false, "Test timed out. Maximum time allowed is " + (gTestTimeout / 1000) +
+ " seconds");
+
+ try {
+ 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);
+ } else {
+ SimpleTest.finish();
+ }
+}
+
+/**
+ * Default callback for the wizard's documentElement pageshow listener. This
+ * will return early for event's where the originalTarget's nodeName is not
+ * wizardpage.
+ */
+function onPageShowDefault(aEvent) {
+ if (!gTimeoutTimer) {
+ debugDump("gTimeoutTimer is null... returning early");
+ return;
+ }
+
+ // Return early if the event's original target isn't for a wizardpage element.
+ // This check is necessary due to the remotecontent element firing pageshow.
+ if (aEvent.originalTarget.nodeName != "wizardpage") {
+ debugDump("only handles events with an originalTarget nodeName of " +
+ "|wizardpage|. aEvent.originalTarget.nodeName = " +
+ aEvent.originalTarget.nodeName + "... returning early");
+ return;
+ }
+
+ gTestCounter++;
+ gCallback(aEvent);
+}
+
+/**
+ * Default callback that can be used by most tests.
+ */
+function defaultCallback(aEvent) {
+ if (!gTimeoutTimer) {
+ debugDump("gTimeoutTimer is null... returning early");
+ return;
+ }
+
+ debugDump("entering - TESTS[" + gTestCounter + "], pageid: " + gTest.pageid +
+ ", aEvent.originalTarget.nodeName: " +
+ aEvent.originalTarget.nodeName);
+
+ if (gTest && gTest.extraStartFunction) {
+ debugDump("calling extraStartFunction " + gTest.extraStartFunction.name);
+ if (gTest.extraStartFunction(aEvent)) {
+ debugDump("extraStartFunction early return");
+ return;
+ }
+ }
+
+ is(gDocElem.currentPage.pageid, gTest.pageid,
+ "Checking currentPage.pageid equals " + gTest.pageid + " in pageshow");
+
+ // Perform extra checks if specified by the test
+ if (gTest.extraCheckFunction) {
+ debugDump("calling extraCheckFunction " + gTest.extraCheckFunction.name);
+ gTest.extraCheckFunction();
+ }
+
+ // The wizard page buttons' disabled and hidden attributes are set after the
+ // pageshow event so use executeSoon to allow them to be set so their disabled
+ // and hidden attribute values can be checked.
+ SimpleTest.executeSoon(delayedDefaultCallback);
+}
+
+/**
+ * Delayed default callback called using executeSoon in defaultCallback which
+ * allows the wizard page buttons' disabled and hidden attributes to be set
+ * before checking their values.
+ */
+function delayedDefaultCallback() {
+ if (!gTimeoutTimer) {
+ debugDump("gTimeoutTimer is null... returning early");
+ return;
+ }
+
+ if (!gTest) {
+ debugDump("gTest is null... returning early");
+ return;
+ }
+
+ debugDump("entering - TESTS[" + gTestCounter + "], pageid: " + gTest.pageid);
+
+ // Verify the pageid hasn't changed after executeSoon was called.
+ is(gDocElem.currentPage.pageid, gTest.pageid,
+ "Checking currentPage.pageid equals " + gTest.pageid + " after " +
+ "executeSoon");
+
+ checkButtonStates();
+
+ // Perform delayed extra checks if specified by the test
+ if (gTest.extraDelayedCheckFunction) {
+ debugDump("calling extraDelayedCheckFunction " +
+ gTest.extraDelayedCheckFunction.name);
+ gTest.extraDelayedCheckFunction();
+ }
+
+ // Used to verify that this test has been performed
+ gTest.ranTest = true;
+
+ if (gTest.buttonClick) {
+ debugDump("clicking " + gTest.buttonClick + " button");
+ if (gTest.extraDelayedFinishFunction) {
+ throw ("Tests cannot have a buttonClick and an extraDelayedFinishFunction property");
+ }
+ gDocElem.getButton(gTest.buttonClick).click();
+ } else if (gTest.extraDelayedFinishFunction) {
+ debugDump("calling extraDelayedFinishFunction " +
+ gTest.extraDelayedFinishFunction.name);
+ gTest.extraDelayedFinishFunction();
+ }
+}
+
+/**
+ * Gets the continue file used to signal the mock http server to continue
+ * downloading for slow download mar file tests without creating it.
+ *
+ * @return nsILocalFile for the continue file.
+ */
+function getContinueFile() {
+ let continueFile = Cc["@mozilla.org/file/directory_service;1"].
+ getService(Ci.nsIProperties).
+ get("CurWorkD", Ci.nsILocalFile);
+ let continuePath = REL_PATH_DATA + "/continue";
+ let continuePathParts = continuePath.split("/");
+ for (let i = 0; i < continuePathParts.length; ++i) {
+ continueFile.append(continuePathParts[i]);
+ }
+ return continueFile;
+}
+
+/**
+ * Creates the continue file used to signal the mock http server to continue
+ * downloading for slow download mar file tests.
+ */
+function createContinueFile() {
+ debugDump("creating 'continue' file for slow mar downloads");
+ writeFile(getContinueFile(), "");
+}
+
+/**
+ * Removes the continue file used to signal the mock http server to continue
+ * downloading for slow download mar file tests.
+ */
+function removeContinueFile() {
+ let continueFile = getContinueFile();
+ if (continueFile.exists()) {
+ debugDump("removing 'continue' file for slow mar downloads");
+ continueFile.remove(false);
+ }
+}
+
+/**
+ * Checks the wizard page buttons' disabled and hidden attributes values are
+ * correct. If an expected button id is not specified then the expected disabled
+ * and hidden attribute value is true.
+ */
+function checkButtonStates() {
+ debugDump("entering - TESTS[" + gTestCounter + "], pageid: " + gTest.pageid);
+
+ const buttonNames = ["extra1", "extra2", "back", "next", "finish", "cancel"];
+ let buttonStates = getExpectedButtonStates();
+ buttonNames.forEach(function(aButtonName) {
+ let button = gDocElem.getButton(aButtonName);
+ let hasHidden = aButtonName in buttonStates &&
+ "hidden" in buttonStates[aButtonName];
+ let hidden = hasHidden ? buttonStates[aButtonName].hidden : true;
+ let hasDisabled = aButtonName in buttonStates &&
+ "disabled" in buttonStates[aButtonName];
+ let disabled = hasDisabled ? buttonStates[aButtonName].disabled : true;
+ is(button.hidden, hidden, "Checking " + aButtonName + " button " +
+ "hidden attribute value equals " + (hidden ? "true" : "false"));
+ is(button.disabled, disabled, "Checking " + aButtonName + " button " +
+ "disabled attribute value equals " + (disabled ? "true" : "false"));
+ });
+}
+
+/**
+ * Returns the expected disabled and hidden attribute values for the buttons of
+ * the current wizard page.
+ */
+function getExpectedButtonStates() {
+ // Allow individual tests to override the expected button states.
+ if (gTest.buttonStates) {
+ return gTest.buttonStates;
+ }
+
+ switch (gTest.pageid) {
+ case PAGEID_CHECKING:
+ return {cancel: {disabled: false, hidden: false}};
+ case PAGEID_FOUND_BASIC:
+ if (gTest.neverButton) {
+ return {extra1: {disabled: false, hidden: false},
+ extra2: {disabled: false, hidden: false},
+ next: {disabled: false, hidden: false}};
+ }
+ return {extra1: {disabled: false, hidden: false},
+ next: {disabled: false, hidden: false}};
+ case PAGEID_DOWNLOADING:
+ return {extra1: {disabled: false, hidden: false}};
+ case PAGEID_NO_UPDATES_FOUND:
+ case PAGEID_MANUAL_UPDATE:
+ case PAGEID_UNSUPPORTED:
+ case PAGEID_ERRORS:
+ case PAGEID_ERROR_EXTRA:
+ return {finish: {disabled: false, hidden: false}};
+ case PAGEID_ERROR_PATCHING:
+ return {next: { disabled: false, hidden: false}};
+ case PAGEID_FINISHED:
+ case PAGEID_FINISHED_BKGRD:
+ return {extra1: { disabled: false, hidden: false},
+ finish: { disabled: false, hidden: false}};
+ }
+ return null;
+}
+
+/**
+ * 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
+ * aPrefHasValue is undefined.
+ *
+ * @param aPrefHasValue (optional)
+ * The expected value returned from prefHasUserValue for the preference
+ * specified in gPrefToCheck. If aPrefHasValue is undefined the value
+ * of the current test's prefHasUserValue property will be used.
+ */
+function checkPrefHasUserValue(aPrefHasValue) {
+ let prefHasUserValue = aPrefHasValue === undefined ? gTest.prefHasUserValue
+ : aPrefHasValue;
+ is(Services.prefs.prefHasUserValue(gPrefToCheck), prefHasUserValue,
+ "Checking prefHasUserValue for preference " + gPrefToCheck + " equals " +
+ (prefHasUserValue ? "true" : "false"));
+}
+
+/**
+ * Checks whether the link is hidden for a general background update check error
+ * or not on the errorextra page and that the app.update.backgroundErrors
+ * preference does not have a user value.
+ *
+ * @param aShouldBeHidden (optional)
+ * The expected value for the label's hidden attribute for the link. If
+ * aShouldBeHidden is undefined the value of the current test's
+ * shouldBeHidden property will be used.
+ */
+function checkErrorExtraPage(aShouldBeHidden) {
+ let shouldBeHidden = aShouldBeHidden === undefined ? gTest.shouldBeHidden
+ : aShouldBeHidden;
+ is(gWin.document.getElementById("errorExtraLinkLabel").hidden, shouldBeHidden,
+ "Checking errorExtraLinkLabel hidden attribute equals " +
+ (shouldBeHidden ? "true" : "false"));
+
+ is(gWin.document.getElementById(gTest.displayedTextElem).hidden, false,
+ "Checking " + gTest.displayedTextElem + " should not be hidden");
+
+ ok(!Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS),
+ "Preference " + PREF_APP_UPDATE_BACKGROUNDERRORS + " should not have a " +
+ "user value");
+}
+
+/**
+ * Gets the update version info for the update url parameters to send to
+ * update.sjs.
+ *
+ * @param aAppVersion (optional)
+ * The application version for the update snippet. If not specified the
+ * current application version will be used.
+ * @return The url parameters for the application and platform version to send
+ * to update.sjs.
+ */
+function getVersionParams(aAppVersion) {
+ let appInfo = Services.appinfo;
+ return "&appVersion=" + (aAppVersion ? aAppVersion : appInfo.version);
+}
+
+/**
+ * Verifies that all tests ran.
+ */
+function verifyTestsRan() {
+ debugDump("entering");
+
+ // Return early if there are no tests defined.
+ if (!TESTS) {
+ return;
+ }
+
+ gTestCounter = -1;
+ for (let i = 0; i < TESTS.length; ++i) {
+ gTestCounter++;
+ let test = TESTS[i];
+ let msg = "Checking if TESTS[" + i + "] test was performed... " +
+ "callback function name = " + gCallback.name + ", " +
+ "pageid = " + test.pageid;
+ ok(test.ranTest, msg);
+ }
+}
+
+/**
+ * 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 = getGREDir();
+ 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);
+}
+
+/**
+ * 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;
+ }
+
+ try {
+ restoreUpdaterBackup();
+ } catch (e) {
+ logTestInfo("Attempt to restore the backed up updater failed... " +
+ "will try again, Exception: " + e);
+ SimpleTest.executeSoon(setupTestUpdater);
+ return;
+ }
+ moveRealUpdater();
+}
+
+/**
+ * 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;
+ }
+
+ 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.
+ */
+function setupPrefs() {
+ if (DEBUG_AUS_TEST) {
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true);
+ }
+
+ // Prevent nsIUpdateTimerManager from notifying nsIApplicationUpdateService
+ // to check for updates by setting the app update last update time to the
+ // current time minus one minute in seconds and the interval time to 12 hours
+ // in seconds.
+ let now = Math.round(Date.now() / 1000) - 60;
+ Services.prefs.setIntPref(PREF_APP_UPDATE_LASTUPDATETIME, now);
+ Services.prefs.setIntPref(PREF_APP_UPDATE_INTERVAL, 43200);
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ENABLED)) {
+ gAppUpdateEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED);
+ }
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true);
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_SERVICE_ENABLED)) {
+ gAppUpdateServiceEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED);
+ }
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, false);
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_STAGING_ENABLED)) {
+ gAppUpdateStagingEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED);
+ }
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, false);
+
+ Services.prefs.setIntPref(PREF_APP_UPDATE_IDLETIME, 0);
+ Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 0);
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, false);
+ Services.prefs.setIntPref(PREF_APP_UPDATE_DOWNLOADBACKGROUNDINTERVAL, 0);
+}
+
+/**
+ * Restores files that were backed up for the tests and general file cleanup.
+ */
+function resetFiles() {
+ // Restore the backed up updater-settings.ini if it exists.
+ let baseAppDir = getGREDir();
+ let updateSettingsIni = baseAppDir.clone();
+ updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI_BAK);
+ if (updateSettingsIni.exists()) {
+ updateSettingsIni.moveTo(baseAppDir, FILE_UPDATE_SETTINGS_INI);
+ }
+
+ // Not being able to remove the "updated" directory will not adversely affect
+ // subsequent tests so wrap it in a try block and don't test whether its
+ // removal was successful.
+ let updatedDir;
+ if (IS_MACOSX) {
+ updatedDir = getUpdatesDir();
+ updatedDir.append(DIR_PATCH);
+ } else {
+ updatedDir = getAppBaseDir();
+ }
+ updatedDir.append(DIR_UPDATED);
+ if (updatedDir.exists()) {
+ try {
+ removeDirRecursive(updatedDir);
+ }
+ catch (e) {
+ logTestInfo("Unable to remove directory. Path: " + updatedDir.path +
+ ", Exception: " + e);
+ }
+ }
+}
+
+/**
+ * Resets the most common preferences used by tests to their original values.
+ */
+function resetPrefs() {
+ if (gAppUpdateURLDefault) {
+ gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_URL, gAppUpdateURLDefault);
+ }
+
+ if (gAppUpdateEnabled !== undefined) {
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, gAppUpdateEnabled);
+ } else if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ENABLED)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_ENABLED);
+ }
+
+ if (gAppUpdateServiceEnabled !== undefined) {
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, gAppUpdateServiceEnabled);
+ } else if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_SERVICE_ENABLED)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_SERVICE_ENABLED);
+ }
+
+ if (gAppUpdateStagingEnabled !== undefined) {
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, gAppUpdateStagingEnabled);
+ } else if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_STAGING_ENABLED)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_STAGING_ENABLED);
+ }
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_IDLETIME)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_IDLETIME);
+ }
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_PROMPTWAITTIME)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_PROMPTWAITTIME);
+ }
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_URL_DETAILS)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_URL_DETAILS);
+ }
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED);
+ }
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_LOG)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_LOG);
+ }
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_SILENT)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_SILENT);
+ }
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
+ }
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDMAXERRORS)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDMAXERRORS);
+ }
+
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_DOWNLOADBACKGROUNDINTERVAL)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_DOWNLOADBACKGROUNDINTERVAL);
+ }
+
+ try {
+ Services.prefs.deleteBranch(PREFBRANCH_APP_UPDATE_NEVER);
+ }
+ catch (e) {
+ }
+}
+
+function setupTimer(aTestTimeout) {
+ gTestTimeout = aTestTimeout;
+ if (gTimeoutTimer) {
+ gTimeoutTimer.cancel();
+ gTimeoutTimer = null;
+ }
+ gTimeoutTimer = Cc["@mozilla.org/timer;1"].
+ createInstance(Ci.nsITimer);
+ gTimeoutTimer.initWithCallback(finishTestTimeout, gTestTimeout,
+ Ci.nsITimer.TYPE_ONE_SHOT);
+}
+
+/**
+ * Closes the update window if it is open and causes the test to fail if an
+ * update window is found.
+ *
+ * @return true if an update window was found, otherwise false.
+ */
+function closeUpdateWindow() {
+ let updateWindow = getUpdateWindow();
+ if (!updateWindow) {
+ return false;
+ }
+
+ ok(false, "Found an existing Update Window from the current or a previous " +
+ "test... attempting to close it.");
+ updateWindow.close();
+ return true;
+}
+
+/**
+ * Gets the update window.
+ *
+ * @return The nsIDOMWindow for the Update Window if it is open and null
+ * if it isn't.
+ */
+function getUpdateWindow() {
+ return Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME);
+}
+
+/**
+ * Helper for background check errors.
+ */
+const errorsPrefObserver = {
+ observedPref: null,
+ maxErrorPref: null,
+
+ /**
+ * Sets up a preference observer and sets the associated maximum errors
+ * preference used for background notification.
+ *
+ * @param aObservePref
+ * The preference to observe.
+ * @param aMaxErrorPref
+ * The maximum errors preference.
+ * @param aMaxErrorCount
+ * The value to set the maximum errors preference to.
+ */
+ init: function(aObservePref, aMaxErrorPref, aMaxErrorCount) {
+ this.observedPref = aObservePref;
+ this.maxErrorPref = aMaxErrorPref;
+
+ let maxErrors = aMaxErrorCount ? aMaxErrorCount : 2;
+ Services.prefs.setIntPref(aMaxErrorPref, maxErrors);
+ Services.prefs.addObserver(aObservePref, this, false);
+ },
+
+ /**
+ * Preference observer for the preference specified in |this.observedPref|.
+ */
+ observe: function XPI_observe(aSubject, aTopic, aData) {
+ if (aData == this.observedPref) {
+ let errCount = Services.prefs.getIntPref(this.observedPref);
+ let errMax = Services.prefs.getIntPref(this.maxErrorPref);
+ if (errCount >= errMax) {
+ debugDump("removing pref observer");
+ Services.prefs.removeObserver(this.observedPref, this);
+ } else {
+ debugDump("notifying AUS");
+ SimpleTest.executeSoon(function() {
+ gAUS.notify(null);
+ });
+ }
+ }
+ }
+};
diff --git a/toolkit/mozapps/update/tests/data/complete.exe b/toolkit/mozapps/update/tests/data/complete.exe
new file mode 100644
index 000000000..da9cdf0cc
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete.exe
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/complete.mar b/toolkit/mozapps/update/tests/data/complete.mar
new file mode 100644
index 000000000..52306e0a2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete.mar
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/complete.png b/toolkit/mozapps/update/tests/data/complete.png
new file mode 100644
index 000000000..2990a539f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete.png
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/complete_log_success_mac b/toolkit/mozapps/update/tests/data/complete_log_success_mac
new file mode 100644
index 000000000..4f992a137
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete_log_success_mac
@@ -0,0 +1,332 @@
+UPDATE TYPE complete
+PREPARE REMOVEFILE Contents/Resources/searchplugins/searchpluginstext0
+PREPARE REMOVEFILE Contents/Resources/searchplugins/searchpluginspng0.png
+PREPARE REMOVEFILE Contents/Resources/removed-files
+PREPARE REMOVEFILE Contents/Resources/precomplete
+PREPARE REMOVEFILE Contents/Resources/2/20/20text0
+PREPARE REMOVEFILE Contents/Resources/2/20/20png0.png
+PREPARE REMOVEFILE Contents/Resources/0/0exe0.exe
+PREPARE REMOVEFILE Contents/Resources/0/00/00text0
+PREPARE REMOVEFILE Contents/MacOS/exe0.exe
+PREPARE REMOVEDIR Contents/Resources/searchplugins/
+PREPARE REMOVEDIR Contents/Resources/defaults/pref/
+PREPARE REMOVEDIR Contents/Resources/defaults/
+PREPARE REMOVEDIR Contents/Resources/2/20/
+PREPARE REMOVEDIR Contents/Resources/2/
+PREPARE REMOVEDIR Contents/Resources/0/00/
+PREPARE REMOVEDIR Contents/Resources/0/
+PREPARE REMOVEDIR Contents/Resources/
+PREPARE REMOVEDIR Contents/MacOS/
+PREPARE REMOVEDIR Contents/
+PREPARE ADD Contents/Resources/searchplugins/searchpluginstext0
+PREPARE ADD Contents/Resources/searchplugins/searchpluginspng1.png
+PREPARE ADD Contents/Resources/searchplugins/searchpluginspng0.png
+PREPARE ADD Contents/Resources/removed-files
+PREPARE ADD Contents/Resources/precomplete
+PREPARE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0
+PREPARE ADD Contents/Resources/distribution/extensions/extensions1/extensions1png1.png
+PREPARE ADD Contents/Resources/distribution/extensions/extensions1/extensions1png0.png
+PREPARE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0
+PREPARE ADD Contents/Resources/distribution/extensions/extensions0/extensions0png1.png
+PREPARE ADD Contents/Resources/distribution/extensions/extensions0/extensions0png0.png
+PREPARE ADD Contents/Resources/1/10/10text0
+PREPARE ADD Contents/Resources/0/0exe0.exe
+PREPARE ADD Contents/Resources/0/00/00text1
+PREPARE ADD Contents/Resources/0/00/00text0
+PREPARE ADD Contents/Resources/0/00/00png0.png
+PREPARE ADD Contents/MacOS/exe0.exe
+PREPARE REMOVEDIR Contents/Resources/9/99/
+PREPARE REMOVEDIR Contents/Resources/9/99/
+PREPARE REMOVEDIR Contents/Resources/9/98/
+PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext0
+PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext1
+PREPARE REMOVEDIR Contents/Resources/9/97/970/
+PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext0
+PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext1
+PREPARE REMOVEDIR Contents/Resources/9/97/971/
+PREPARE REMOVEDIR Contents/Resources/9/97/
+PREPARE REMOVEFILE Contents/Resources/9/96/96text0
+PREPARE REMOVEFILE Contents/Resources/9/96/96text1
+PREPARE REMOVEDIR Contents/Resources/9/96/
+PREPARE REMOVEDIR Contents/Resources/9/95/
+PREPARE REMOVEDIR Contents/Resources/9/95/
+PREPARE REMOVEDIR Contents/Resources/9/94/
+PREPARE REMOVEDIR Contents/Resources/9/94/
+PREPARE REMOVEDIR Contents/Resources/9/93/
+PREPARE REMOVEDIR Contents/Resources/9/92/
+PREPARE REMOVEDIR Contents/Resources/9/91/
+PREPARE REMOVEDIR Contents/Resources/9/90/
+PREPARE REMOVEDIR Contents/Resources/9/90/
+PREPARE REMOVEDIR Contents/Resources/8/89/
+PREPARE REMOVEDIR Contents/Resources/8/89/
+PREPARE REMOVEDIR Contents/Resources/8/88/
+PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext0
+PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext1
+PREPARE REMOVEDIR Contents/Resources/8/87/870/
+PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext0
+PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext1
+PREPARE REMOVEDIR Contents/Resources/8/87/871/
+PREPARE REMOVEDIR Contents/Resources/8/87/
+PREPARE REMOVEFILE Contents/Resources/8/86/86text0
+PREPARE REMOVEFILE Contents/Resources/8/86/86text1
+PREPARE REMOVEDIR Contents/Resources/8/86/
+PREPARE REMOVEDIR Contents/Resources/8/85/
+PREPARE REMOVEDIR Contents/Resources/8/85/
+PREPARE REMOVEDIR Contents/Resources/8/84/
+PREPARE REMOVEDIR Contents/Resources/8/84/
+PREPARE REMOVEDIR Contents/Resources/8/83/
+PREPARE REMOVEDIR Contents/Resources/8/82/
+PREPARE REMOVEDIR Contents/Resources/8/81/
+PREPARE REMOVEDIR Contents/Resources/8/80/
+PREPARE REMOVEDIR Contents/Resources/8/80/
+PREPARE REMOVEFILE Contents/Resources/7/70/7xtest.exe
+PREPARE REMOVEFILE Contents/Resources/7/70/7xtext0
+PREPARE REMOVEFILE Contents/Resources/7/70/7xtext1
+PREPARE REMOVEDIR Contents/Resources/7/70/
+PREPARE REMOVEFILE Contents/Resources/7/71/7xtest.exe
+PREPARE REMOVEFILE Contents/Resources/7/71/7xtext0
+PREPARE REMOVEFILE Contents/Resources/7/71/7xtext1
+PREPARE REMOVEDIR Contents/Resources/7/71/
+PREPARE REMOVEFILE Contents/Resources/7/7text0
+PREPARE REMOVEFILE Contents/Resources/7/7text1
+PREPARE REMOVEDIR Contents/Resources/7/
+PREPARE REMOVEDIR Contents/Resources/6/
+PREPARE REMOVEFILE Contents/Resources/5/5text1
+PREPARE REMOVEFILE Contents/Resources/5/5text0
+PREPARE REMOVEFILE Contents/Resources/5/5test.exe
+PREPARE REMOVEFILE Contents/Resources/5/5text0
+PREPARE REMOVEFILE Contents/Resources/5/5text1
+PREPARE REMOVEDIR Contents/Resources/5/
+PREPARE REMOVEFILE Contents/Resources/4/4text1
+PREPARE REMOVEFILE Contents/Resources/4/4text0
+PREPARE REMOVEDIR Contents/Resources/4/
+PREPARE REMOVEFILE Contents/Resources/3/3text1
+PREPARE REMOVEFILE Contents/Resources/3/3text0
+EXECUTE REMOVEFILE Contents/Resources/searchplugins/searchpluginstext0
+EXECUTE REMOVEFILE Contents/Resources/searchplugins/searchpluginspng0.png
+EXECUTE REMOVEFILE Contents/Resources/removed-files
+EXECUTE REMOVEFILE Contents/Resources/precomplete
+EXECUTE REMOVEFILE Contents/Resources/2/20/20text0
+EXECUTE REMOVEFILE Contents/Resources/2/20/20png0.png
+EXECUTE REMOVEFILE Contents/Resources/0/0exe0.exe
+EXECUTE REMOVEFILE Contents/Resources/0/00/00text0
+EXECUTE REMOVEFILE Contents/MacOS/exe0.exe
+EXECUTE REMOVEDIR Contents/Resources/searchplugins/
+EXECUTE REMOVEDIR Contents/Resources/defaults/pref/
+EXECUTE REMOVEDIR Contents/Resources/defaults/
+EXECUTE REMOVEDIR Contents/Resources/2/20/
+EXECUTE REMOVEDIR Contents/Resources/2/
+EXECUTE REMOVEDIR Contents/Resources/0/00/
+EXECUTE REMOVEDIR Contents/Resources/0/
+EXECUTE REMOVEDIR Contents/Resources/
+EXECUTE REMOVEDIR Contents/MacOS/
+EXECUTE REMOVEDIR Contents/
+EXECUTE ADD Contents/Resources/searchplugins/searchpluginstext0
+EXECUTE ADD Contents/Resources/searchplugins/searchpluginspng1.png
+EXECUTE ADD Contents/Resources/searchplugins/searchpluginspng0.png
+EXECUTE ADD Contents/Resources/removed-files
+EXECUTE ADD Contents/Resources/precomplete
+EXECUTE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0
+EXECUTE ADD Contents/Resources/distribution/extensions/extensions1/extensions1png1.png
+EXECUTE ADD Contents/Resources/distribution/extensions/extensions1/extensions1png0.png
+EXECUTE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0
+EXECUTE ADD Contents/Resources/distribution/extensions/extensions0/extensions0png1.png
+EXECUTE ADD Contents/Resources/distribution/extensions/extensions0/extensions0png0.png
+EXECUTE ADD Contents/Resources/1/10/10text0
+EXECUTE ADD Contents/Resources/0/0exe0.exe
+EXECUTE ADD Contents/Resources/0/00/00text1
+EXECUTE ADD Contents/Resources/0/00/00text0
+EXECUTE ADD Contents/Resources/0/00/00png0.png
+EXECUTE ADD Contents/MacOS/exe0.exe
+EXECUTE REMOVEDIR Contents/Resources/9/99/
+EXECUTE REMOVEDIR Contents/Resources/9/99/
+EXECUTE REMOVEDIR Contents/Resources/9/98/
+EXECUTE REMOVEFILE Contents/Resources/9/97/970/97xtext0
+EXECUTE REMOVEFILE Contents/Resources/9/97/970/97xtext1
+EXECUTE REMOVEDIR Contents/Resources/9/97/970/
+EXECUTE REMOVEFILE Contents/Resources/9/97/971/97xtext0
+EXECUTE REMOVEFILE Contents/Resources/9/97/971/97xtext1
+EXECUTE REMOVEDIR Contents/Resources/9/97/971/
+EXECUTE REMOVEDIR Contents/Resources/9/97/
+EXECUTE REMOVEFILE Contents/Resources/9/96/96text0
+EXECUTE REMOVEFILE Contents/Resources/9/96/96text1
+EXECUTE REMOVEDIR Contents/Resources/9/96/
+EXECUTE REMOVEDIR Contents/Resources/9/95/
+EXECUTE REMOVEDIR Contents/Resources/9/95/
+EXECUTE REMOVEDIR Contents/Resources/9/94/
+EXECUTE REMOVEDIR Contents/Resources/9/94/
+EXECUTE REMOVEDIR Contents/Resources/9/93/
+EXECUTE REMOVEDIR Contents/Resources/9/92/
+EXECUTE REMOVEDIR Contents/Resources/9/91/
+EXECUTE REMOVEDIR Contents/Resources/9/90/
+EXECUTE REMOVEDIR Contents/Resources/9/90/
+EXECUTE REMOVEDIR Contents/Resources/8/89/
+EXECUTE REMOVEDIR Contents/Resources/8/89/
+EXECUTE REMOVEDIR Contents/Resources/8/88/
+EXECUTE REMOVEFILE Contents/Resources/8/87/870/87xtext0
+EXECUTE REMOVEFILE Contents/Resources/8/87/870/87xtext1
+EXECUTE REMOVEDIR Contents/Resources/8/87/870/
+EXECUTE REMOVEFILE Contents/Resources/8/87/871/87xtext0
+EXECUTE REMOVEFILE Contents/Resources/8/87/871/87xtext1
+EXECUTE REMOVEDIR Contents/Resources/8/87/871/
+EXECUTE REMOVEDIR Contents/Resources/8/87/
+EXECUTE REMOVEFILE Contents/Resources/8/86/86text0
+EXECUTE REMOVEFILE Contents/Resources/8/86/86text1
+EXECUTE REMOVEDIR Contents/Resources/8/86/
+EXECUTE REMOVEDIR Contents/Resources/8/85/
+EXECUTE REMOVEDIR Contents/Resources/8/85/
+EXECUTE REMOVEDIR Contents/Resources/8/84/
+EXECUTE REMOVEDIR Contents/Resources/8/84/
+EXECUTE REMOVEDIR Contents/Resources/8/83/
+EXECUTE REMOVEDIR Contents/Resources/8/82/
+EXECUTE REMOVEDIR Contents/Resources/8/81/
+EXECUTE REMOVEDIR Contents/Resources/8/80/
+EXECUTE REMOVEDIR Contents/Resources/8/80/
+EXECUTE REMOVEFILE Contents/Resources/7/70/7xtest.exe
+EXECUTE REMOVEFILE Contents/Resources/7/70/7xtext0
+EXECUTE REMOVEFILE Contents/Resources/7/70/7xtext1
+EXECUTE REMOVEDIR Contents/Resources/7/70/
+EXECUTE REMOVEFILE Contents/Resources/7/71/7xtest.exe
+EXECUTE REMOVEFILE Contents/Resources/7/71/7xtext0
+EXECUTE REMOVEFILE Contents/Resources/7/71/7xtext1
+EXECUTE REMOVEDIR Contents/Resources/7/71/
+EXECUTE REMOVEFILE Contents/Resources/7/7text0
+EXECUTE REMOVEFILE Contents/Resources/7/7text1
+EXECUTE REMOVEDIR Contents/Resources/7/
+EXECUTE REMOVEDIR Contents/Resources/6/
+EXECUTE REMOVEFILE Contents/Resources/5/5text1
+EXECUTE REMOVEFILE Contents/Resources/5/5text0
+EXECUTE REMOVEFILE Contents/Resources/5/5test.exe
+EXECUTE REMOVEFILE Contents/Resources/5/5text0
+file cannot be removed because it does not exist; skipping
+EXECUTE REMOVEFILE Contents/Resources/5/5text1
+file cannot be removed because it does not exist; skipping
+EXECUTE REMOVEDIR Contents/Resources/5/
+EXECUTE REMOVEFILE Contents/Resources/4/4text1
+EXECUTE REMOVEFILE Contents/Resources/4/4text0
+EXECUTE REMOVEDIR Contents/Resources/4/
+EXECUTE REMOVEFILE Contents/Resources/3/3text1
+EXECUTE REMOVEFILE Contents/Resources/3/3text0
+FINISH REMOVEFILE Contents/Resources/searchplugins/searchpluginstext0
+FINISH REMOVEFILE Contents/Resources/searchplugins/searchpluginspng0.png
+FINISH REMOVEFILE Contents/Resources/removed-files
+FINISH REMOVEFILE Contents/Resources/precomplete
+FINISH REMOVEFILE Contents/Resources/2/20/20text0
+FINISH REMOVEFILE Contents/Resources/2/20/20png0.png
+FINISH REMOVEFILE Contents/Resources/0/0exe0.exe
+FINISH REMOVEFILE Contents/Resources/0/00/00text0
+FINISH REMOVEFILE Contents/MacOS/exe0.exe
+FINISH REMOVEDIR Contents/Resources/searchplugins/
+removing directory: Contents/Resources/searchplugins/, rv: 0
+FINISH REMOVEDIR Contents/Resources/defaults/pref/
+removing directory: Contents/Resources/defaults/pref/, rv: 0
+FINISH REMOVEDIR Contents/Resources/defaults/
+removing directory: Contents/Resources/defaults/, rv: 0
+FINISH REMOVEDIR Contents/Resources/2/20/
+FINISH REMOVEDIR Contents/Resources/2/
+FINISH REMOVEDIR Contents/Resources/0/00/
+removing directory: Contents/Resources/0/00/, rv: 0
+FINISH REMOVEDIR Contents/Resources/0/
+removing directory: Contents/Resources/0/, rv: 0
+FINISH REMOVEDIR Contents/Resources/
+removing directory: Contents/Resources/, rv: 0
+FINISH REMOVEDIR Contents/MacOS/
+removing directory: Contents/MacOS/, rv: 0
+FINISH REMOVEDIR Contents/
+removing directory: Contents/, rv: 0
+FINISH ADD Contents/Resources/searchplugins/searchpluginstext0
+FINISH ADD Contents/Resources/searchplugins/searchpluginspng1.png
+FINISH ADD Contents/Resources/searchplugins/searchpluginspng0.png
+FINISH ADD Contents/Resources/removed-files
+FINISH ADD Contents/Resources/precomplete
+FINISH ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0
+FINISH ADD Contents/Resources/distribution/extensions/extensions1/extensions1png1.png
+FINISH ADD Contents/Resources/distribution/extensions/extensions1/extensions1png0.png
+FINISH ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0
+FINISH ADD Contents/Resources/distribution/extensions/extensions0/extensions0png1.png
+FINISH ADD Contents/Resources/distribution/extensions/extensions0/extensions0png0.png
+FINISH ADD Contents/Resources/1/10/10text0
+FINISH ADD Contents/Resources/0/0exe0.exe
+FINISH ADD Contents/Resources/0/00/00text1
+FINISH ADD Contents/Resources/0/00/00text0
+FINISH ADD Contents/Resources/0/00/00png0.png
+FINISH ADD Contents/MacOS/exe0.exe
+FINISH REMOVEDIR Contents/Resources/9/99/
+FINISH REMOVEDIR Contents/Resources/9/99/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/9/98/
+FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext0
+FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext1
+FINISH REMOVEDIR Contents/Resources/9/97/970/
+FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext0
+FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext1
+FINISH REMOVEDIR Contents/Resources/9/97/971/
+FINISH REMOVEDIR Contents/Resources/9/97/
+FINISH REMOVEFILE Contents/Resources/9/96/96text0
+FINISH REMOVEFILE Contents/Resources/9/96/96text1
+FINISH REMOVEDIR Contents/Resources/9/96/
+FINISH REMOVEDIR Contents/Resources/9/95/
+FINISH REMOVEDIR Contents/Resources/9/95/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/9/94/
+FINISH REMOVEDIR Contents/Resources/9/94/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/9/93/
+FINISH REMOVEDIR Contents/Resources/9/92/
+removing directory: Contents/Resources/9/92/, rv: 0
+FINISH REMOVEDIR Contents/Resources/9/91/
+removing directory: Contents/Resources/9/91/, rv: 0
+FINISH REMOVEDIR Contents/Resources/9/90/
+FINISH REMOVEDIR Contents/Resources/9/90/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/8/89/
+FINISH REMOVEDIR Contents/Resources/8/89/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/8/88/
+FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext0
+FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext1
+FINISH REMOVEDIR Contents/Resources/8/87/870/
+FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext0
+FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext1
+FINISH REMOVEDIR Contents/Resources/8/87/871/
+FINISH REMOVEDIR Contents/Resources/8/87/
+FINISH REMOVEFILE Contents/Resources/8/86/86text0
+FINISH REMOVEFILE Contents/Resources/8/86/86text1
+FINISH REMOVEDIR Contents/Resources/8/86/
+FINISH REMOVEDIR Contents/Resources/8/85/
+FINISH REMOVEDIR Contents/Resources/8/85/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/8/84/
+FINISH REMOVEDIR Contents/Resources/8/84/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/8/83/
+FINISH REMOVEDIR Contents/Resources/8/82/
+removing directory: Contents/Resources/8/82/, rv: 0
+FINISH REMOVEDIR Contents/Resources/8/81/
+removing directory: Contents/Resources/8/81/, rv: 0
+FINISH REMOVEDIR Contents/Resources/8/80/
+FINISH REMOVEDIR Contents/Resources/8/80/
+directory no longer exists; skipping
+FINISH REMOVEFILE Contents/Resources/7/70/7xtest.exe
+FINISH REMOVEFILE Contents/Resources/7/70/7xtext0
+FINISH REMOVEFILE Contents/Resources/7/70/7xtext1
+FINISH REMOVEDIR Contents/Resources/7/70/
+FINISH REMOVEFILE Contents/Resources/7/71/7xtest.exe
+FINISH REMOVEFILE Contents/Resources/7/71/7xtext0
+FINISH REMOVEFILE Contents/Resources/7/71/7xtext1
+FINISH REMOVEDIR Contents/Resources/7/71/
+FINISH REMOVEFILE Contents/Resources/7/7text0
+FINISH REMOVEFILE Contents/Resources/7/7text1
+FINISH REMOVEDIR Contents/Resources/7/
+FINISH REMOVEDIR Contents/Resources/6/
+FINISH REMOVEFILE Contents/Resources/5/5text1
+FINISH REMOVEFILE Contents/Resources/5/5text0
+FINISH REMOVEFILE Contents/Resources/5/5test.exe
+FINISH REMOVEDIR Contents/Resources/5/
+FINISH REMOVEFILE Contents/Resources/4/4text1
+FINISH REMOVEFILE Contents/Resources/4/4text0
+FINISH REMOVEDIR Contents/Resources/4/
+FINISH REMOVEFILE Contents/Resources/3/3text1
+FINISH REMOVEFILE Contents/Resources/3/3text0
+succeeded
+calling QuitProgressUI
diff --git a/toolkit/mozapps/update/tests/data/complete_log_success_win b/toolkit/mozapps/update/tests/data/complete_log_success_win
new file mode 100644
index 000000000..c5a03dc9d
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete_log_success_win
@@ -0,0 +1,320 @@
+UPDATE TYPE complete
+PREPARE REMOVEFILE searchplugins/searchpluginstext0
+PREPARE REMOVEFILE searchplugins/searchpluginspng0.png
+PREPARE REMOVEFILE removed-files
+PREPARE REMOVEFILE precomplete
+PREPARE REMOVEFILE exe0.exe
+PREPARE REMOVEFILE 2/20/20text0
+PREPARE REMOVEFILE 2/20/20png0.png
+PREPARE REMOVEFILE 0/0exe0.exe
+PREPARE REMOVEFILE 0/00/00text0
+PREPARE REMOVEDIR searchplugins/
+PREPARE REMOVEDIR defaults/pref/
+PREPARE REMOVEDIR defaults/
+PREPARE REMOVEDIR 2/20/
+PREPARE REMOVEDIR 2/
+PREPARE REMOVEDIR 0/00/
+PREPARE REMOVEDIR 0/
+PREPARE ADD searchplugins/searchpluginstext0
+PREPARE ADD searchplugins/searchpluginspng1.png
+PREPARE ADD searchplugins/searchpluginspng0.png
+PREPARE ADD removed-files
+PREPARE ADD precomplete
+PREPARE ADD exe0.exe
+PREPARE ADD distribution/extensions/extensions1/extensions1text0
+PREPARE ADD distribution/extensions/extensions1/extensions1png1.png
+PREPARE ADD distribution/extensions/extensions1/extensions1png0.png
+PREPARE ADD distribution/extensions/extensions0/extensions0text0
+PREPARE ADD distribution/extensions/extensions0/extensions0png1.png
+PREPARE ADD distribution/extensions/extensions0/extensions0png0.png
+PREPARE ADD 1/10/10text0
+PREPARE ADD 0/0exe0.exe
+PREPARE ADD 0/00/00text1
+PREPARE ADD 0/00/00text0
+PREPARE ADD 0/00/00png0.png
+PREPARE REMOVEDIR 9/99/
+PREPARE REMOVEDIR 9/99/
+PREPARE REMOVEDIR 9/98/
+PREPARE REMOVEFILE 9/97/970/97xtext0
+PREPARE REMOVEFILE 9/97/970/97xtext1
+PREPARE REMOVEDIR 9/97/970/
+PREPARE REMOVEFILE 9/97/971/97xtext0
+PREPARE REMOVEFILE 9/97/971/97xtext1
+PREPARE REMOVEDIR 9/97/971/
+PREPARE REMOVEDIR 9/97/
+PREPARE REMOVEFILE 9/96/96text0
+PREPARE REMOVEFILE 9/96/96text1
+PREPARE REMOVEDIR 9/96/
+PREPARE REMOVEDIR 9/95/
+PREPARE REMOVEDIR 9/95/
+PREPARE REMOVEDIR 9/94/
+PREPARE REMOVEDIR 9/94/
+PREPARE REMOVEDIR 9/93/
+PREPARE REMOVEDIR 9/92/
+PREPARE REMOVEDIR 9/91/
+PREPARE REMOVEDIR 9/90/
+PREPARE REMOVEDIR 9/90/
+PREPARE REMOVEDIR 8/89/
+PREPARE REMOVEDIR 8/89/
+PREPARE REMOVEDIR 8/88/
+PREPARE REMOVEFILE 8/87/870/87xtext0
+PREPARE REMOVEFILE 8/87/870/87xtext1
+PREPARE REMOVEDIR 8/87/870/
+PREPARE REMOVEFILE 8/87/871/87xtext0
+PREPARE REMOVEFILE 8/87/871/87xtext1
+PREPARE REMOVEDIR 8/87/871/
+PREPARE REMOVEDIR 8/87/
+PREPARE REMOVEFILE 8/86/86text0
+PREPARE REMOVEFILE 8/86/86text1
+PREPARE REMOVEDIR 8/86/
+PREPARE REMOVEDIR 8/85/
+PREPARE REMOVEDIR 8/85/
+PREPARE REMOVEDIR 8/84/
+PREPARE REMOVEDIR 8/84/
+PREPARE REMOVEDIR 8/83/
+PREPARE REMOVEDIR 8/82/
+PREPARE REMOVEDIR 8/81/
+PREPARE REMOVEDIR 8/80/
+PREPARE REMOVEDIR 8/80/
+PREPARE REMOVEFILE 7/70/7xtest.exe
+PREPARE REMOVEFILE 7/70/7xtext0
+PREPARE REMOVEFILE 7/70/7xtext1
+PREPARE REMOVEDIR 7/70/
+PREPARE REMOVEFILE 7/71/7xtest.exe
+PREPARE REMOVEFILE 7/71/7xtext0
+PREPARE REMOVEFILE 7/71/7xtext1
+PREPARE REMOVEDIR 7/71/
+PREPARE REMOVEFILE 7/7text0
+PREPARE REMOVEFILE 7/7text1
+PREPARE REMOVEDIR 7/
+PREPARE REMOVEDIR 6/
+PREPARE REMOVEFILE 5/5text1
+PREPARE REMOVEFILE 5/5text0
+PREPARE REMOVEFILE 5/5test.exe
+PREPARE REMOVEFILE 5/5text0
+PREPARE REMOVEFILE 5/5text1
+PREPARE REMOVEDIR 5/
+PREPARE REMOVEFILE 4/4text1
+PREPARE REMOVEFILE 4/4text0
+PREPARE REMOVEDIR 4/
+PREPARE REMOVEFILE 3/3text1
+PREPARE REMOVEFILE 3/3text0
+EXECUTE REMOVEFILE searchplugins/searchpluginstext0
+EXECUTE REMOVEFILE searchplugins/searchpluginspng0.png
+EXECUTE REMOVEFILE removed-files
+EXECUTE REMOVEFILE precomplete
+EXECUTE REMOVEFILE exe0.exe
+EXECUTE REMOVEFILE 2/20/20text0
+EXECUTE REMOVEFILE 2/20/20png0.png
+EXECUTE REMOVEFILE 0/0exe0.exe
+EXECUTE REMOVEFILE 0/00/00text0
+EXECUTE REMOVEDIR searchplugins/
+EXECUTE REMOVEDIR defaults/pref/
+EXECUTE REMOVEDIR defaults/
+EXECUTE REMOVEDIR 2/20/
+EXECUTE REMOVEDIR 2/
+EXECUTE REMOVEDIR 0/00/
+EXECUTE REMOVEDIR 0/
+EXECUTE ADD searchplugins/searchpluginstext0
+EXECUTE ADD searchplugins/searchpluginspng1.png
+EXECUTE ADD searchplugins/searchpluginspng0.png
+EXECUTE ADD removed-files
+EXECUTE ADD precomplete
+EXECUTE ADD exe0.exe
+EXECUTE ADD distribution/extensions/extensions1/extensions1text0
+EXECUTE ADD distribution/extensions/extensions1/extensions1png1.png
+EXECUTE ADD distribution/extensions/extensions1/extensions1png0.png
+EXECUTE ADD distribution/extensions/extensions0/extensions0text0
+EXECUTE ADD distribution/extensions/extensions0/extensions0png1.png
+EXECUTE ADD distribution/extensions/extensions0/extensions0png0.png
+EXECUTE ADD 1/10/10text0
+EXECUTE ADD 0/0exe0.exe
+EXECUTE ADD 0/00/00text1
+EXECUTE ADD 0/00/00text0
+EXECUTE ADD 0/00/00png0.png
+EXECUTE REMOVEDIR 9/99/
+EXECUTE REMOVEDIR 9/99/
+EXECUTE REMOVEDIR 9/98/
+EXECUTE REMOVEFILE 9/97/970/97xtext0
+EXECUTE REMOVEFILE 9/97/970/97xtext1
+EXECUTE REMOVEDIR 9/97/970/
+EXECUTE REMOVEFILE 9/97/971/97xtext0
+EXECUTE REMOVEFILE 9/97/971/97xtext1
+EXECUTE REMOVEDIR 9/97/971/
+EXECUTE REMOVEDIR 9/97/
+EXECUTE REMOVEFILE 9/96/96text0
+EXECUTE REMOVEFILE 9/96/96text1
+EXECUTE REMOVEDIR 9/96/
+EXECUTE REMOVEDIR 9/95/
+EXECUTE REMOVEDIR 9/95/
+EXECUTE REMOVEDIR 9/94/
+EXECUTE REMOVEDIR 9/94/
+EXECUTE REMOVEDIR 9/93/
+EXECUTE REMOVEDIR 9/92/
+EXECUTE REMOVEDIR 9/91/
+EXECUTE REMOVEDIR 9/90/
+EXECUTE REMOVEDIR 9/90/
+EXECUTE REMOVEDIR 8/89/
+EXECUTE REMOVEDIR 8/89/
+EXECUTE REMOVEDIR 8/88/
+EXECUTE REMOVEFILE 8/87/870/87xtext0
+EXECUTE REMOVEFILE 8/87/870/87xtext1
+EXECUTE REMOVEDIR 8/87/870/
+EXECUTE REMOVEFILE 8/87/871/87xtext0
+EXECUTE REMOVEFILE 8/87/871/87xtext1
+EXECUTE REMOVEDIR 8/87/871/
+EXECUTE REMOVEDIR 8/87/
+EXECUTE REMOVEFILE 8/86/86text0
+EXECUTE REMOVEFILE 8/86/86text1
+EXECUTE REMOVEDIR 8/86/
+EXECUTE REMOVEDIR 8/85/
+EXECUTE REMOVEDIR 8/85/
+EXECUTE REMOVEDIR 8/84/
+EXECUTE REMOVEDIR 8/84/
+EXECUTE REMOVEDIR 8/83/
+EXECUTE REMOVEDIR 8/82/
+EXECUTE REMOVEDIR 8/81/
+EXECUTE REMOVEDIR 8/80/
+EXECUTE REMOVEDIR 8/80/
+EXECUTE REMOVEFILE 7/70/7xtest.exe
+EXECUTE REMOVEFILE 7/70/7xtext0
+EXECUTE REMOVEFILE 7/70/7xtext1
+EXECUTE REMOVEDIR 7/70/
+EXECUTE REMOVEFILE 7/71/7xtest.exe
+EXECUTE REMOVEFILE 7/71/7xtext0
+EXECUTE REMOVEFILE 7/71/7xtext1
+EXECUTE REMOVEDIR 7/71/
+EXECUTE REMOVEFILE 7/7text0
+EXECUTE REMOVEFILE 7/7text1
+EXECUTE REMOVEDIR 7/
+EXECUTE REMOVEDIR 6/
+EXECUTE REMOVEFILE 5/5text1
+EXECUTE REMOVEFILE 5/5text0
+EXECUTE REMOVEFILE 5/5test.exe
+EXECUTE REMOVEFILE 5/5text0
+file cannot be removed because it does not exist; skipping
+EXECUTE REMOVEFILE 5/5text1
+file cannot be removed because it does not exist; skipping
+EXECUTE REMOVEDIR 5/
+EXECUTE REMOVEFILE 4/4text1
+EXECUTE REMOVEFILE 4/4text0
+EXECUTE REMOVEDIR 4/
+EXECUTE REMOVEFILE 3/3text1
+EXECUTE REMOVEFILE 3/3text0
+FINISH REMOVEFILE searchplugins/searchpluginstext0
+FINISH REMOVEFILE searchplugins/searchpluginspng0.png
+FINISH REMOVEFILE removed-files
+FINISH REMOVEFILE precomplete
+FINISH REMOVEFILE exe0.exe
+FINISH REMOVEFILE 2/20/20text0
+FINISH REMOVEFILE 2/20/20png0.png
+FINISH REMOVEFILE 0/0exe0.exe
+FINISH REMOVEFILE 0/00/00text0
+FINISH REMOVEDIR searchplugins/
+removing directory: searchplugins/, rv: 0
+FINISH REMOVEDIR defaults/pref/
+removing directory: defaults/pref/, rv: 0
+FINISH REMOVEDIR defaults/
+removing directory: defaults/, rv: 0
+FINISH REMOVEDIR 2/20/
+FINISH REMOVEDIR 2/
+FINISH REMOVEDIR 0/00/
+removing directory: 0/00/, rv: 0
+FINISH REMOVEDIR 0/
+removing directory: 0/, rv: 0
+FINISH ADD searchplugins/searchpluginstext0
+FINISH ADD searchplugins/searchpluginspng1.png
+FINISH ADD searchplugins/searchpluginspng0.png
+FINISH ADD removed-files
+FINISH ADD precomplete
+FINISH ADD exe0.exe
+FINISH ADD distribution/extensions/extensions1/extensions1text0
+FINISH ADD distribution/extensions/extensions1/extensions1png1.png
+FINISH ADD distribution/extensions/extensions1/extensions1png0.png
+FINISH ADD distribution/extensions/extensions0/extensions0text0
+FINISH ADD distribution/extensions/extensions0/extensions0png1.png
+FINISH ADD distribution/extensions/extensions0/extensions0png0.png
+FINISH ADD 1/10/10text0
+FINISH ADD 0/0exe0.exe
+FINISH ADD 0/00/00text1
+FINISH ADD 0/00/00text0
+FINISH ADD 0/00/00png0.png
+FINISH REMOVEDIR 9/99/
+FINISH REMOVEDIR 9/99/
+directory no longer exists; skipping
+FINISH REMOVEDIR 9/98/
+FINISH REMOVEFILE 9/97/970/97xtext0
+FINISH REMOVEFILE 9/97/970/97xtext1
+FINISH REMOVEDIR 9/97/970/
+FINISH REMOVEFILE 9/97/971/97xtext0
+FINISH REMOVEFILE 9/97/971/97xtext1
+FINISH REMOVEDIR 9/97/971/
+FINISH REMOVEDIR 9/97/
+FINISH REMOVEFILE 9/96/96text0
+FINISH REMOVEFILE 9/96/96text1
+FINISH REMOVEDIR 9/96/
+FINISH REMOVEDIR 9/95/
+FINISH REMOVEDIR 9/95/
+directory no longer exists; skipping
+FINISH REMOVEDIR 9/94/
+FINISH REMOVEDIR 9/94/
+directory no longer exists; skipping
+FINISH REMOVEDIR 9/93/
+FINISH REMOVEDIR 9/92/
+removing directory: 9/92/, rv: 0
+FINISH REMOVEDIR 9/91/
+removing directory: 9/91/, rv: 0
+FINISH REMOVEDIR 9/90/
+FINISH REMOVEDIR 9/90/
+directory no longer exists; skipping
+FINISH REMOVEDIR 8/89/
+FINISH REMOVEDIR 8/89/
+directory no longer exists; skipping
+FINISH REMOVEDIR 8/88/
+FINISH REMOVEFILE 8/87/870/87xtext0
+FINISH REMOVEFILE 8/87/870/87xtext1
+FINISH REMOVEDIR 8/87/870/
+FINISH REMOVEFILE 8/87/871/87xtext0
+FINISH REMOVEFILE 8/87/871/87xtext1
+FINISH REMOVEDIR 8/87/871/
+FINISH REMOVEDIR 8/87/
+FINISH REMOVEFILE 8/86/86text0
+FINISH REMOVEFILE 8/86/86text1
+FINISH REMOVEDIR 8/86/
+FINISH REMOVEDIR 8/85/
+FINISH REMOVEDIR 8/85/
+directory no longer exists; skipping
+FINISH REMOVEDIR 8/84/
+FINISH REMOVEDIR 8/84/
+directory no longer exists; skipping
+FINISH REMOVEDIR 8/83/
+FINISH REMOVEDIR 8/82/
+removing directory: 8/82/, rv: 0
+FINISH REMOVEDIR 8/81/
+removing directory: 8/81/, rv: 0
+FINISH REMOVEDIR 8/80/
+FINISH REMOVEDIR 8/80/
+directory no longer exists; skipping
+FINISH REMOVEFILE 7/70/7xtest.exe
+FINISH REMOVEFILE 7/70/7xtext0
+FINISH REMOVEFILE 7/70/7xtext1
+FINISH REMOVEDIR 7/70/
+FINISH REMOVEFILE 7/71/7xtest.exe
+FINISH REMOVEFILE 7/71/7xtext0
+FINISH REMOVEFILE 7/71/7xtext1
+FINISH REMOVEDIR 7/71/
+FINISH REMOVEFILE 7/7text0
+FINISH REMOVEFILE 7/7text1
+FINISH REMOVEDIR 7/
+FINISH REMOVEDIR 6/
+FINISH REMOVEFILE 5/5text1
+FINISH REMOVEFILE 5/5text0
+FINISH REMOVEFILE 5/5test.exe
+FINISH REMOVEDIR 5/
+FINISH REMOVEFILE 4/4text1
+FINISH REMOVEFILE 4/4text0
+FINISH REMOVEDIR 4/
+FINISH REMOVEFILE 3/3text1
+FINISH REMOVEFILE 3/3text0
+succeeded
+calling QuitProgressUI
diff --git a/toolkit/mozapps/update/tests/data/complete_mac.mar b/toolkit/mozapps/update/tests/data/complete_mac.mar
new file mode 100644
index 000000000..ca1497f4f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete_mac.mar
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/complete_precomplete b/toolkit/mozapps/update/tests/data/complete_precomplete
new file mode 100644
index 000000000..ae7a0013f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete_precomplete
@@ -0,0 +1,18 @@
+remove "searchplugins/searchpluginstext0"
+remove "searchplugins/searchpluginspng1.png"
+remove "searchplugins/searchpluginspng0.png"
+remove "removed-files"
+remove "precomplete"
+remove "exe0.exe"
+remove "1/10/10text0"
+remove "0/0exe0.exe"
+remove "0/00/00text1"
+remove "0/00/00text0"
+remove "0/00/00png0.png"
+rmdir "searchplugins/"
+rmdir "defaults/pref/"
+rmdir "defaults/"
+rmdir "1/10/"
+rmdir "1/"
+rmdir "0/00/"
+rmdir "0/"
diff --git a/toolkit/mozapps/update/tests/data/complete_precomplete_mac b/toolkit/mozapps/update/tests/data/complete_precomplete_mac
new file mode 100644
index 000000000..8d81a36d6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete_precomplete_mac
@@ -0,0 +1,21 @@
+remove "Contents/Resources/searchplugins/searchpluginstext0"
+remove "Contents/Resources/searchplugins/searchpluginspng1.png"
+remove "Contents/Resources/searchplugins/searchpluginspng0.png"
+remove "Contents/Resources/removed-files"
+remove "Contents/Resources/precomplete"
+remove "Contents/Resources/1/10/10text0"
+remove "Contents/Resources/0/0exe0.exe"
+remove "Contents/Resources/0/00/00text1"
+remove "Contents/Resources/0/00/00text0"
+remove "Contents/Resources/0/00/00png0.png"
+remove "Contents/MacOS/exe0.exe"
+rmdir "Contents/Resources/searchplugins/"
+rmdir "Contents/Resources/defaults/pref/"
+rmdir "Contents/Resources/defaults/"
+rmdir "Contents/Resources/1/10/"
+rmdir "Contents/Resources/1/"
+rmdir "Contents/Resources/0/00/"
+rmdir "Contents/Resources/0/"
+rmdir "Contents/Resources/"
+rmdir "Contents/MacOS/"
+rmdir "Contents/"
diff --git a/toolkit/mozapps/update/tests/data/complete_removed-files b/toolkit/mozapps/update/tests/data/complete_removed-files
new file mode 100644
index 000000000..e45c43c1f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete_removed-files
@@ -0,0 +1,41 @@
+text0
+text1
+3/3text0
+3/3text1
+4/exe0.exe
+4/4text0
+4/4text1
+4/
+5/5text0
+5/5text1
+5/*
+6/
+7/*
+8/80/
+8/81/
+8/82/
+8/83/
+8/84/
+8/85/*
+8/86/*
+8/87/*
+8/88/*
+8/89/*
+8/80/
+8/84/*
+8/85/*
+8/89/
+9/90/
+9/91/
+9/92/
+9/93/
+9/94/
+9/95/*
+9/96/*
+9/97/*
+9/98/*
+9/99/*
+9/90/
+9/94/*
+9/95/*
+9/99/
diff --git a/toolkit/mozapps/update/tests/data/complete_removed-files_mac b/toolkit/mozapps/update/tests/data/complete_removed-files_mac
new file mode 100644
index 000000000..955dc5b34
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete_removed-files_mac
@@ -0,0 +1,41 @@
+Contents/Resources/text0
+Contents/Resources/text1
+Contents/Resources/3/3text0
+Contents/Resources/3/3text1
+Contents/Resources/4/exe0.exe
+Contents/Resources/4/4text0
+Contents/Resources/4/4text1
+Contents/Resources/4/
+Contents/Resources/5/5text0
+Contents/Resources/5/5text1
+Contents/Resources/5/*
+Contents/Resources/6/
+Contents/Resources/7/*
+Contents/Resources/8/80/
+Contents/Resources/8/81/
+Contents/Resources/8/82/
+Contents/Resources/8/83/
+Contents/Resources/8/84/
+Contents/Resources/8/85/*
+Contents/Resources/8/86/*
+Contents/Resources/8/87/*
+Contents/Resources/8/88/*
+Contents/Resources/8/89/*
+Contents/Resources/8/80/
+Contents/Resources/8/84/*
+Contents/Resources/8/85/*
+Contents/Resources/8/89/
+Contents/Resources/9/90/
+Contents/Resources/9/91/
+Contents/Resources/9/92/
+Contents/Resources/9/93/
+Contents/Resources/9/94/
+Contents/Resources/9/95/*
+Contents/Resources/9/96/*
+Contents/Resources/9/97/*
+Contents/Resources/9/98/*
+Contents/Resources/9/99/*
+Contents/Resources/9/90/
+Contents/Resources/9/94/*
+Contents/Resources/9/95/*
+Contents/Resources/9/99/
diff --git a/toolkit/mozapps/update/tests/data/complete_update_manifest b/toolkit/mozapps/update/tests/data/complete_update_manifest
new file mode 100644
index 000000000..383a324f6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/complete_update_manifest
@@ -0,0 +1,59 @@
+type "complete"
+add "precomplete"
+add "searchplugins/searchpluginstext0"
+add "searchplugins/searchpluginspng1.png"
+add "searchplugins/searchpluginspng0.png"
+add "removed-files"
+add-if "extensions/extensions1" "extensions/extensions1/extensions1text0"
+add-if "extensions/extensions1" "extensions/extensions1/extensions1png1.png"
+add-if "extensions/extensions1" "extensions/extensions1/extensions1png0.png"
+add-if "extensions/extensions0" "extensions/extensions0/extensions0text0"
+add-if "extensions/extensions0" "extensions/extensions0/extensions0png1.png"
+add-if "extensions/extensions0" "extensions/extensions0/extensions0png0.png"
+add "exe0.exe"
+add "1/10/10text0"
+add "0/0exe0.exe"
+add "0/00/00text1"
+add "0/00/00text0"
+add "0/00/00png0.png"
+remove "text1"
+remove "text0"
+rmrfdir "9/99/"
+rmdir "9/99/"
+rmrfdir "9/98/"
+rmrfdir "9/97/"
+rmrfdir "9/96/"
+rmrfdir "9/95/"
+rmrfdir "9/95/"
+rmrfdir "9/94/"
+rmdir "9/94/"
+rmdir "9/93/"
+rmdir "9/92/"
+rmdir "9/91/"
+rmdir "9/90/"
+rmdir "9/90/"
+rmrfdir "8/89/"
+rmdir "8/89/"
+rmrfdir "8/88/"
+rmrfdir "8/87/"
+rmrfdir "8/86/"
+rmrfdir "8/85/"
+rmrfdir "8/85/"
+rmrfdir "8/84/"
+rmdir "8/84/"
+rmdir "8/83/"
+rmdir "8/82/"
+rmdir "8/81/"
+rmdir "8/80/"
+rmdir "8/80/"
+rmrfdir "7/"
+rmdir "6/"
+remove "5/5text1"
+remove "5/5text0"
+rmrfdir "5/"
+remove "4/exe0.exe"
+remove "4/4text1"
+remove "4/4text0"
+rmdir "4/"
+remove "3/3text1"
+remove "3/3text0"
diff --git a/toolkit/mozapps/update/tests/data/old_version.mar b/toolkit/mozapps/update/tests/data/old_version.mar
new file mode 100644
index 000000000..31550698a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/old_version.mar
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/partial.exe b/toolkit/mozapps/update/tests/data/partial.exe
new file mode 100644
index 000000000..3949fd2a0
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial.exe
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/partial.mar b/toolkit/mozapps/update/tests/data/partial.mar
new file mode 100644
index 000000000..789d3d98d
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial.mar
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/partial.png b/toolkit/mozapps/update/tests/data/partial.png
new file mode 100644
index 000000000..9246f586c
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial.png
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/partial_log_failure_mac b/toolkit/mozapps/update/tests/data/partial_log_failure_mac
new file mode 100644
index 000000000..3b2933ebd
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial_log_failure_mac
@@ -0,0 +1,192 @@
+UPDATE TYPE partial
+PREPARE ADD Contents/Resources/searchplugins/searchpluginstext0
+PREPARE PATCH Contents/Resources/searchplugins/searchpluginspng1.png
+PREPARE PATCH Contents/Resources/searchplugins/searchpluginspng0.png
+PREPARE ADD Contents/Resources/precomplete
+PREPARE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0
+PREPARE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png
+PREPARE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png
+PREPARE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0
+PREPARE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png
+PREPARE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png
+PREPARE PATCH Contents/Resources/0/0exe0.exe
+PREPARE ADD Contents/Resources/0/00/00text0
+PREPARE PATCH Contents/Resources/0/00/00png0.png
+PREPARE PATCH Contents/MacOS/exe0.exe
+PREPARE ADD Contents/Resources/2/20/20text0
+PREPARE ADD Contents/Resources/2/20/20png0.png
+PREPARE ADD Contents/Resources/0/00/00text2
+PREPARE REMOVEFILE Contents/Resources/1/10/10text0
+PREPARE REMOVEFILE Contents/Resources/0/00/00text1
+PREPARE REMOVEDIR Contents/Resources/9/99/
+PREPARE REMOVEDIR Contents/Resources/9/99/
+PREPARE REMOVEDIR Contents/Resources/9/98/
+PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext0
+PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext1
+PREPARE REMOVEDIR Contents/Resources/9/97/970/
+PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext0
+PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext1
+PREPARE REMOVEDIR Contents/Resources/9/97/971/
+PREPARE REMOVEDIR Contents/Resources/9/97/
+PREPARE REMOVEFILE Contents/Resources/9/96/96text0
+PREPARE REMOVEFILE Contents/Resources/9/96/96text1
+PREPARE REMOVEDIR Contents/Resources/9/96/
+PREPARE REMOVEDIR Contents/Resources/9/95/
+PREPARE REMOVEDIR Contents/Resources/9/95/
+PREPARE REMOVEDIR Contents/Resources/9/94/
+PREPARE REMOVEDIR Contents/Resources/9/94/
+PREPARE REMOVEDIR Contents/Resources/9/93/
+PREPARE REMOVEDIR Contents/Resources/9/92/
+PREPARE REMOVEDIR Contents/Resources/9/91/
+PREPARE REMOVEDIR Contents/Resources/9/90/
+PREPARE REMOVEDIR Contents/Resources/9/90/
+PREPARE REMOVEDIR Contents/Resources/8/89/
+PREPARE REMOVEDIR Contents/Resources/8/89/
+PREPARE REMOVEDIR Contents/Resources/8/88/
+PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext0
+PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext1
+PREPARE REMOVEDIR Contents/Resources/8/87/870/
+PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext0
+PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext1
+PREPARE REMOVEDIR Contents/Resources/8/87/871/
+PREPARE REMOVEDIR Contents/Resources/8/87/
+PREPARE REMOVEFILE Contents/Resources/8/86/86text0
+PREPARE REMOVEFILE Contents/Resources/8/86/86text1
+PREPARE REMOVEDIR Contents/Resources/8/86/
+PREPARE REMOVEDIR Contents/Resources/8/85/
+PREPARE REMOVEDIR Contents/Resources/8/85/
+PREPARE REMOVEDIR Contents/Resources/8/84/
+PREPARE REMOVEDIR Contents/Resources/8/84/
+PREPARE REMOVEDIR Contents/Resources/8/83/
+PREPARE REMOVEDIR Contents/Resources/8/82/
+PREPARE REMOVEDIR Contents/Resources/8/81/
+PREPARE REMOVEDIR Contents/Resources/8/80/
+PREPARE REMOVEDIR Contents/Resources/8/80/
+PREPARE REMOVEFILE Contents/Resources/7/70/7xtest.exe
+PREPARE REMOVEFILE Contents/Resources/7/70/7xtext0
+PREPARE REMOVEFILE Contents/Resources/7/70/7xtext1
+PREPARE REMOVEDIR Contents/Resources/7/70/
+PREPARE REMOVEFILE Contents/Resources/7/71/7xtest.exe
+PREPARE REMOVEFILE Contents/Resources/7/71/7xtext0
+PREPARE REMOVEFILE Contents/Resources/7/71/7xtext1
+PREPARE REMOVEDIR Contents/Resources/7/71/
+PREPARE REMOVEFILE Contents/Resources/7/7text0
+PREPARE REMOVEFILE Contents/Resources/7/7text1
+PREPARE REMOVEDIR Contents/Resources/7/
+PREPARE REMOVEDIR Contents/Resources/6/
+PREPARE REMOVEFILE Contents/Resources/5/5text1
+PREPARE REMOVEFILE Contents/Resources/5/5text0
+PREPARE REMOVEFILE Contents/Resources/5/5test.exe
+PREPARE REMOVEFILE Contents/Resources/5/5text0
+PREPARE REMOVEFILE Contents/Resources/5/5text1
+PREPARE REMOVEDIR Contents/Resources/5/
+PREPARE REMOVEFILE Contents/Resources/4/4text1
+PREPARE REMOVEFILE Contents/Resources/4/4text0
+PREPARE REMOVEDIR Contents/Resources/4/
+PREPARE REMOVEFILE Contents/Resources/3/3text1
+PREPARE REMOVEFILE Contents/Resources/3/3text0
+PREPARE REMOVEDIR Contents/Resources/1/10/
+PREPARE REMOVEDIR Contents/Resources/1/
+EXECUTE ADD Contents/Resources/searchplugins/searchpluginstext0
+EXECUTE PATCH Contents/Resources/searchplugins/searchpluginspng1.png
+EXECUTE PATCH Contents/Resources/searchplugins/searchpluginspng0.png
+EXECUTE ADD Contents/Resources/precomplete
+EXECUTE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0
+EXECUTE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png
+EXECUTE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png
+EXECUTE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0
+EXECUTE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png
+EXECUTE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png
+EXECUTE PATCH Contents/Resources/0/0exe0.exe
+LoadSourceFile: destination file size 776 does not match expected size 79872
+LoadSourceFile failed
+### execution failed
+FINISH ADD Contents/Resources/searchplugins/searchpluginstext0
+FINISH PATCH Contents/Resources/searchplugins/searchpluginspng1.png
+FINISH PATCH Contents/Resources/searchplugins/searchpluginspng0.png
+FINISH ADD Contents/Resources/precomplete
+FINISH ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0
+backup_restore: backup file doesn't exist: Contents/Resources/distribution/extensions/extensions1/extensions1text0.moz-backup
+FINISH PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png
+FINISH PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png
+FINISH ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0
+FINISH PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png
+FINISH PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png
+FINISH PATCH Contents/Resources/0/0exe0.exe
+backup_restore: backup file doesn't exist: Contents/Resources/0/0exe0.exe.moz-backup
+FINISH ADD Contents/Resources/0/00/00text0
+backup_restore: backup file doesn't exist: Contents/Resources/0/00/00text0.moz-backup
+FINISH PATCH Contents/Resources/0/00/00png0.png
+backup_restore: backup file doesn't exist: Contents/Resources/0/00/00png0.png.moz-backup
+FINISH PATCH Contents/MacOS/exe0.exe
+backup_restore: backup file doesn't exist: Contents/MacOS/exe0.exe.moz-backup
+FINISH ADD Contents/Resources/2/20/20text0
+backup_restore: backup file doesn't exist: Contents/Resources/2/20/20text0.moz-backup
+FINISH ADD Contents/Resources/2/20/20png0.png
+backup_restore: backup file doesn't exist: Contents/Resources/2/20/20png0.png.moz-backup
+FINISH ADD Contents/Resources/0/00/00text2
+backup_restore: backup file doesn't exist: Contents/Resources/0/00/00text2.moz-backup
+FINISH REMOVEFILE Contents/Resources/1/10/10text0
+backup_restore: backup file doesn't exist: Contents/Resources/1/10/10text0.moz-backup
+FINISH REMOVEFILE Contents/Resources/0/00/00text1
+backup_restore: backup file doesn't exist: Contents/Resources/0/00/00text1.moz-backup
+FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext0
+backup_restore: backup file doesn't exist: Contents/Resources/9/97/970/97xtext0.moz-backup
+FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext1
+backup_restore: backup file doesn't exist: Contents/Resources/9/97/970/97xtext1.moz-backup
+FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext0
+backup_restore: backup file doesn't exist: Contents/Resources/9/97/971/97xtext0.moz-backup
+FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext1
+backup_restore: backup file doesn't exist: Contents/Resources/9/97/971/97xtext1.moz-backup
+FINISH REMOVEFILE Contents/Resources/9/96/96text0
+backup_restore: backup file doesn't exist: Contents/Resources/9/96/96text0.moz-backup
+FINISH REMOVEFILE Contents/Resources/9/96/96text1
+backup_restore: backup file doesn't exist: Contents/Resources/9/96/96text1.moz-backup
+FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext0
+backup_restore: backup file doesn't exist: Contents/Resources/8/87/870/87xtext0.moz-backup
+FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext1
+backup_restore: backup file doesn't exist: Contents/Resources/8/87/870/87xtext1.moz-backup
+FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext0
+backup_restore: backup file doesn't exist: Contents/Resources/8/87/871/87xtext0.moz-backup
+FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext1
+backup_restore: backup file doesn't exist: Contents/Resources/8/87/871/87xtext1.moz-backup
+FINISH REMOVEFILE Contents/Resources/8/86/86text0
+backup_restore: backup file doesn't exist: Contents/Resources/8/86/86text0.moz-backup
+FINISH REMOVEFILE Contents/Resources/8/86/86text1
+backup_restore: backup file doesn't exist: Contents/Resources/8/86/86text1.moz-backup
+FINISH REMOVEFILE Contents/Resources/7/70/7xtest.exe
+backup_restore: backup file doesn't exist: Contents/Resources/7/70/7xtest.exe.moz-backup
+FINISH REMOVEFILE Contents/Resources/7/70/7xtext0
+backup_restore: backup file doesn't exist: Contents/Resources/7/70/7xtext0.moz-backup
+FINISH REMOVEFILE Contents/Resources/7/70/7xtext1
+backup_restore: backup file doesn't exist: Contents/Resources/7/70/7xtext1.moz-backup
+FINISH REMOVEFILE Contents/Resources/7/71/7xtest.exe
+backup_restore: backup file doesn't exist: Contents/Resources/7/71/7xtest.exe.moz-backup
+FINISH REMOVEFILE Contents/Resources/7/71/7xtext0
+backup_restore: backup file doesn't exist: Contents/Resources/7/71/7xtext0.moz-backup
+FINISH REMOVEFILE Contents/Resources/7/71/7xtext1
+backup_restore: backup file doesn't exist: Contents/Resources/7/71/7xtext1.moz-backup
+FINISH REMOVEFILE Contents/Resources/7/7text0
+backup_restore: backup file doesn't exist: Contents/Resources/7/7text0.moz-backup
+FINISH REMOVEFILE Contents/Resources/7/7text1
+backup_restore: backup file doesn't exist: Contents/Resources/7/7text1.moz-backup
+FINISH REMOVEFILE Contents/Resources/5/5text1
+backup_restore: backup file doesn't exist: Contents/Resources/5/5text1.moz-backup
+FINISH REMOVEFILE Contents/Resources/5/5text0
+backup_restore: backup file doesn't exist: Contents/Resources/5/5text0.moz-backup
+FINISH REMOVEFILE Contents/Resources/5/5test.exe
+backup_restore: backup file doesn't exist: Contents/Resources/5/5test.exe.moz-backup
+FINISH REMOVEFILE Contents/Resources/5/5text0
+backup_restore: backup file doesn't exist: Contents/Resources/5/5text0.moz-backup
+FINISH REMOVEFILE Contents/Resources/5/5text1
+backup_restore: backup file doesn't exist: Contents/Resources/5/5text1.moz-backup
+FINISH REMOVEFILE Contents/Resources/4/4text1
+backup_restore: backup file doesn't exist: Contents/Resources/4/4text1.moz-backup
+FINISH REMOVEFILE Contents/Resources/4/4text0
+backup_restore: backup file doesn't exist: Contents/Resources/4/4text0.moz-backup
+FINISH REMOVEFILE Contents/Resources/3/3text1
+backup_restore: backup file doesn't exist: Contents/Resources/3/3text1.moz-backup
+FINISH REMOVEFILE Contents/Resources/3/3text0
+backup_restore: backup file doesn't exist: Contents/Resources/3/3text0.moz-backup
+failed: 2
+calling QuitProgressUI
diff --git a/toolkit/mozapps/update/tests/data/partial_log_failure_win b/toolkit/mozapps/update/tests/data/partial_log_failure_win
new file mode 100644
index 000000000..e3d683dc1
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial_log_failure_win
@@ -0,0 +1,192 @@
+UPDATE TYPE partial
+PREPARE ADD searchplugins/searchpluginstext0
+PREPARE PATCH searchplugins/searchpluginspng1.png
+PREPARE PATCH searchplugins/searchpluginspng0.png
+PREPARE ADD precomplete
+PREPARE PATCH exe0.exe
+PREPARE ADD distribution/extensions/extensions1/extensions1text0
+PREPARE PATCH distribution/extensions/extensions1/extensions1png1.png
+PREPARE PATCH distribution/extensions/extensions1/extensions1png0.png
+PREPARE ADD distribution/extensions/extensions0/extensions0text0
+PREPARE PATCH distribution/extensions/extensions0/extensions0png1.png
+PREPARE PATCH distribution/extensions/extensions0/extensions0png0.png
+PREPARE PATCH 0/0exe0.exe
+PREPARE ADD 0/00/00text0
+PREPARE PATCH 0/00/00png0.png
+PREPARE ADD 2/20/20text0
+PREPARE ADD 2/20/20png0.png
+PREPARE ADD 0/00/00text2
+PREPARE REMOVEFILE 1/10/10text0
+PREPARE REMOVEFILE 0/00/00text1
+PREPARE REMOVEDIR 9/99/
+PREPARE REMOVEDIR 9/99/
+PREPARE REMOVEDIR 9/98/
+PREPARE REMOVEFILE 9/97/970/97xtext0
+PREPARE REMOVEFILE 9/97/970/97xtext1
+PREPARE REMOVEDIR 9/97/970/
+PREPARE REMOVEFILE 9/97/971/97xtext0
+PREPARE REMOVEFILE 9/97/971/97xtext1
+PREPARE REMOVEDIR 9/97/971/
+PREPARE REMOVEDIR 9/97/
+PREPARE REMOVEFILE 9/96/96text0
+PREPARE REMOVEFILE 9/96/96text1
+PREPARE REMOVEDIR 9/96/
+PREPARE REMOVEDIR 9/95/
+PREPARE REMOVEDIR 9/95/
+PREPARE REMOVEDIR 9/94/
+PREPARE REMOVEDIR 9/94/
+PREPARE REMOVEDIR 9/93/
+PREPARE REMOVEDIR 9/92/
+PREPARE REMOVEDIR 9/91/
+PREPARE REMOVEDIR 9/90/
+PREPARE REMOVEDIR 9/90/
+PREPARE REMOVEDIR 8/89/
+PREPARE REMOVEDIR 8/89/
+PREPARE REMOVEDIR 8/88/
+PREPARE REMOVEFILE 8/87/870/87xtext0
+PREPARE REMOVEFILE 8/87/870/87xtext1
+PREPARE REMOVEDIR 8/87/870/
+PREPARE REMOVEFILE 8/87/871/87xtext0
+PREPARE REMOVEFILE 8/87/871/87xtext1
+PREPARE REMOVEDIR 8/87/871/
+PREPARE REMOVEDIR 8/87/
+PREPARE REMOVEFILE 8/86/86text0
+PREPARE REMOVEFILE 8/86/86text1
+PREPARE REMOVEDIR 8/86/
+PREPARE REMOVEDIR 8/85/
+PREPARE REMOVEDIR 8/85/
+PREPARE REMOVEDIR 8/84/
+PREPARE REMOVEDIR 8/84/
+PREPARE REMOVEDIR 8/83/
+PREPARE REMOVEDIR 8/82/
+PREPARE REMOVEDIR 8/81/
+PREPARE REMOVEDIR 8/80/
+PREPARE REMOVEDIR 8/80/
+PREPARE REMOVEFILE 7/70/7xtest.exe
+PREPARE REMOVEFILE 7/70/7xtext0
+PREPARE REMOVEFILE 7/70/7xtext1
+PREPARE REMOVEDIR 7/70/
+PREPARE REMOVEFILE 7/71/7xtest.exe
+PREPARE REMOVEFILE 7/71/7xtext0
+PREPARE REMOVEFILE 7/71/7xtext1
+PREPARE REMOVEDIR 7/71/
+PREPARE REMOVEFILE 7/7text0
+PREPARE REMOVEFILE 7/7text1
+PREPARE REMOVEDIR 7/
+PREPARE REMOVEDIR 6/
+PREPARE REMOVEFILE 5/5text1
+PREPARE REMOVEFILE 5/5text0
+PREPARE REMOVEFILE 5/5test.exe
+PREPARE REMOVEFILE 5/5text0
+PREPARE REMOVEFILE 5/5text1
+PREPARE REMOVEDIR 5/
+PREPARE REMOVEFILE 4/4text1
+PREPARE REMOVEFILE 4/4text0
+PREPARE REMOVEDIR 4/
+PREPARE REMOVEFILE 3/3text1
+PREPARE REMOVEFILE 3/3text0
+PREPARE REMOVEDIR 1/10/
+PREPARE REMOVEDIR 1/
+EXECUTE ADD searchplugins/searchpluginstext0
+EXECUTE PATCH searchplugins/searchpluginspng1.png
+EXECUTE PATCH searchplugins/searchpluginspng0.png
+EXECUTE ADD precomplete
+EXECUTE PATCH exe0.exe
+EXECUTE ADD distribution/extensions/extensions1/extensions1text0
+EXECUTE PATCH distribution/extensions/extensions1/extensions1png1.png
+EXECUTE PATCH distribution/extensions/extensions1/extensions1png0.png
+EXECUTE ADD distribution/extensions/extensions0/extensions0text0
+EXECUTE PATCH distribution/extensions/extensions0/extensions0png1.png
+EXECUTE PATCH distribution/extensions/extensions0/extensions0png0.png
+EXECUTE PATCH 0/0exe0.exe
+LoadSourceFile: destination file size 776 does not match expected size 79872
+LoadSourceFile failed
+### execution failed
+FINISH ADD searchplugins/searchpluginstext0
+FINISH PATCH searchplugins/searchpluginspng1.png
+FINISH PATCH searchplugins/searchpluginspng0.png
+FINISH ADD precomplete
+FINISH PATCH exe0.exe
+FINISH ADD distribution/extensions/extensions1/extensions1text0
+backup_restore: backup file doesn't exist: distribution/extensions/extensions1/extensions1text0.moz-backup
+FINISH PATCH distribution/extensions/extensions1/extensions1png1.png
+FINISH PATCH distribution/extensions/extensions1/extensions1png0.png
+FINISH ADD distribution/extensions/extensions0/extensions0text0
+FINISH PATCH distribution/extensions/extensions0/extensions0png1.png
+FINISH PATCH distribution/extensions/extensions0/extensions0png0.png
+FINISH PATCH 0/0exe0.exe
+backup_restore: backup file doesn't exist: 0/0exe0.exe.moz-backup
+FINISH ADD 0/00/00text0
+backup_restore: backup file doesn't exist: 0/00/00text0.moz-backup
+FINISH PATCH 0/00/00png0.png
+backup_restore: backup file doesn't exist: 0/00/00png0.png.moz-backup
+FINISH ADD 2/20/20text0
+backup_restore: backup file doesn't exist: 2/20/20text0.moz-backup
+FINISH ADD 2/20/20png0.png
+backup_restore: backup file doesn't exist: 2/20/20png0.png.moz-backup
+FINISH ADD 0/00/00text2
+backup_restore: backup file doesn't exist: 0/00/00text2.moz-backup
+FINISH REMOVEFILE 1/10/10text0
+backup_restore: backup file doesn't exist: 1/10/10text0.moz-backup
+FINISH REMOVEFILE 0/00/00text1
+backup_restore: backup file doesn't exist: 0/00/00text1.moz-backup
+FINISH REMOVEFILE 9/97/970/97xtext0
+backup_restore: backup file doesn't exist: 9/97/970/97xtext0.moz-backup
+FINISH REMOVEFILE 9/97/970/97xtext1
+backup_restore: backup file doesn't exist: 9/97/970/97xtext1.moz-backup
+FINISH REMOVEFILE 9/97/971/97xtext0
+backup_restore: backup file doesn't exist: 9/97/971/97xtext0.moz-backup
+FINISH REMOVEFILE 9/97/971/97xtext1
+backup_restore: backup file doesn't exist: 9/97/971/97xtext1.moz-backup
+FINISH REMOVEFILE 9/96/96text0
+backup_restore: backup file doesn't exist: 9/96/96text0.moz-backup
+FINISH REMOVEFILE 9/96/96text1
+backup_restore: backup file doesn't exist: 9/96/96text1.moz-backup
+FINISH REMOVEFILE 8/87/870/87xtext0
+backup_restore: backup file doesn't exist: 8/87/870/87xtext0.moz-backup
+FINISH REMOVEFILE 8/87/870/87xtext1
+backup_restore: backup file doesn't exist: 8/87/870/87xtext1.moz-backup
+FINISH REMOVEFILE 8/87/871/87xtext0
+backup_restore: backup file doesn't exist: 8/87/871/87xtext0.moz-backup
+FINISH REMOVEFILE 8/87/871/87xtext1
+backup_restore: backup file doesn't exist: 8/87/871/87xtext1.moz-backup
+FINISH REMOVEFILE 8/86/86text0
+backup_restore: backup file doesn't exist: 8/86/86text0.moz-backup
+FINISH REMOVEFILE 8/86/86text1
+backup_restore: backup file doesn't exist: 8/86/86text1.moz-backup
+FINISH REMOVEFILE 7/70/7xtest.exe
+backup_restore: backup file doesn't exist: 7/70/7xtest.exe.moz-backup
+FINISH REMOVEFILE 7/70/7xtext0
+backup_restore: backup file doesn't exist: 7/70/7xtext0.moz-backup
+FINISH REMOVEFILE 7/70/7xtext1
+backup_restore: backup file doesn't exist: 7/70/7xtext1.moz-backup
+FINISH REMOVEFILE 7/71/7xtest.exe
+backup_restore: backup file doesn't exist: 7/71/7xtest.exe.moz-backup
+FINISH REMOVEFILE 7/71/7xtext0
+backup_restore: backup file doesn't exist: 7/71/7xtext0.moz-backup
+FINISH REMOVEFILE 7/71/7xtext1
+backup_restore: backup file doesn't exist: 7/71/7xtext1.moz-backup
+FINISH REMOVEFILE 7/7text0
+backup_restore: backup file doesn't exist: 7/7text0.moz-backup
+FINISH REMOVEFILE 7/7text1
+backup_restore: backup file doesn't exist: 7/7text1.moz-backup
+FINISH REMOVEFILE 5/5text1
+backup_restore: backup file doesn't exist: 5/5text1.moz-backup
+FINISH REMOVEFILE 5/5text0
+backup_restore: backup file doesn't exist: 5/5text0.moz-backup
+FINISH REMOVEFILE 5/5test.exe
+backup_restore: backup file doesn't exist: 5/5test.exe.moz-backup
+FINISH REMOVEFILE 5/5text0
+backup_restore: backup file doesn't exist: 5/5text0.moz-backup
+FINISH REMOVEFILE 5/5text1
+backup_restore: backup file doesn't exist: 5/5text1.moz-backup
+FINISH REMOVEFILE 4/4text1
+backup_restore: backup file doesn't exist: 4/4text1.moz-backup
+FINISH REMOVEFILE 4/4text0
+backup_restore: backup file doesn't exist: 4/4text0.moz-backup
+FINISH REMOVEFILE 3/3text1
+backup_restore: backup file doesn't exist: 3/3text1.moz-backup
+FINISH REMOVEFILE 3/3text0
+backup_restore: backup file doesn't exist: 3/3text0.moz-backup
+failed: 2
+calling QuitProgressUI
diff --git a/toolkit/mozapps/update/tests/data/partial_log_success_mac b/toolkit/mozapps/update/tests/data/partial_log_success_mac
new file mode 100644
index 000000000..fb5272ad2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial_log_success_mac
@@ -0,0 +1,279 @@
+UPDATE TYPE partial
+PREPARE ADD Contents/Resources/searchplugins/searchpluginstext0
+PREPARE PATCH Contents/Resources/searchplugins/searchpluginspng1.png
+PREPARE PATCH Contents/Resources/searchplugins/searchpluginspng0.png
+PREPARE ADD Contents/Resources/precomplete
+PREPARE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0
+PREPARE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png
+PREPARE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png
+PREPARE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0
+PREPARE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png
+PREPARE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png
+PREPARE PATCH Contents/Resources/0/0exe0.exe
+PREPARE ADD Contents/Resources/0/00/00text0
+PREPARE PATCH Contents/Resources/0/00/00png0.png
+PREPARE PATCH Contents/MacOS/exe0.exe
+PREPARE ADD Contents/Resources/2/20/20text0
+PREPARE ADD Contents/Resources/2/20/20png0.png
+PREPARE ADD Contents/Resources/0/00/00text2
+PREPARE REMOVEFILE Contents/Resources/1/10/10text0
+PREPARE REMOVEFILE Contents/Resources/0/00/00text1
+PREPARE REMOVEDIR Contents/Resources/9/99/
+PREPARE REMOVEDIR Contents/Resources/9/99/
+PREPARE REMOVEDIR Contents/Resources/9/98/
+PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext0
+PREPARE REMOVEFILE Contents/Resources/9/97/970/97xtext1
+PREPARE REMOVEDIR Contents/Resources/9/97/970/
+PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext0
+PREPARE REMOVEFILE Contents/Resources/9/97/971/97xtext1
+PREPARE REMOVEDIR Contents/Resources/9/97/971/
+PREPARE REMOVEDIR Contents/Resources/9/97/
+PREPARE REMOVEFILE Contents/Resources/9/96/96text0
+PREPARE REMOVEFILE Contents/Resources/9/96/96text1
+PREPARE REMOVEDIR Contents/Resources/9/96/
+PREPARE REMOVEDIR Contents/Resources/9/95/
+PREPARE REMOVEDIR Contents/Resources/9/95/
+PREPARE REMOVEDIR Contents/Resources/9/94/
+PREPARE REMOVEDIR Contents/Resources/9/94/
+PREPARE REMOVEDIR Contents/Resources/9/93/
+PREPARE REMOVEDIR Contents/Resources/9/92/
+PREPARE REMOVEDIR Contents/Resources/9/91/
+PREPARE REMOVEDIR Contents/Resources/9/90/
+PREPARE REMOVEDIR Contents/Resources/9/90/
+PREPARE REMOVEDIR Contents/Resources/8/89/
+PREPARE REMOVEDIR Contents/Resources/8/89/
+PREPARE REMOVEDIR Contents/Resources/8/88/
+PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext0
+PREPARE REMOVEFILE Contents/Resources/8/87/870/87xtext1
+PREPARE REMOVEDIR Contents/Resources/8/87/870/
+PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext0
+PREPARE REMOVEFILE Contents/Resources/8/87/871/87xtext1
+PREPARE REMOVEDIR Contents/Resources/8/87/871/
+PREPARE REMOVEDIR Contents/Resources/8/87/
+PREPARE REMOVEFILE Contents/Resources/8/86/86text0
+PREPARE REMOVEFILE Contents/Resources/8/86/86text1
+PREPARE REMOVEDIR Contents/Resources/8/86/
+PREPARE REMOVEDIR Contents/Resources/8/85/
+PREPARE REMOVEDIR Contents/Resources/8/85/
+PREPARE REMOVEDIR Contents/Resources/8/84/
+PREPARE REMOVEDIR Contents/Resources/8/84/
+PREPARE REMOVEDIR Contents/Resources/8/83/
+PREPARE REMOVEDIR Contents/Resources/8/82/
+PREPARE REMOVEDIR Contents/Resources/8/81/
+PREPARE REMOVEDIR Contents/Resources/8/80/
+PREPARE REMOVEDIR Contents/Resources/8/80/
+PREPARE REMOVEFILE Contents/Resources/7/70/7xtest.exe
+PREPARE REMOVEFILE Contents/Resources/7/70/7xtext0
+PREPARE REMOVEFILE Contents/Resources/7/70/7xtext1
+PREPARE REMOVEDIR Contents/Resources/7/70/
+PREPARE REMOVEFILE Contents/Resources/7/71/7xtest.exe
+PREPARE REMOVEFILE Contents/Resources/7/71/7xtext0
+PREPARE REMOVEFILE Contents/Resources/7/71/7xtext1
+PREPARE REMOVEDIR Contents/Resources/7/71/
+PREPARE REMOVEFILE Contents/Resources/7/7text0
+PREPARE REMOVEFILE Contents/Resources/7/7text1
+PREPARE REMOVEDIR Contents/Resources/7/
+PREPARE REMOVEDIR Contents/Resources/6/
+PREPARE REMOVEFILE Contents/Resources/5/5text1
+PREPARE REMOVEFILE Contents/Resources/5/5text0
+PREPARE REMOVEFILE Contents/Resources/5/5test.exe
+PREPARE REMOVEFILE Contents/Resources/5/5text0
+PREPARE REMOVEFILE Contents/Resources/5/5text1
+PREPARE REMOVEDIR Contents/Resources/5/
+PREPARE REMOVEFILE Contents/Resources/4/4text1
+PREPARE REMOVEFILE Contents/Resources/4/4text0
+PREPARE REMOVEDIR Contents/Resources/4/
+PREPARE REMOVEFILE Contents/Resources/3/3text1
+PREPARE REMOVEFILE Contents/Resources/3/3text0
+PREPARE REMOVEDIR Contents/Resources/1/10/
+PREPARE REMOVEDIR Contents/Resources/1/
+EXECUTE ADD Contents/Resources/searchplugins/searchpluginstext0
+EXECUTE PATCH Contents/Resources/searchplugins/searchpluginspng1.png
+EXECUTE PATCH Contents/Resources/searchplugins/searchpluginspng0.png
+EXECUTE ADD Contents/Resources/precomplete
+EXECUTE ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0
+EXECUTE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png
+EXECUTE PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png
+EXECUTE ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0
+EXECUTE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png
+EXECUTE PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png
+EXECUTE PATCH Contents/Resources/0/0exe0.exe
+EXECUTE ADD Contents/Resources/0/00/00text0
+EXECUTE PATCH Contents/Resources/0/00/00png0.png
+EXECUTE PATCH Contents/MacOS/exe0.exe
+EXECUTE ADD Contents/Resources/2/20/20text0
+EXECUTE ADD Contents/Resources/2/20/20png0.png
+EXECUTE ADD Contents/Resources/0/00/00text2
+EXECUTE REMOVEFILE Contents/Resources/1/10/10text0
+EXECUTE REMOVEFILE Contents/Resources/0/00/00text1
+EXECUTE REMOVEDIR Contents/Resources/9/99/
+EXECUTE REMOVEDIR Contents/Resources/9/99/
+EXECUTE REMOVEDIR Contents/Resources/9/98/
+EXECUTE REMOVEFILE Contents/Resources/9/97/970/97xtext0
+EXECUTE REMOVEFILE Contents/Resources/9/97/970/97xtext1
+EXECUTE REMOVEDIR Contents/Resources/9/97/970/
+EXECUTE REMOVEFILE Contents/Resources/9/97/971/97xtext0
+EXECUTE REMOVEFILE Contents/Resources/9/97/971/97xtext1
+EXECUTE REMOVEDIR Contents/Resources/9/97/971/
+EXECUTE REMOVEDIR Contents/Resources/9/97/
+EXECUTE REMOVEFILE Contents/Resources/9/96/96text0
+EXECUTE REMOVEFILE Contents/Resources/9/96/96text1
+EXECUTE REMOVEDIR Contents/Resources/9/96/
+EXECUTE REMOVEDIR Contents/Resources/9/95/
+EXECUTE REMOVEDIR Contents/Resources/9/95/
+EXECUTE REMOVEDIR Contents/Resources/9/94/
+EXECUTE REMOVEDIR Contents/Resources/9/94/
+EXECUTE REMOVEDIR Contents/Resources/9/93/
+EXECUTE REMOVEDIR Contents/Resources/9/92/
+EXECUTE REMOVEDIR Contents/Resources/9/91/
+EXECUTE REMOVEDIR Contents/Resources/9/90/
+EXECUTE REMOVEDIR Contents/Resources/9/90/
+EXECUTE REMOVEDIR Contents/Resources/8/89/
+EXECUTE REMOVEDIR Contents/Resources/8/89/
+EXECUTE REMOVEDIR Contents/Resources/8/88/
+EXECUTE REMOVEFILE Contents/Resources/8/87/870/87xtext0
+EXECUTE REMOVEFILE Contents/Resources/8/87/870/87xtext1
+EXECUTE REMOVEDIR Contents/Resources/8/87/870/
+EXECUTE REMOVEFILE Contents/Resources/8/87/871/87xtext0
+EXECUTE REMOVEFILE Contents/Resources/8/87/871/87xtext1
+EXECUTE REMOVEDIR Contents/Resources/8/87/871/
+EXECUTE REMOVEDIR Contents/Resources/8/87/
+EXECUTE REMOVEFILE Contents/Resources/8/86/86text0
+EXECUTE REMOVEFILE Contents/Resources/8/86/86text1
+EXECUTE REMOVEDIR Contents/Resources/8/86/
+EXECUTE REMOVEDIR Contents/Resources/8/85/
+EXECUTE REMOVEDIR Contents/Resources/8/85/
+EXECUTE REMOVEDIR Contents/Resources/8/84/
+EXECUTE REMOVEDIR Contents/Resources/8/84/
+EXECUTE REMOVEDIR Contents/Resources/8/83/
+EXECUTE REMOVEDIR Contents/Resources/8/82/
+EXECUTE REMOVEDIR Contents/Resources/8/81/
+EXECUTE REMOVEDIR Contents/Resources/8/80/
+EXECUTE REMOVEDIR Contents/Resources/8/80/
+EXECUTE REMOVEFILE Contents/Resources/7/70/7xtest.exe
+EXECUTE REMOVEFILE Contents/Resources/7/70/7xtext0
+EXECUTE REMOVEFILE Contents/Resources/7/70/7xtext1
+EXECUTE REMOVEDIR Contents/Resources/7/70/
+EXECUTE REMOVEFILE Contents/Resources/7/71/7xtest.exe
+EXECUTE REMOVEFILE Contents/Resources/7/71/7xtext0
+EXECUTE REMOVEFILE Contents/Resources/7/71/7xtext1
+EXECUTE REMOVEDIR Contents/Resources/7/71/
+EXECUTE REMOVEFILE Contents/Resources/7/7text0
+EXECUTE REMOVEFILE Contents/Resources/7/7text1
+EXECUTE REMOVEDIR Contents/Resources/7/
+EXECUTE REMOVEDIR Contents/Resources/6/
+EXECUTE REMOVEFILE Contents/Resources/5/5text1
+EXECUTE REMOVEFILE Contents/Resources/5/5text0
+EXECUTE REMOVEFILE Contents/Resources/5/5test.exe
+EXECUTE REMOVEFILE Contents/Resources/5/5text0
+file cannot be removed because it does not exist; skipping
+EXECUTE REMOVEFILE Contents/Resources/5/5text1
+file cannot be removed because it does not exist; skipping
+EXECUTE REMOVEDIR Contents/Resources/5/
+EXECUTE REMOVEFILE Contents/Resources/4/4text1
+EXECUTE REMOVEFILE Contents/Resources/4/4text0
+EXECUTE REMOVEDIR Contents/Resources/4/
+EXECUTE REMOVEFILE Contents/Resources/3/3text1
+EXECUTE REMOVEFILE Contents/Resources/3/3text0
+EXECUTE REMOVEDIR Contents/Resources/1/10/
+EXECUTE REMOVEDIR Contents/Resources/1/
+FINISH ADD Contents/Resources/searchplugins/searchpluginstext0
+FINISH PATCH Contents/Resources/searchplugins/searchpluginspng1.png
+FINISH PATCH Contents/Resources/searchplugins/searchpluginspng0.png
+FINISH ADD Contents/Resources/precomplete
+FINISH ADD Contents/Resources/distribution/extensions/extensions1/extensions1text0
+FINISH PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png1.png
+FINISH PATCH Contents/Resources/distribution/extensions/extensions1/extensions1png0.png
+FINISH ADD Contents/Resources/distribution/extensions/extensions0/extensions0text0
+FINISH PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png1.png
+FINISH PATCH Contents/Resources/distribution/extensions/extensions0/extensions0png0.png
+FINISH PATCH Contents/Resources/0/0exe0.exe
+FINISH ADD Contents/Resources/0/00/00text0
+FINISH PATCH Contents/Resources/0/00/00png0.png
+FINISH PATCH Contents/MacOS/exe0.exe
+FINISH ADD Contents/Resources/2/20/20text0
+FINISH ADD Contents/Resources/2/20/20png0.png
+FINISH ADD Contents/Resources/0/00/00text2
+FINISH REMOVEFILE Contents/Resources/1/10/10text0
+FINISH REMOVEFILE Contents/Resources/0/00/00text1
+FINISH REMOVEDIR Contents/Resources/9/99/
+FINISH REMOVEDIR Contents/Resources/9/99/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/9/98/
+FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext0
+FINISH REMOVEFILE Contents/Resources/9/97/970/97xtext1
+FINISH REMOVEDIR Contents/Resources/9/97/970/
+FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext0
+FINISH REMOVEFILE Contents/Resources/9/97/971/97xtext1
+FINISH REMOVEDIR Contents/Resources/9/97/971/
+FINISH REMOVEDIR Contents/Resources/9/97/
+FINISH REMOVEFILE Contents/Resources/9/96/96text0
+FINISH REMOVEFILE Contents/Resources/9/96/96text1
+FINISH REMOVEDIR Contents/Resources/9/96/
+FINISH REMOVEDIR Contents/Resources/9/95/
+FINISH REMOVEDIR Contents/Resources/9/95/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/9/94/
+FINISH REMOVEDIR Contents/Resources/9/94/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/9/93/
+FINISH REMOVEDIR Contents/Resources/9/92/
+removing directory: Contents/Resources/9/92/, rv: 0
+FINISH REMOVEDIR Contents/Resources/9/91/
+removing directory: Contents/Resources/9/91/, rv: 0
+FINISH REMOVEDIR Contents/Resources/9/90/
+FINISH REMOVEDIR Contents/Resources/9/90/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/8/89/
+FINISH REMOVEDIR Contents/Resources/8/89/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/8/88/
+FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext0
+FINISH REMOVEFILE Contents/Resources/8/87/870/87xtext1
+FINISH REMOVEDIR Contents/Resources/8/87/870/
+FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext0
+FINISH REMOVEFILE Contents/Resources/8/87/871/87xtext1
+FINISH REMOVEDIR Contents/Resources/8/87/871/
+FINISH REMOVEDIR Contents/Resources/8/87/
+FINISH REMOVEFILE Contents/Resources/8/86/86text0
+FINISH REMOVEFILE Contents/Resources/8/86/86text1
+FINISH REMOVEDIR Contents/Resources/8/86/
+FINISH REMOVEDIR Contents/Resources/8/85/
+FINISH REMOVEDIR Contents/Resources/8/85/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/8/84/
+FINISH REMOVEDIR Contents/Resources/8/84/
+directory no longer exists; skipping
+FINISH REMOVEDIR Contents/Resources/8/83/
+FINISH REMOVEDIR Contents/Resources/8/82/
+removing directory: Contents/Resources/8/82/, rv: 0
+FINISH REMOVEDIR Contents/Resources/8/81/
+removing directory: Contents/Resources/8/81/, rv: 0
+FINISH REMOVEDIR Contents/Resources/8/80/
+FINISH REMOVEDIR Contents/Resources/8/80/
+directory no longer exists; skipping
+FINISH REMOVEFILE Contents/Resources/7/70/7xtest.exe
+FINISH REMOVEFILE Contents/Resources/7/70/7xtext0
+FINISH REMOVEFILE Contents/Resources/7/70/7xtext1
+FINISH REMOVEDIR Contents/Resources/7/70/
+FINISH REMOVEFILE Contents/Resources/7/71/7xtest.exe
+FINISH REMOVEFILE Contents/Resources/7/71/7xtext0
+FINISH REMOVEFILE Contents/Resources/7/71/7xtext1
+FINISH REMOVEDIR Contents/Resources/7/71/
+FINISH REMOVEFILE Contents/Resources/7/7text0
+FINISH REMOVEFILE Contents/Resources/7/7text1
+FINISH REMOVEDIR Contents/Resources/7/
+FINISH REMOVEDIR Contents/Resources/6/
+FINISH REMOVEFILE Contents/Resources/5/5text1
+FINISH REMOVEFILE Contents/Resources/5/5text0
+FINISH REMOVEFILE Contents/Resources/5/5test.exe
+FINISH REMOVEDIR Contents/Resources/5/
+FINISH REMOVEFILE Contents/Resources/4/4text1
+FINISH REMOVEFILE Contents/Resources/4/4text0
+FINISH REMOVEDIR Contents/Resources/4/
+FINISH REMOVEFILE Contents/Resources/3/3text1
+FINISH REMOVEFILE Contents/Resources/3/3text0
+FINISH REMOVEDIR Contents/Resources/1/10/
+FINISH REMOVEDIR Contents/Resources/1/
+succeeded
+calling QuitProgressUI
diff --git a/toolkit/mozapps/update/tests/data/partial_log_success_win b/toolkit/mozapps/update/tests/data/partial_log_success_win
new file mode 100644
index 000000000..1f5c4b3b4
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial_log_success_win
@@ -0,0 +1,279 @@
+UPDATE TYPE partial
+PREPARE ADD searchplugins/searchpluginstext0
+PREPARE PATCH searchplugins/searchpluginspng1.png
+PREPARE PATCH searchplugins/searchpluginspng0.png
+PREPARE ADD precomplete
+PREPARE PATCH exe0.exe
+PREPARE ADD distribution/extensions/extensions1/extensions1text0
+PREPARE PATCH distribution/extensions/extensions1/extensions1png1.png
+PREPARE PATCH distribution/extensions/extensions1/extensions1png0.png
+PREPARE ADD distribution/extensions/extensions0/extensions0text0
+PREPARE PATCH distribution/extensions/extensions0/extensions0png1.png
+PREPARE PATCH distribution/extensions/extensions0/extensions0png0.png
+PREPARE PATCH 0/0exe0.exe
+PREPARE ADD 0/00/00text0
+PREPARE PATCH 0/00/00png0.png
+PREPARE ADD 2/20/20text0
+PREPARE ADD 2/20/20png0.png
+PREPARE ADD 0/00/00text2
+PREPARE REMOVEFILE 1/10/10text0
+PREPARE REMOVEFILE 0/00/00text1
+PREPARE REMOVEDIR 9/99/
+PREPARE REMOVEDIR 9/99/
+PREPARE REMOVEDIR 9/98/
+PREPARE REMOVEFILE 9/97/970/97xtext0
+PREPARE REMOVEFILE 9/97/970/97xtext1
+PREPARE REMOVEDIR 9/97/970/
+PREPARE REMOVEFILE 9/97/971/97xtext0
+PREPARE REMOVEFILE 9/97/971/97xtext1
+PREPARE REMOVEDIR 9/97/971/
+PREPARE REMOVEDIR 9/97/
+PREPARE REMOVEFILE 9/96/96text0
+PREPARE REMOVEFILE 9/96/96text1
+PREPARE REMOVEDIR 9/96/
+PREPARE REMOVEDIR 9/95/
+PREPARE REMOVEDIR 9/95/
+PREPARE REMOVEDIR 9/94/
+PREPARE REMOVEDIR 9/94/
+PREPARE REMOVEDIR 9/93/
+PREPARE REMOVEDIR 9/92/
+PREPARE REMOVEDIR 9/91/
+PREPARE REMOVEDIR 9/90/
+PREPARE REMOVEDIR 9/90/
+PREPARE REMOVEDIR 8/89/
+PREPARE REMOVEDIR 8/89/
+PREPARE REMOVEDIR 8/88/
+PREPARE REMOVEFILE 8/87/870/87xtext0
+PREPARE REMOVEFILE 8/87/870/87xtext1
+PREPARE REMOVEDIR 8/87/870/
+PREPARE REMOVEFILE 8/87/871/87xtext0
+PREPARE REMOVEFILE 8/87/871/87xtext1
+PREPARE REMOVEDIR 8/87/871/
+PREPARE REMOVEDIR 8/87/
+PREPARE REMOVEFILE 8/86/86text0
+PREPARE REMOVEFILE 8/86/86text1
+PREPARE REMOVEDIR 8/86/
+PREPARE REMOVEDIR 8/85/
+PREPARE REMOVEDIR 8/85/
+PREPARE REMOVEDIR 8/84/
+PREPARE REMOVEDIR 8/84/
+PREPARE REMOVEDIR 8/83/
+PREPARE REMOVEDIR 8/82/
+PREPARE REMOVEDIR 8/81/
+PREPARE REMOVEDIR 8/80/
+PREPARE REMOVEDIR 8/80/
+PREPARE REMOVEFILE 7/70/7xtest.exe
+PREPARE REMOVEFILE 7/70/7xtext0
+PREPARE REMOVEFILE 7/70/7xtext1
+PREPARE REMOVEDIR 7/70/
+PREPARE REMOVEFILE 7/71/7xtest.exe
+PREPARE REMOVEFILE 7/71/7xtext0
+PREPARE REMOVEFILE 7/71/7xtext1
+PREPARE REMOVEDIR 7/71/
+PREPARE REMOVEFILE 7/7text0
+PREPARE REMOVEFILE 7/7text1
+PREPARE REMOVEDIR 7/
+PREPARE REMOVEDIR 6/
+PREPARE REMOVEFILE 5/5text1
+PREPARE REMOVEFILE 5/5text0
+PREPARE REMOVEFILE 5/5test.exe
+PREPARE REMOVEFILE 5/5text0
+PREPARE REMOVEFILE 5/5text1
+PREPARE REMOVEDIR 5/
+PREPARE REMOVEFILE 4/4text1
+PREPARE REMOVEFILE 4/4text0
+PREPARE REMOVEDIR 4/
+PREPARE REMOVEFILE 3/3text1
+PREPARE REMOVEFILE 3/3text0
+PREPARE REMOVEDIR 1/10/
+PREPARE REMOVEDIR 1/
+EXECUTE ADD searchplugins/searchpluginstext0
+EXECUTE PATCH searchplugins/searchpluginspng1.png
+EXECUTE PATCH searchplugins/searchpluginspng0.png
+EXECUTE ADD precomplete
+EXECUTE PATCH exe0.exe
+EXECUTE ADD distribution/extensions/extensions1/extensions1text0
+EXECUTE PATCH distribution/extensions/extensions1/extensions1png1.png
+EXECUTE PATCH distribution/extensions/extensions1/extensions1png0.png
+EXECUTE ADD distribution/extensions/extensions0/extensions0text0
+EXECUTE PATCH distribution/extensions/extensions0/extensions0png1.png
+EXECUTE PATCH distribution/extensions/extensions0/extensions0png0.png
+EXECUTE PATCH 0/0exe0.exe
+EXECUTE ADD 0/00/00text0
+EXECUTE PATCH 0/00/00png0.png
+EXECUTE ADD 2/20/20text0
+EXECUTE ADD 2/20/20png0.png
+EXECUTE ADD 0/00/00text2
+EXECUTE REMOVEFILE 1/10/10text0
+EXECUTE REMOVEFILE 0/00/00text1
+EXECUTE REMOVEDIR 9/99/
+EXECUTE REMOVEDIR 9/99/
+EXECUTE REMOVEDIR 9/98/
+EXECUTE REMOVEFILE 9/97/970/97xtext0
+EXECUTE REMOVEFILE 9/97/970/97xtext1
+EXECUTE REMOVEDIR 9/97/970/
+EXECUTE REMOVEFILE 9/97/971/97xtext0
+EXECUTE REMOVEFILE 9/97/971/97xtext1
+EXECUTE REMOVEDIR 9/97/971/
+EXECUTE REMOVEDIR 9/97/
+EXECUTE REMOVEFILE 9/96/96text0
+EXECUTE REMOVEFILE 9/96/96text1
+EXECUTE REMOVEDIR 9/96/
+EXECUTE REMOVEDIR 9/95/
+EXECUTE REMOVEDIR 9/95/
+EXECUTE REMOVEDIR 9/94/
+EXECUTE REMOVEDIR 9/94/
+EXECUTE REMOVEDIR 9/93/
+EXECUTE REMOVEDIR 9/92/
+EXECUTE REMOVEDIR 9/91/
+EXECUTE REMOVEDIR 9/90/
+EXECUTE REMOVEDIR 9/90/
+EXECUTE REMOVEDIR 8/89/
+EXECUTE REMOVEDIR 8/89/
+EXECUTE REMOVEDIR 8/88/
+EXECUTE REMOVEFILE 8/87/870/87xtext0
+EXECUTE REMOVEFILE 8/87/870/87xtext1
+EXECUTE REMOVEDIR 8/87/870/
+EXECUTE REMOVEFILE 8/87/871/87xtext0
+EXECUTE REMOVEFILE 8/87/871/87xtext1
+EXECUTE REMOVEDIR 8/87/871/
+EXECUTE REMOVEDIR 8/87/
+EXECUTE REMOVEFILE 8/86/86text0
+EXECUTE REMOVEFILE 8/86/86text1
+EXECUTE REMOVEDIR 8/86/
+EXECUTE REMOVEDIR 8/85/
+EXECUTE REMOVEDIR 8/85/
+EXECUTE REMOVEDIR 8/84/
+EXECUTE REMOVEDIR 8/84/
+EXECUTE REMOVEDIR 8/83/
+EXECUTE REMOVEDIR 8/82/
+EXECUTE REMOVEDIR 8/81/
+EXECUTE REMOVEDIR 8/80/
+EXECUTE REMOVEDIR 8/80/
+EXECUTE REMOVEFILE 7/70/7xtest.exe
+EXECUTE REMOVEFILE 7/70/7xtext0
+EXECUTE REMOVEFILE 7/70/7xtext1
+EXECUTE REMOVEDIR 7/70/
+EXECUTE REMOVEFILE 7/71/7xtest.exe
+EXECUTE REMOVEFILE 7/71/7xtext0
+EXECUTE REMOVEFILE 7/71/7xtext1
+EXECUTE REMOVEDIR 7/71/
+EXECUTE REMOVEFILE 7/7text0
+EXECUTE REMOVEFILE 7/7text1
+EXECUTE REMOVEDIR 7/
+EXECUTE REMOVEDIR 6/
+EXECUTE REMOVEFILE 5/5text1
+EXECUTE REMOVEFILE 5/5text0
+EXECUTE REMOVEFILE 5/5test.exe
+EXECUTE REMOVEFILE 5/5text0
+file cannot be removed because it does not exist; skipping
+EXECUTE REMOVEFILE 5/5text1
+file cannot be removed because it does not exist; skipping
+EXECUTE REMOVEDIR 5/
+EXECUTE REMOVEFILE 4/4text1
+EXECUTE REMOVEFILE 4/4text0
+EXECUTE REMOVEDIR 4/
+EXECUTE REMOVEFILE 3/3text1
+EXECUTE REMOVEFILE 3/3text0
+EXECUTE REMOVEDIR 1/10/
+EXECUTE REMOVEDIR 1/
+FINISH ADD searchplugins/searchpluginstext0
+FINISH PATCH searchplugins/searchpluginspng1.png
+FINISH PATCH searchplugins/searchpluginspng0.png
+FINISH ADD precomplete
+FINISH PATCH exe0.exe
+FINISH ADD distribution/extensions/extensions1/extensions1text0
+FINISH PATCH distribution/extensions/extensions1/extensions1png1.png
+FINISH PATCH distribution/extensions/extensions1/extensions1png0.png
+FINISH ADD distribution/extensions/extensions0/extensions0text0
+FINISH PATCH distribution/extensions/extensions0/extensions0png1.png
+FINISH PATCH distribution/extensions/extensions0/extensions0png0.png
+FINISH PATCH 0/0exe0.exe
+FINISH ADD 0/00/00text0
+FINISH PATCH 0/00/00png0.png
+FINISH ADD 2/20/20text0
+FINISH ADD 2/20/20png0.png
+FINISH ADD 0/00/00text2
+FINISH REMOVEFILE 1/10/10text0
+FINISH REMOVEFILE 0/00/00text1
+FINISH REMOVEDIR 9/99/
+FINISH REMOVEDIR 9/99/
+directory no longer exists; skipping
+FINISH REMOVEDIR 9/98/
+FINISH REMOVEFILE 9/97/970/97xtext0
+FINISH REMOVEFILE 9/97/970/97xtext1
+FINISH REMOVEDIR 9/97/970/
+FINISH REMOVEFILE 9/97/971/97xtext0
+FINISH REMOVEFILE 9/97/971/97xtext1
+FINISH REMOVEDIR 9/97/971/
+FINISH REMOVEDIR 9/97/
+FINISH REMOVEFILE 9/96/96text0
+FINISH REMOVEFILE 9/96/96text1
+FINISH REMOVEDIR 9/96/
+FINISH REMOVEDIR 9/95/
+FINISH REMOVEDIR 9/95/
+directory no longer exists; skipping
+FINISH REMOVEDIR 9/94/
+FINISH REMOVEDIR 9/94/
+directory no longer exists; skipping
+FINISH REMOVEDIR 9/93/
+FINISH REMOVEDIR 9/92/
+removing directory: 9/92/, rv: 0
+FINISH REMOVEDIR 9/91/
+removing directory: 9/91/, rv: 0
+FINISH REMOVEDIR 9/90/
+FINISH REMOVEDIR 9/90/
+directory no longer exists; skipping
+FINISH REMOVEDIR 8/89/
+FINISH REMOVEDIR 8/89/
+directory no longer exists; skipping
+FINISH REMOVEDIR 8/88/
+FINISH REMOVEFILE 8/87/870/87xtext0
+FINISH REMOVEFILE 8/87/870/87xtext1
+FINISH REMOVEDIR 8/87/870/
+FINISH REMOVEFILE 8/87/871/87xtext0
+FINISH REMOVEFILE 8/87/871/87xtext1
+FINISH REMOVEDIR 8/87/871/
+FINISH REMOVEDIR 8/87/
+FINISH REMOVEFILE 8/86/86text0
+FINISH REMOVEFILE 8/86/86text1
+FINISH REMOVEDIR 8/86/
+FINISH REMOVEDIR 8/85/
+FINISH REMOVEDIR 8/85/
+directory no longer exists; skipping
+FINISH REMOVEDIR 8/84/
+FINISH REMOVEDIR 8/84/
+directory no longer exists; skipping
+FINISH REMOVEDIR 8/83/
+FINISH REMOVEDIR 8/82/
+removing directory: 8/82/, rv: 0
+FINISH REMOVEDIR 8/81/
+removing directory: 8/81/, rv: 0
+FINISH REMOVEDIR 8/80/
+FINISH REMOVEDIR 8/80/
+directory no longer exists; skipping
+FINISH REMOVEFILE 7/70/7xtest.exe
+FINISH REMOVEFILE 7/70/7xtext0
+FINISH REMOVEFILE 7/70/7xtext1
+FINISH REMOVEDIR 7/70/
+FINISH REMOVEFILE 7/71/7xtest.exe
+FINISH REMOVEFILE 7/71/7xtext0
+FINISH REMOVEFILE 7/71/7xtext1
+FINISH REMOVEDIR 7/71/
+FINISH REMOVEFILE 7/7text0
+FINISH REMOVEFILE 7/7text1
+FINISH REMOVEDIR 7/
+FINISH REMOVEDIR 6/
+FINISH REMOVEFILE 5/5text1
+FINISH REMOVEFILE 5/5text0
+FINISH REMOVEFILE 5/5test.exe
+FINISH REMOVEDIR 5/
+FINISH REMOVEFILE 4/4text1
+FINISH REMOVEFILE 4/4text0
+FINISH REMOVEDIR 4/
+FINISH REMOVEFILE 3/3text1
+FINISH REMOVEFILE 3/3text0
+FINISH REMOVEDIR 1/10/
+FINISH REMOVEDIR 1/
+succeeded
+calling QuitProgressUI
diff --git a/toolkit/mozapps/update/tests/data/partial_mac.mar b/toolkit/mozapps/update/tests/data/partial_mac.mar
new file mode 100644
index 000000000..5a702ed4a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial_mac.mar
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/partial_precomplete b/toolkit/mozapps/update/tests/data/partial_precomplete
new file mode 100644
index 000000000..3ec201463
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial_precomplete
@@ -0,0 +1,19 @@
+remove "searchplugins/searchpluginstext0"
+remove "searchplugins/searchpluginspng1.png"
+remove "searchplugins/searchpluginspng0.png"
+remove "removed-files"
+remove "precomplete"
+remove "exe0.exe"
+remove "2/20/20text0"
+remove "2/20/20png0.png"
+remove "0/0exe0.exe"
+remove "0/00/00text2"
+remove "0/00/00text0"
+remove "0/00/00png0.png"
+rmdir "searchplugins/"
+rmdir "defaults/pref/"
+rmdir "defaults/"
+rmdir "2/20/"
+rmdir "2/"
+rmdir "0/00/"
+rmdir "0/"
diff --git a/toolkit/mozapps/update/tests/data/partial_precomplete_mac b/toolkit/mozapps/update/tests/data/partial_precomplete_mac
new file mode 100644
index 000000000..c65b6e4e3
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial_precomplete_mac
@@ -0,0 +1,22 @@
+remove "Contents/Resources/searchplugins/searchpluginstext0"
+remove "Contents/Resources/searchplugins/searchpluginspng1.png"
+remove "Contents/Resources/searchplugins/searchpluginspng0.png"
+remove "Contents/Resources/removed-files"
+remove "Contents/Resources/precomplete"
+remove "Contents/Resources/2/20/20text0"
+remove "Contents/Resources/2/20/20png0.png"
+remove "Contents/Resources/0/0exe0.exe"
+remove "Contents/Resources/0/00/00text2"
+remove "Contents/Resources/0/00/00text0"
+remove "Contents/Resources/0/00/00png0.png"
+remove "Contents/MacOS/exe0.exe"
+rmdir "Contents/Resources/searchplugins/"
+rmdir "Contents/Resources/defaults/pref/"
+rmdir "Contents/Resources/defaults/"
+rmdir "Contents/Resources/2/20/"
+rmdir "Contents/Resources/2/"
+rmdir "Contents/Resources/0/00/"
+rmdir "Contents/Resources/0/"
+rmdir "Contents/Resources/"
+rmdir "Contents/MacOS/"
+rmdir "Contents/"
diff --git a/toolkit/mozapps/update/tests/data/partial_removed-files b/toolkit/mozapps/update/tests/data/partial_removed-files
new file mode 100644
index 000000000..881311b82
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial_removed-files
@@ -0,0 +1,41 @@
+a/b/text0
+a/b/text1
+a/b/3/3text0
+a/b/3/3text1
+a/b/4/4exe0.exe
+a/b/4/4text0
+a/b/4/4text1
+a/b/4/
+a/b/5/5text0
+a/b/5/5text1
+a/b/5/*
+a/b/6/
+a/b/7/*
+a/b/8/80/
+a/b/8/81/
+a/b/8/82/
+a/b/8/83/
+a/b/8/84/
+a/b/8/85/*
+a/b/8/86/*
+a/b/8/87/*
+a/b/8/88/*
+a/b/8/89/*
+a/b/8/80/
+a/b/8/84/*
+a/b/8/85/*
+a/b/8/89/
+a/b/9/90/
+a/b/9/91/
+a/b/9/92/
+a/b/9/93/
+a/b/9/94/
+a/b/9/95/*
+a/b/9/96/*
+a/b/9/97/*
+a/b/9/98/*
+a/b/9/99/*
+a/b/9/90/
+a/b/9/94/*
+a/b/9/95/*
+a/b/9/99/
diff --git a/toolkit/mozapps/update/tests/data/partial_removed-files_mac b/toolkit/mozapps/update/tests/data/partial_removed-files_mac
new file mode 100644
index 000000000..955dc5b34
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial_removed-files_mac
@@ -0,0 +1,41 @@
+Contents/Resources/text0
+Contents/Resources/text1
+Contents/Resources/3/3text0
+Contents/Resources/3/3text1
+Contents/Resources/4/exe0.exe
+Contents/Resources/4/4text0
+Contents/Resources/4/4text1
+Contents/Resources/4/
+Contents/Resources/5/5text0
+Contents/Resources/5/5text1
+Contents/Resources/5/*
+Contents/Resources/6/
+Contents/Resources/7/*
+Contents/Resources/8/80/
+Contents/Resources/8/81/
+Contents/Resources/8/82/
+Contents/Resources/8/83/
+Contents/Resources/8/84/
+Contents/Resources/8/85/*
+Contents/Resources/8/86/*
+Contents/Resources/8/87/*
+Contents/Resources/8/88/*
+Contents/Resources/8/89/*
+Contents/Resources/8/80/
+Contents/Resources/8/84/*
+Contents/Resources/8/85/*
+Contents/Resources/8/89/
+Contents/Resources/9/90/
+Contents/Resources/9/91/
+Contents/Resources/9/92/
+Contents/Resources/9/93/
+Contents/Resources/9/94/
+Contents/Resources/9/95/*
+Contents/Resources/9/96/*
+Contents/Resources/9/97/*
+Contents/Resources/9/98/*
+Contents/Resources/9/99/*
+Contents/Resources/9/90/
+Contents/Resources/9/94/*
+Contents/Resources/9/95/*
+Contents/Resources/9/99/
diff --git a/toolkit/mozapps/update/tests/data/partial_update_manifest b/toolkit/mozapps/update/tests/data/partial_update_manifest
new file mode 100644
index 000000000..8d4e60ed2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/partial_update_manifest
@@ -0,0 +1,63 @@
+type "partial"
+add "precomplete"
+add "a/b/searchplugins/searchpluginstext0"
+patch-if "a/b/searchplugins/searchpluginspng1.png" "a/b/searchplugins/searchpluginspng1.png.patch" "a/b/searchplugins/searchpluginspng1.png"
+patch-if "a/b/searchplugins/searchpluginspng0.png" "a/b/searchplugins/searchpluginspng0.png.patch" "a/b/searchplugins/searchpluginspng0.png"
+add-if "a/b/extensions/extensions1" "a/b/extensions/extensions1/extensions1text0"
+patch-if "a/b/extensions/extensions1" "a/b/extensions/extensions1/extensions1png1.png.patch" "a/b/extensions/extensions1/extensions1png1.png"
+patch-if "a/b/extensions/extensions1" "a/b/extensions/extensions1/extensions1png0.png.patch" "a/b/extensions/extensions1/extensions1png0.png"
+add-if "a/b/extensions/extensions0" "a/b/extensions/extensions0/extensions0text0"
+patch-if "a/b/extensions/extensions0" "a/b/extensions/extensions0/extensions0png1.png.patch" "a/b/extensions/extensions0/extensions0png1.png"
+patch-if "a/b/extensions/extensions0" "a/b/extensions/extensions0/extensions0png0.png.patch" "a/b/extensions/extensions0/extensions0png0.png"
+patch "a/b/exe0.exe.patch" "a/b/exe0.exe"
+patch "a/b/0/0exe0.exe.patch" "a/b/0/0exe0.exe"
+add "a/b/0/00/00text0"
+patch "a/b/0/00/00png0.png.patch" "a/b/0/00/00png0.png"
+add "a/b/2/20/20text0"
+add "a/b/2/20/20png0.png"
+add "a/b/0/00/00text2"
+remove "a/b/1/10/10text0"
+remove "a/b/0/00/00text1"
+remove "a/b/text1"
+remove "a/b/text0"
+rmrfdir "a/b/9/99/"
+rmdir "a/b/9/99/"
+rmrfdir "a/b/9/98/"
+rmrfdir "a/b/9/97/"
+rmrfdir "a/b/9/96/"
+rmrfdir "a/b/9/95/"
+rmrfdir "a/b/9/95/"
+rmrfdir "a/b/9/94/"
+rmdir "a/b/9/94/"
+rmdir "a/b/9/93/"
+rmdir "a/b/9/92/"
+rmdir "a/b/9/91/"
+rmdir "a/b/9/90/"
+rmdir "a/b/9/90/"
+rmrfdir "a/b/8/89/"
+rmdir "a/b/8/89/"
+rmrfdir "a/b/8/88/"
+rmrfdir "a/b/8/87/"
+rmrfdir "a/b/8/86/"
+rmrfdir "a/b/8/85/"
+rmrfdir "a/b/8/85/"
+rmrfdir "a/b/8/84/"
+rmdir "a/b/8/84/"
+rmdir "a/b/8/83/"
+rmdir "a/b/8/82/"
+rmdir "a/b/8/81/"
+rmdir "a/b/8/80/"
+rmdir "a/b/8/80/"
+rmrfdir "a/b/7/"
+rmdir "a/b/6/"
+remove "a/b/5/5text1"
+remove "a/b/5/5text0"
+rmrfdir "a/b/5/"
+remove "a/b/4/4text1"
+remove "a/b/4/4text0"
+remove "a/b/4/4exe0.exe"
+rmdir "a/b/4/"
+remove "a/b/3/3text1"
+remove "a/b/3/3text0"
+rmdir "a/b/1/10/"
+rmdir "a/b/1/"
diff --git a/toolkit/mozapps/update/tests/data/replace_log_success b/toolkit/mozapps/update/tests/data/replace_log_success
new file mode 100644
index 000000000..323f1db41
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/replace_log_success
@@ -0,0 +1,6 @@
+Performing a replace request
+rename_file: proceeding to rename the directory
+rename_file: proceeding to rename the directory
+Now, remove the tmpDir
+succeeded
+calling QuitProgressUI
diff --git a/toolkit/mozapps/update/tests/data/shared.js b/toolkit/mozapps/update/tests/data/shared.js
new file mode 100644
index 000000000..e9a10da06
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/shared.js
@@ -0,0 +1,632 @@
+/* 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/. */
+
+/* Shared code for xpcshell and mochitests-chrome */
+/* eslint-disable no-undef */
+
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const PREF_APP_UPDATE_AUTO = "app.update.auto";
+const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors";
+const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors";
+const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
+const PREF_APP_UPDATE_DOWNLOADBACKGROUNDINTERVAL = "app.update.download.backgroundInterval";
+const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
+const PREF_APP_UPDATE_IDLETIME = "app.update.idletime";
+const PREF_APP_UPDATE_LOG = "app.update.log";
+const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
+const PREF_APP_UPDATE_PROMPTWAITTIME = "app.update.promptWaitTime";
+const PREF_APP_UPDATE_RETRYTIMEOUT = "app.update.socket.retryTimeout";
+const PREF_APP_UPDATE_SERVICE_ENABLED = "app.update.service.enabled";
+const PREF_APP_UPDATE_SILENT = "app.update.silent";
+const PREF_APP_UPDATE_SOCKET_MAXERRORS = "app.update.socket.maxErrors";
+const PREF_APP_UPDATE_STAGING_ENABLED = "app.update.staging.enabled";
+const PREF_APP_UPDATE_URL = "app.update.url";
+const PREF_APP_UPDATE_URL_DETAILS = "app.update.url.details";
+
+const PREFBRANCH_APP_UPDATE_NEVER = "app.update.never.";
+
+const PREFBRANCH_APP_PARTNER = "app.partner.";
+const PREF_DISTRIBUTION_ID = "distribution.id";
+const PREF_DISTRIBUTION_VERSION = "distribution.version";
+const PREF_TOOLKIT_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
+
+const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
+const NS_APP_USER_PROFILE_50_DIR = "ProfD";
+const NS_GRE_DIR = "GreD";
+const NS_GRE_BIN_DIR = "GreBinD";
+const NS_XPCOM_CURRENT_PROCESS_DIR = "XCurProcD";
+const XRE_EXECUTABLE_FILE = "XREExeF";
+const XRE_UPDATE_ROOT_DIR = "UpdRootD";
+
+const DIR_PATCH = "0";
+const DIR_TOBEDELETED = "tobedeleted";
+const DIR_UPDATES = "updates";
+const DIR_UPDATED = IS_MACOSX ? "Updated.app" : "updated";
+
+const FILE_ACTIVE_UPDATE_XML = "active-update.xml";
+const FILE_APPLICATION_INI = "application.ini";
+const FILE_BACKUP_UPDATE_LOG = "backup-update.log";
+const FILE_LAST_UPDATE_LOG = "last-update.log";
+const FILE_UPDATE_SETTINGS_INI = "update-settings.ini";
+const FILE_UPDATE_SETTINGS_INI_BAK = "update-settings.ini.bak";
+const FILE_UPDATER_INI = "updater.ini";
+const FILE_UPDATES_XML = "updates.xml";
+const FILE_UPDATE_LOG = "update.log";
+const FILE_UPDATE_MAR = "update.mar";
+const FILE_UPDATE_STATUS = "update.status";
+const FILE_UPDATE_TEST = "update.test";
+const FILE_UPDATE_VERSION = "update.version";
+
+const UPDATE_SETTINGS_CONTENTS = "[Settings]\n" +
+ "ACCEPTED_MAR_CHANNEL_IDS=xpcshell-test\n";
+
+const PR_RDWR = 0x04;
+const PR_CREATE_FILE = 0x08;
+const PR_TRUNCATE = 0x20;
+
+const DEFAULT_UPDATE_VERSION = "999999.0";
+
+var gChannel;
+
+/* import-globals-from ../data/sharedUpdateXML.js */
+Services.scriptloader.loadSubScript(DATA_URI_SPEC + "sharedUpdateXML.js", this);
+
+const PERMS_FILE = FileUtils.PERMS_FILE;
+const PERMS_DIRECTORY = FileUtils.PERMS_DIRECTORY;
+
+const MODE_WRONLY = FileUtils.MODE_WRONLY;
+const MODE_CREATE = FileUtils.MODE_CREATE;
+const MODE_APPEND = FileUtils.MODE_APPEND;
+const MODE_TRUNCATE = FileUtils.MODE_TRUNCATE;
+
+const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.properties";
+const gUpdateBundle = Services.strings.createBundle(URI_UPDATES_PROPERTIES);
+
+XPCOMUtils.defineLazyGetter(this, "gAUS", function test_gAUS() {
+ return Cc["@mozilla.org/updates/update-service;1"].
+ getService(Ci.nsIApplicationUpdateService).
+ QueryInterface(Ci.nsITimerCallback).
+ QueryInterface(Ci.nsIObserver).
+ QueryInterface(Ci.nsIUpdateCheckListener);
+});
+
+XPCOMUtils.defineLazyServiceGetter(this, "gUpdateManager",
+ "@mozilla.org/updates/update-manager;1",
+ "nsIUpdateManager");
+
+XPCOMUtils.defineLazyGetter(this, "gUpdateChecker", function test_gUC() {
+ return Cc["@mozilla.org/updates/update-checker;1"].
+ createInstance(Ci.nsIUpdateChecker);
+});
+
+XPCOMUtils.defineLazyGetter(this, "gUP", function test_gUP() {
+ return Cc["@mozilla.org/updates/update-prompt;1"].
+ createInstance(Ci.nsIUpdatePrompt);
+});
+
+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);
+});
+
+/* Triggers post-update processing */
+function testPostUpdateProcessing() {
+ gAUS.observe(null, "test-post-update-processing", "");
+}
+
+/* Initializes the update service stub */
+function initUpdateServiceStub() {
+ Cc["@mozilla.org/updates/update-service-stub;1"].
+ createInstance(Ci.nsISupports);
+}
+
+/* Reloads the update metadata from disk */
+function reloadUpdateManagerData() {
+ gUpdateManager.QueryInterface(Ci.nsIObserver).
+ observe(null, "um-reload-update-data", "");
+}
+
+const observer = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic == "nsPref:changed" && aData == PREF_APP_UPDATE_CHANNEL) {
+ let channel = gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL);
+ if (channel != gChannel) {
+ debugDump("Changing channel from " + channel + " to " + gChannel);
+ gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, gChannel);
+ }
+ }
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
+};
+
+/**
+ * Sets the app.update.channel preference.
+ *
+ * @param aChannel
+ * The update channel.
+ */
+function setUpdateChannel(aChannel) {
+ gChannel = aChannel;
+ debugDump("setting default pref " + PREF_APP_UPDATE_CHANNEL + " to " + gChannel);
+ gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, gChannel);
+ gPrefRoot.addObserver(PREF_APP_UPDATE_CHANNEL, observer, false);
+}
+
+/**
+ * Sets the app.update.url default preference.
+ *
+ * @param aURL
+ * The update url. If not specified 'URL_HOST + "/update.xml"' will be
+ * used.
+ */
+function setUpdateURL(aURL) {
+ let url = aURL ? aURL : URL_HOST + "/update.xml";
+ debugDump("setting " + PREF_APP_UPDATE_URL + " to " + url);
+ gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_URL, url);
+}
+
+/**
+ * Returns either the active or regular update database XML file.
+ *
+ * @param isActiveUpdate
+ * If true this will return the active-update.xml otherwise it will
+ * return the updates.xml file.
+ */
+function getUpdatesXMLFile(aIsActiveUpdate) {
+ let file = getUpdatesRootDir();
+ file.append(aIsActiveUpdate ? FILE_ACTIVE_UPDATE_XML : FILE_UPDATES_XML);
+ return file;
+}
+
+/**
+ * Writes the updates specified to either the active-update.xml or the
+ * updates.xml.
+ *
+ * @param aContent
+ * The updates represented as a string to write to the XML file.
+ * @param isActiveUpdate
+ * If true this will write to the active-update.xml otherwise it will
+ * write to the updates.xml file.
+ */
+function writeUpdatesToXMLFile(aContent, aIsActiveUpdate) {
+ writeFile(getUpdatesXMLFile(aIsActiveUpdate), aContent);
+}
+
+/**
+ * Writes the current update operation/state to a file in the patch
+ * directory, indicating to the patching system that operations need
+ * to be performed.
+ *
+ * @param aStatus
+ * The status value to write.
+ */
+function writeStatusFile(aStatus) {
+ let file = getUpdatesPatchDir();
+ file.append(FILE_UPDATE_STATUS);
+ writeFile(file, aStatus + "\n");
+}
+
+/**
+ * Writes the current update version to a file in the patch directory,
+ * indicating to the patching system the version of the update.
+ *
+ * @param aVersion
+ * The version value to write.
+ */
+function writeVersionFile(aVersion) {
+ let file = getUpdatesPatchDir();
+ file.append(FILE_UPDATE_VERSION);
+ writeFile(file, aVersion + "\n");
+}
+
+/**
+ * Gets the root directory for the updates directory.
+ *
+ * @return nsIFile for the updates root directory.
+ */
+function getUpdatesRootDir() {
+ return Services.dirsvc.get(XRE_UPDATE_ROOT_DIR, Ci.nsIFile);
+}
+
+/**
+ * Gets the updates directory.
+ *
+ * @return nsIFile for the updates directory.
+ */
+function getUpdatesDir() {
+ let dir = getUpdatesRootDir();
+ dir.append(DIR_UPDATES);
+ return dir;
+}
+
+/**
+ * Gets the directory for update patches.
+ *
+ * @return nsIFile for the updates directory.
+ */
+function getUpdatesPatchDir() {
+ let dir = getUpdatesDir();
+ dir.append(DIR_PATCH);
+ return dir;
+}
+
+/**
+ * Writes text to a file. This will replace existing text if the file exists
+ * and create the file if it doesn't exist.
+ *
+ * @param aFile
+ * The file to write to. Will be created if it doesn't exist.
+ * @param aText
+ * The text to write to the file. If there is existing text it will be
+ * replaced.
+ */
+function writeFile(aFile, aText) {
+ let fos = Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(Ci.nsIFileOutputStream);
+ if (!aFile.exists()) {
+ aFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
+ }
+ fos.init(aFile, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE, 0);
+ fos.write(aText, aText.length);
+ fos.close();
+}
+
+/**
+ * Reads the current update operation/state in the status file in the patch
+ * directory including the error code if it is present.
+ *
+ * @return The status value.
+ */
+function readStatusFile() {
+ let file = getUpdatesPatchDir();
+ file.append(FILE_UPDATE_STATUS);
+
+ if (!file.exists()) {
+ debugDump("update status file does not exists! Path: " + file.path);
+ return STATE_NONE;
+ }
+
+ return readFile(file).split("\n")[0];
+}
+
+/**
+ * Reads the current update operation/state in the status file in the patch
+ * directory without the error code if it is present.
+ *
+ * @return The state value.
+ */
+function readStatusState() {
+ return readStatusFile().split(": ")[0];
+}
+
+/**
+ * Reads the current update operation/state in the status file in the patch
+ * directory with the error code.
+ *
+ * @return The state value.
+ */
+function readStatusFailedCode() {
+ return readStatusFile().split(": ")[1];
+}
+
+/**
+ * Reads text from a file and returns the string.
+ *
+ * @param aFile
+ * The file to read from.
+ * @return The string of text read from the file.
+ */
+function readFile(aFile) {
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ if (!aFile.exists()) {
+ return null;
+ }
+ // Specifying -1 for ioFlags will open the file with the default of PR_RDONLY.
+ // Specifying -1 for perm will open the file with the default of 0.
+ fis.init(aFile, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF);
+ let sis = Cc["@mozilla.org/scriptableinputstream;1"].
+ createInstance(Ci.nsIScriptableInputStream);
+ sis.init(fis);
+ let text = sis.read(sis.available());
+ sis.close();
+ return text;
+}
+
+/**
+ * Reads the binary contents of a file and returns it as a string.
+ *
+ * @param aFile
+ * The file to read from.
+ * @return The contents of the file as a string.
+ */
+function readFileBytes(aFile) {
+ debugDump("attempting to read file, path: " + aFile.path);
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ // Specifying -1 for ioFlags will open the file with the default of PR_RDONLY.
+ // Specifying -1 for perm will open the file with the default of 0.
+ fis.init(aFile, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF);
+ let bis = Cc["@mozilla.org/binaryinputstream;1"].
+ createInstance(Ci.nsIBinaryInputStream);
+ bis.setInputStream(fis);
+ let data = [];
+ let count = fis.available();
+ while (count > 0) {
+ let bytes = bis.readByteArray(Math.min(65535, count));
+ data.push(String.fromCharCode.apply(null, bytes));
+ count -= bytes.length;
+ if (bytes.length == 0) {
+ throw "Nothing read from input stream!";
+ }
+ }
+ data.join('');
+ fis.close();
+ return data.toString();
+}
+
+/* Returns human readable status text from the updates.properties bundle */
+function getStatusText(aErrCode) {
+ return getString("check_error-" + aErrCode);
+}
+
+/* Returns a string from the updates.properties bundle */
+function getString(aName) {
+ try {
+ return gUpdateBundle.GetStringFromName(aName);
+ } catch (e) {
+ }
+ return null;
+}
+
+/**
+ * Gets the file extension for an nsIFile.
+ *
+ * @param aFile
+ * The file to get the file extension for.
+ * @return The file extension.
+ */
+function getFileExtension(aFile) {
+ return Services.io.newFileURI(aFile).QueryInterface(Ci.nsIURL).
+ fileExtension;
+}
+
+/**
+ * Removes the updates.xml file, active-update.xml file, and all files and
+ * sub-directories in the updates directory except for the "0" sub-directory.
+ * This prevents some tests from failing due to files being left behind when the
+ * tests are interrupted.
+ */
+function removeUpdateDirsAndFiles() {
+ let file = getUpdatesXMLFile(true);
+ try {
+ if (file.exists()) {
+ file.remove(false);
+ }
+ } catch (e) {
+ logTestInfo("Unable to remove file. Path: " + file.path +
+ ", Exception: " + e);
+ }
+
+ file = getUpdatesXMLFile(false);
+ try {
+ if (file.exists()) {
+ file.remove(false);
+ }
+ } catch (e) {
+ logTestInfo("Unable to remove file. Path: " + file.path +
+ ", Exception: " + e);
+ }
+
+ // This fails sporadically on Mac OS X so wrap it in a try catch
+ let updatesDir = getUpdatesDir();
+ try {
+ cleanUpdatesDir(updatesDir);
+ } catch (e) {
+ logTestInfo("Unable to remove files / directories from directory. Path: " +
+ updatesDir.path + ", Exception: " + e);
+ }
+}
+
+/**
+ * Removes all files and sub-directories in the updates directory except for
+ * the "0" sub-directory.
+ *
+ * @param aDir
+ * nsIFile for the directory to be deleted.
+ */
+function cleanUpdatesDir(aDir) {
+ if (!aDir.exists()) {
+ return;
+ }
+
+ let dirEntries = aDir.directoryEntries;
+ while (dirEntries.hasMoreElements()) {
+ let entry = dirEntries.getNext().QueryInterface(Ci.nsIFile);
+
+ if (entry.isDirectory()) {
+ if (entry.leafName == DIR_PATCH && entry.parent.leafName == DIR_UPDATES) {
+ cleanUpdatesDir(entry);
+ entry.permissions = PERMS_DIRECTORY;
+ } else {
+ try {
+ entry.remove(true);
+ return;
+ } catch (e) {
+ }
+ cleanUpdatesDir(entry);
+ entry.permissions = PERMS_DIRECTORY;
+ try {
+ entry.remove(true);
+ } catch (e) {
+ logTestInfo("cleanUpdatesDir: unable to remove directory. Path: " +
+ entry.path + ", Exception: " + e);
+ throw (e);
+ }
+ }
+ } else {
+ entry.permissions = PERMS_FILE;
+ try {
+ entry.remove(false);
+ } catch (e) {
+ logTestInfo("cleanUpdatesDir: unable to remove file. Path: " +
+ entry.path + ", Exception: " + e);
+ throw (e);
+ }
+ }
+ }
+}
+
+/**
+ * Deletes a directory and its children. First it tries nsIFile::Remove(true).
+ * If that fails it will fall back to recursing, setting the appropriate
+ * permissions, and deleting the current entry.
+ *
+ * @param aDir
+ * nsIFile for the directory to be deleted.
+ */
+function removeDirRecursive(aDir) {
+ if (!aDir.exists()) {
+ return;
+ }
+
+ try {
+ debugDump("attempting to remove directory. Path: " + aDir.path);
+ aDir.remove(true);
+ return;
+ } catch (e) {
+ logTestInfo("non-fatal error removing directory. Exception: " + e);
+ }
+
+ let dirEntries = aDir.directoryEntries;
+ while (dirEntries.hasMoreElements()) {
+ let entry = dirEntries.getNext().QueryInterface(Ci.nsIFile);
+
+ if (entry.isDirectory()) {
+ removeDirRecursive(entry);
+ } else {
+ entry.permissions = PERMS_FILE;
+ try {
+ debugDump("attempting to remove file. Path: " + entry.path);
+ entry.remove(false);
+ } catch (e) {
+ logTestInfo("error removing file. Exception: " + e);
+ throw (e);
+ }
+ }
+ }
+
+ aDir.permissions = PERMS_DIRECTORY;
+ try {
+ debugDump("attempting to remove directory. Path: " + aDir.path);
+ aDir.remove(true);
+ } catch (e) {
+ logTestInfo("error removing directory. Exception: " + e);
+ throw (e);
+ }
+}
+
+/**
+ * Returns the directory for the currently running process. This is used to
+ * clean up after the tests and to locate the active-update.xml and updates.xml
+ * files.
+ *
+ * @return nsIFile for the current process directory.
+ */
+function getCurrentProcessDir() {
+ return Services.dirsvc.get(NS_XPCOM_CURRENT_PROCESS_DIR, Ci.nsIFile);
+}
+
+/**
+ * Gets the application base directory.
+ *
+ * @return nsIFile object for the application base directory.
+ */
+function getAppBaseDir() {
+ return Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile).parent;
+}
+
+/**
+ * Returns the Gecko Runtime Engine directory where files other than executable
+ * binaries are located. On Mac OS X this will be <bundle>/Contents/Resources/
+ * and the installation directory on all other platforms.
+ *
+ * @return nsIFile for the Gecko Runtime Engine directory.
+ */
+function getGREDir() {
+ return Services.dirsvc.get(NS_GRE_DIR, Ci.nsIFile);
+}
+
+/**
+ * Returns the Gecko Runtime Engine Binary directory where the executable
+ * binaries are located such as the updater binary (Windows and Linux) or
+ * updater package (Mac OS X). On Mac OS X this will be
+ * <bundle>/Contents/MacOS/ and the installation directory on all other
+ * platforms.
+ *
+ * @return nsIFile for the Gecko Runtime Engine Binary directory.
+ */
+function getGREBinDir() {
+ return Services.dirsvc.get(NS_GRE_BIN_DIR, Ci.nsIFile);
+}
+
+/**
+ * Logs TEST-INFO messages.
+ *
+ * @param aText
+ * The text to log.
+ * @param aCaller (optional)
+ * An optional Components.stack.caller. If not specified
+ * Components.stack.caller will be used.
+ */
+function logTestInfo(aText, aCaller) {
+ let caller = aCaller ? aCaller : Components.stack.caller;
+ let now = new Date();
+ let hh = now.getHours();
+ let mm = now.getMinutes();
+ let ss = now.getSeconds();
+ let ms = now.getMilliseconds();
+ let time = (hh < 10 ? "0" + hh : hh) + ":" +
+ (mm < 10 ? "0" + mm : mm) + ":" +
+ (ss < 10 ? "0" + ss : ss) + ":";
+ if (ms < 10) {
+ time += "00";
+ } else if (ms < 100) {
+ time += "0";
+ }
+ time += ms;
+ let msg = time + " | TEST-INFO | " + caller.filename + " | [" + caller.name +
+ " : " + caller.lineNumber + "] " + aText;
+ LOG_FUNCTION(msg);
+}
+
+/**
+ * Logs TEST-INFO messages when DEBUG_AUS_TEST evaluates to true.
+ *
+ * @param aText
+ * The text to log.
+ * @param aCaller (optional)
+ * An optional Components.stack.caller. If not specified
+ * Components.stack.caller will be used.
+ */
+function debugDump(aText, aCaller) {
+ if (DEBUG_AUS_TEST) {
+ let caller = aCaller ? aCaller : Components.stack.caller;
+ logTestInfo(aText, caller);
+ }
+}
diff --git a/toolkit/mozapps/update/tests/data/sharedUpdateXML.js b/toolkit/mozapps/update/tests/data/sharedUpdateXML.js
new file mode 100644
index 000000000..3aa01eff4
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/sharedUpdateXML.js
@@ -0,0 +1,364 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Helper functions for creating xml strings used by application update tests.
+ *
+ * !IMPORTANT - This file contains everything needed (along with dependencies)
+ * by the updates.sjs file used by the mochitest-chrome tests. Since xpcshell
+ * used by the http server is launched with -v 170 this file must not use
+ * features greater than JavaScript 1.7.
+ */
+
+/* eslint-disable no-undef */
+
+const FILE_SIMPLE_MAR = "simple.mar";
+const SIZE_SIMPLE_MAR = "1031";
+const MD5_HASH_SIMPLE_MAR = "1f8c038577bb6845d94ccec4999113ee";
+const SHA1_HASH_SIMPLE_MAR = "5d49a672c87f10f31d7e326349564a11272a028b";
+const SHA256_HASH_SIMPLE_MAR = "1aabbed5b1dd6e16e139afc5b43d479e254e0c26" +
+ "3c8fb9249c0a1bb93071c5fb";
+const SHA384_HASH_SIMPLE_MAR = "26615014ea034af32ef5651492d5f493f5a7a1a48522e" +
+ "d24c366442a5ec21d5ef02e23fb58d79729b8ca2f9541" +
+ "99dd53";
+const SHA512_HASH_SIMPLE_MAR = "922e5ae22081795f6e8d65a3c508715c9a314054179a8" +
+ "bbfe5f50dc23919ad89888291bc0a07586ab17dd0304a" +
+ "b5347473601127571c66f61f5080348e05c36b";
+
+const STATE_NONE = "null";
+const STATE_DOWNLOADING = "downloading";
+const STATE_PENDING = "pending";
+const STATE_PENDING_SVC = "pending-service";
+const STATE_APPLYING = "applying";
+const STATE_APPLIED = "applied";
+const STATE_APPLIED_SVC = "applied-service";
+const STATE_SUCCEEDED = "succeeded";
+const STATE_DOWNLOAD_FAILED = "download-failed";
+const STATE_FAILED = "failed";
+
+const LOADSOURCE_ERROR_WRONG_SIZE = 2;
+const CRC_ERROR = 4;
+const READ_ERROR = 6;
+const WRITE_ERROR = 7;
+const MAR_CHANNEL_MISMATCH_ERROR = 22;
+const VERSION_DOWNGRADE_ERROR = 23;
+const SERVICE_COULD_NOT_COPY_UPDATER = 49;
+const SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR = 52;
+const SERVICE_INVALID_APPLYTO_DIR_ERROR = 54;
+const SERVICE_INVALID_INSTALL_DIR_PATH_ERROR = 55;
+const SERVICE_INVALID_WORKING_DIR_PATH_ERROR = 56;
+const INVALID_APPLYTO_DIR_STAGED_ERROR = 72;
+const INVALID_APPLYTO_DIR_ERROR = 74;
+const INVALID_INSTALL_DIR_PATH_ERROR = 75;
+const INVALID_WORKING_DIR_PATH_ERROR = 76;
+const INVALID_CALLBACK_PATH_ERROR = 77;
+const INVALID_CALLBACK_DIR_ERROR = 78;
+
+const STATE_FAILED_DELIMETER = ": ";
+
+const STATE_FAILED_LOADSOURCE_ERROR_WRONG_SIZE =
+ STATE_FAILED + STATE_FAILED_DELIMETER + LOADSOURCE_ERROR_WRONG_SIZE;
+const STATE_FAILED_CRC_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + CRC_ERROR;
+const STATE_FAILED_READ_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + READ_ERROR;
+const STATE_FAILED_WRITE_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + WRITE_ERROR;
+const STATE_FAILED_MAR_CHANNEL_MISMATCH_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + MAR_CHANNEL_MISMATCH_ERROR;
+const STATE_FAILED_VERSION_DOWNGRADE_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + VERSION_DOWNGRADE_ERROR;
+const STATE_FAILED_SERVICE_COULD_NOT_COPY_UPDATER =
+ STATE_FAILED + STATE_FAILED_DELIMETER + SERVICE_COULD_NOT_COPY_UPDATER
+const STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR;
+const STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + SERVICE_INVALID_APPLYTO_DIR_ERROR;
+const STATE_FAILED_SERVICE_INVALID_INSTALL_DIR_PATH_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + SERVICE_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_FAILED_SERVICE_INVALID_WORKING_DIR_PATH_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + SERVICE_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_FAILED_INVALID_APPLYTO_DIR_STAGED_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_APPLYTO_DIR_STAGED_ERROR;
+const STATE_FAILED_INVALID_APPLYTO_DIR_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_APPLYTO_DIR_ERROR;
+const STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_FAILED_INVALID_CALLBACK_PATH_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_CALLBACK_PATH_ERROR;
+const STATE_FAILED_INVALID_CALLBACK_DIR_ERROR =
+ STATE_FAILED + STATE_FAILED_DELIMETER + INVALID_CALLBACK_DIR_ERROR;
+
+/**
+ * Constructs a string representing a remote update xml file.
+ *
+ * @param aUpdates
+ * The string representing the update elements.
+ * @return The string representing a remote update xml file.
+ */
+function getRemoteUpdatesXMLString(aUpdates) {
+ return "<?xml version=\"1.0\"?>\n" +
+ "<updates>\n" +
+ aUpdates +
+ "</updates>\n";
+}
+
+/**
+ * Constructs a string representing an update element for a remote update xml
+ * file. See getUpdateString for parameter information not provided below.
+ *
+ * @param aPatches
+ * String representing the application update patches.
+ * @return The string representing an update element for an update xml file.
+ */
+function getRemoteUpdateString(aPatches, aType, aName, aDisplayVersion,
+ aAppVersion, aBuildID, aDetailsURL, aShowPrompt,
+ aShowNeverForVersion, aPromptWaitTime,
+ aBackgroundInterval, aCustom1, aCustom2) {
+ return getUpdateString(aType, aName, aDisplayVersion, aAppVersion,
+ aBuildID, aDetailsURL, aShowPrompt,
+ aShowNeverForVersion, aPromptWaitTime,
+ aBackgroundInterval, aCustom1, aCustom2) + ">\n" +
+ aPatches +
+ " </update>\n";
+}
+
+/**
+ * Constructs a string representing a patch element for a remote update xml
+ * file. See getPatchString for parameter information not provided below.
+ *
+ * @return The string representing a patch element for a remote update xml file.
+ */
+function getRemotePatchString(aType, aURL, aHashFunction, aHashValue, aSize) {
+ return getPatchString(aType, aURL, aHashFunction, aHashValue, aSize) +
+ "/>\n";
+}
+
+/**
+ * Constructs a string representing a local update xml file.
+ *
+ * @param aUpdates
+ * The string representing the update elements.
+ * @return The string representing a local update xml file.
+ */
+function getLocalUpdatesXMLString(aUpdates) {
+ if (!aUpdates || aUpdates == "") {
+ return "<updates xmlns=\"http://www.mozilla.org/2005/app-update\"/>";
+ }
+ return ("<updates xmlns=\"http://www.mozilla.org/2005/app-update\">" +
+ aUpdates +
+ "</updates>").replace(/>\s+\n*</g, '><');
+}
+
+/**
+ * Constructs a string representing an update element for a local update xml
+ * file. See getUpdateString for parameter information not provided below.
+ *
+ * @param aPatches
+ * String representing the application update patches.
+ * @param aServiceURL (optional)
+ * The update's xml url.
+ * If not specified it will default to 'http://test_service/'.
+ * @param aIsCompleteUpdate (optional)
+ * The string 'true' if this update was a complete update or the string
+ * 'false' if this update was a partial update.
+ * If not specified it will default to 'true'.
+ * @param aChannel (optional)
+ * The update channel name.
+ * If not specified it will default to the default preference value of
+ * app.update.channel.
+ * @param aForegroundDownload (optional)
+ * The string 'true' if this update was manually downloaded or the
+ * string 'false' if this update was automatically downloaded.
+ * If not specified it will default to 'true'.
+ * @param aPreviousAppVersion (optional)
+ * The application version prior to applying the update.
+ * If not specified it will not be present.
+ * @return The string representing an update element for an update xml file.
+ */
+function getLocalUpdateString(aPatches, aType, aName, aDisplayVersion,
+ aAppVersion, aBuildID, aDetailsURL, aServiceURL,
+ aInstallDate, aStatusText, aIsCompleteUpdate,
+ aChannel, aForegroundDownload, aShowPrompt,
+ aShowNeverForVersion, aPromptWaitTime,
+ aBackgroundInterval, aPreviousAppVersion,
+ aCustom1, aCustom2) {
+ let serviceURL = aServiceURL ? aServiceURL : "http://test_service/";
+ let installDate = aInstallDate ? aInstallDate : "1238441400314";
+ let statusText = aStatusText ? aStatusText : "Install Pending";
+ let isCompleteUpdate =
+ typeof aIsCompleteUpdate == "string" ? aIsCompleteUpdate : "true";
+ let channel = aChannel ? aChannel
+ : gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL);
+ let foregroundDownload =
+ typeof aForegroundDownload == "string" ? aForegroundDownload : "true";
+ let previousAppVersion = aPreviousAppVersion ? "previousAppVersion=\"" +
+ aPreviousAppVersion + "\" "
+ : "";
+ return getUpdateString(aType, aName, aDisplayVersion, aAppVersion, aBuildID,
+ aDetailsURL, aShowPrompt, aShowNeverForVersion,
+ aPromptWaitTime, aBackgroundInterval, aCustom1, aCustom2) +
+ " " +
+ previousAppVersion +
+ "serviceURL=\"" + serviceURL + "\" " +
+ "installDate=\"" + installDate + "\" " +
+ "statusText=\"" + statusText + "\" " +
+ "isCompleteUpdate=\"" + isCompleteUpdate + "\" " +
+ "channel=\"" + channel + "\" " +
+ "foregroundDownload=\"" + foregroundDownload + "\">" +
+ aPatches +
+ " </update>";
+}
+
+/**
+ * Constructs a string representing a patch element for a local update xml file.
+ * See getPatchString for parameter information not provided below.
+ *
+ * @param aSelected (optional)
+ * Whether this patch is selected represented or not. The string 'true'
+ * denotes selected and the string 'false' denotes not selected.
+ * If not specified it will default to the string 'true'.
+ * @param aState (optional)
+ * The patch's state.
+ * If not specified it will default to STATE_SUCCEEDED.
+ * @return The string representing a patch element for a local update xml file.
+ */
+function getLocalPatchString(aType, aURL, aHashFunction, aHashValue, aSize,
+ aSelected, aState) {
+ let selected = typeof aSelected == "string" ? aSelected : "true";
+ let state = aState ? aState : STATE_SUCCEEDED;
+ return getPatchString(aType, aURL, aHashFunction, aHashValue, aSize) + " " +
+ "selected=\"" + selected + "\" " +
+ "state=\"" + state + "\"/>\n";
+}
+
+/**
+ * Constructs a string representing an update element for a remote update xml
+ * file.
+ *
+ * @param aType (optional)
+ * The update's type which should be major or minor. If not specified it
+ * will default to 'major'.
+ * @param aName (optional)
+ * The update's name.
+ * If not specified it will default to 'App Update Test'.
+ * @param aDisplayVersion (optional)
+ * The update's display version.
+ * If not specified it will default to 'version #' where # is the value
+ * of DEFAULT_UPDATE_VERSION.
+ * @param aAppVersion (optional)
+ * The update's application version.
+ * If not specified it will default to the value of
+ * DEFAULT_UPDATE_VERSION.
+ * @param aBuildID (optional)
+ * The update's build id.
+ * If not specified it will default to '20080811053724'.
+ * @param aDetailsURL (optional)
+ * The update's details url.
+ * If not specified it will default to 'http://test_details/' due to due
+ * to bug 470244.
+ * @param aShowPrompt (optional)
+ * Whether to show the prompt for the update when auto update is
+ * enabled.
+ * If not specified it will not be present and the update service will
+ * default to false.
+ * @param aShowNeverForVersion (optional)
+ * Whether to show the 'No Thanks' button in the update prompt.
+ * If not specified it will not be present and the update service will
+ * default to false.
+ * @param aPromptWaitTime (optional)
+ * Override for the app.update.promptWaitTime preference.
+ * @param aBackgroundInterval (optional)
+ * Override for the app.update.download.backgroundInterval preference.
+ * @param aCustom1 (optional)
+ * A custom attribute name and attribute value to add to the xml.
+ * Example: custom1_attribute="custom1 value"
+ * If not specified it will not be present.
+ * @param aCustom2 (optional)
+ * A custom attribute name and attribute value to add to the xml.
+ * Example: custom2_attribute="custom2 value"
+ * If not specified it will not be present.
+ * @return The string representing an update element for an update xml file.
+ */
+function getUpdateString(aType, aName, aDisplayVersion, aAppVersion, aBuildID,
+ aDetailsURL, aShowPrompt, aShowNeverForVersion,
+ aPromptWaitTime, aBackgroundInterval, aCustom1,
+ aCustom2) {
+ let type = aType ? aType : "major";
+ let name = aName ? aName : "App Update Test";
+ let displayVersion = aDisplayVersion ? "displayVersion=\"" +
+ aDisplayVersion + "\" "
+ : "";
+ let appVersion = "appVersion=\"" +
+ (aAppVersion ? aAppVersion : DEFAULT_UPDATE_VERSION) +
+ "\" ";
+ let buildID = aBuildID ? aBuildID : "20080811053724";
+ // XXXrstrong - not specifying a detailsURL will cause a leak due to bug 470244
+// let detailsURL = aDetailsURL ? "detailsURL=\"" + aDetailsURL + "\" " : "";
+ let detailsURL = "detailsURL=\"" +
+ (aDetailsURL ? aDetailsURL
+ : "http://test_details/") + "\" ";
+ let showPrompt = aShowPrompt ? "showPrompt=\"" + aShowPrompt + "\" " : "";
+ let showNeverForVersion = aShowNeverForVersion ? "showNeverForVersion=\"" +
+ aShowNeverForVersion + "\" "
+ : "";
+ let promptWaitTime = aPromptWaitTime ? "promptWaitTime=\"" + aPromptWaitTime +
+ "\" "
+ : "";
+ let backgroundInterval = aBackgroundInterval ? "backgroundInterval=\"" +
+ aBackgroundInterval + "\" "
+ : "";
+ let custom1 = aCustom1 ? aCustom1 + " " : "";
+ let custom2 = aCustom2 ? aCustom2 + " " : "";
+ return " <update type=\"" + type + "\" " +
+ "name=\"" + name + "\" " +
+ displayVersion +
+ appVersion +
+ detailsURL +
+ showPrompt +
+ showNeverForVersion +
+ promptWaitTime +
+ backgroundInterval +
+ custom1 +
+ custom2 +
+ "buildID=\"" + buildID + "\"";
+}
+
+/**
+ * Constructs a string representing a patch element for an update xml file.
+ *
+ * @param aType (optional)
+ * The patch's type which should be complete or partial.
+ * If not specified it will default to 'complete'.
+ * @param aURL (optional)
+ * The patch's url to the mar file.
+ * If not specified it will default to the value of:
+ * gURLData + FILE_SIMPLE_MAR
+ * @param aHashFunction (optional)
+ * The patch's hash function used to verify the mar file.
+ * If not specified it will default to 'MD5'.
+ * @param aHashValue (optional)
+ * The patch's hash value used to verify the mar file.
+ * If not specified it will default to the value of MD5_HASH_SIMPLE_MAR
+ * which is the MD5 hash value for the file specified by FILE_SIMPLE_MAR.
+ * @param aSize (optional)
+ * The patch's file size for the mar file.
+ * If not specified it will default to the file size for FILE_SIMPLE_MAR
+ * specified by SIZE_SIMPLE_MAR.
+ * @return The string representing a patch element for an update xml file.
+ */
+function getPatchString(aType, aURL, aHashFunction, aHashValue, aSize) {
+ let type = aType ? aType : "complete";
+ let url = aURL ? aURL : gURLData + FILE_SIMPLE_MAR;
+ let hashFunction = aHashFunction ? aHashFunction : "MD5";
+ let hashValue = aHashValue ? aHashValue : MD5_HASH_SIMPLE_MAR;
+ let size = aSize ? aSize : SIZE_SIMPLE_MAR;
+ return " <patch type=\"" + type + "\" " +
+ "URL=\"" + url + "\" " +
+ "hashFunction=\"" + hashFunction + "\" " +
+ "hashValue=\"" + hashValue + "\" " +
+ "size=\"" + size + "\"";
+}
diff --git a/toolkit/mozapps/update/tests/data/simple.mar b/toolkit/mozapps/update/tests/data/simple.mar
new file mode 100644
index 000000000..b2ccbd8d2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/simple.mar
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/wrong_product_channel.mar b/toolkit/mozapps/update/tests/data/wrong_product_channel.mar
new file mode 100644
index 000000000..1e39cc214
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/wrong_product_channel.mar
Binary files differ
diff --git a/toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js b/toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js
new file mode 100644
index 000000000..e5f0fce58
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js
@@ -0,0 +1,53 @@
+/* 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/. */
+
+/* Preprocessed constants used by xpcshell tests */
+
+const INSTALL_LOCALE = "@AB_CD@";
+const MOZ_APP_NAME = "@MOZ_APP_NAME@";
+const BIN_SUFFIX = "@BIN_SUFFIX@";
+
+// MOZ_APP_VENDOR is optional.
+#ifdef MOZ_APP_VENDOR
+const MOZ_APP_VENDOR = "@MOZ_APP_VENDOR@";
+#else
+const MOZ_APP_VENDOR = "";
+#endif
+
+// MOZ_APP_BASENAME is not optional for tests.
+const MOZ_APP_BASENAME = "@MOZ_APP_BASENAME@";
+const APP_BIN_SUFFIX = "@BIN_SUFFIX@";
+
+const APP_INFO_NAME = "XPCShell";
+const APP_INFO_VENDOR = "Mozilla";
+
+#ifdef XP_WIN
+const IS_WIN = true;
+#else
+const IS_WIN = false;
+#endif
+
+#ifdef XP_MACOSX
+const IS_MACOSX = true;
+#else
+const IS_MACOSX = false;
+#endif
+
+#ifdef XP_UNIX
+const IS_UNIX = true;
+#else
+const IS_UNIX = false;
+#endif
+
+#ifdef MOZ_VERIFY_MAR_SIGNATURE
+const MOZ_VERIFY_MAR_SIGNATURE = true;
+#else
+const MOZ_VERIFY_MAR_SIGNATURE = false;
+#endif
+
+#ifdef DISABLE_UPDATER_AUTHENTICODE_CHECK
+ const IS_AUTHENTICODE_CHECK_ENABLED = false;
+#else
+ const IS_AUTHENTICODE_CHECK_ENABLED = true;
+#endif
diff --git a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
new file mode 100644
index 000000000..ada08f0ae
--- /dev/null
+++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
@@ -0,0 +1,4047 @@
+/* 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/. */
+
+/**
+ * Test log warnings that happen before the test has started
+ * "Couldn't get the user appdata directory. Crash events may not be produced."
+ * in nsExceptionHandler.cpp (possibly bug 619104)
+ *
+ * Test log warnings that happen after the test has finished
+ * "OOPDeinit() without successful OOPInit()" in nsExceptionHandler.cpp
+ * (bug 619104)
+ * "XPCOM objects created/destroyed from static ctor/dtor" in nsTraceRefcnt.cpp
+ * (possibly bug 457479)
+ *
+ * Other warnings printed to the test logs
+ * "site security information will not be persisted" in
+ * nsSiteSecurityService.cpp and the error in nsSystemInfo.cpp preceding this
+ * error are due to not having a profile when running some of the xpcshell
+ * tests. Since most xpcshell tests also log these errors these tests don't
+ * call do_get_profile unless necessary for the test.
+ * The "This method is lossy. Use GetCanonicalPath !" warning on Windows in
+ * nsLocalFileWin.cpp is from the call to GetNSSProfilePath in
+ * nsNSSComponent.cpp due to it using GetNativeCanonicalPath.
+ * "!mMainThread" in nsThreadManager.cpp are due to using timers and it might be
+ * possible to fix some or all of these in the test itself.
+ * "NS_FAILED(rv)" in nsThreadUtils.cpp are due to using timers and it might be
+ * possible to fix some or all of these in the test itself.
+ */
+
+'use strict';
+/* eslint-disable no-undef */
+
+const { classes: Cc, interfaces: Ci, manager: Cm, results: Cr,
+ utils: Cu } = Components;
+
+/* global INSTALL_LOCALE, MOZ_APP_NAME, BIN_SUFFIX, MOZ_APP_VENDOR */
+/* global MOZ_APP_BASENAME, APP_BIN_SUFFIX, APP_INFO_NAME, APP_INFO_VENDOR */
+/* global IS_WIN, IS_MACOSX, IS_UNIX, MOZ_VERIFY_MAR_SIGNATURE */
+/* global IS_AUTHENTICODE_CHECK_ENABLED */
+load("../data/xpcshellConstantsPP.js");
+
+function getLogSuffix() {
+ if (IS_WIN) {
+ return "_win";
+ }
+ if (IS_MACOSX) {
+ return "_mac";
+ }
+ return "_linux";
+}
+
+Cu.import("resource://gre/modules/Services.jsm", this);
+Cu.import("resource://gre/modules/ctypes.jsm", this);
+
+const DIR_MACOS = IS_MACOSX ? "Contents/MacOS/" : "";
+const DIR_RESOURCES = IS_MACOSX ? "Contents/Resources/" : "";
+const TEST_FILE_SUFFIX = IS_MACOSX ? "_mac" : "";
+const FILE_COMPLETE_MAR = "complete" + TEST_FILE_SUFFIX + ".mar";
+const FILE_PARTIAL_MAR = "partial" + TEST_FILE_SUFFIX + ".mar";
+const FILE_COMPLETE_PRECOMPLETE = "complete_precomplete" + TEST_FILE_SUFFIX;
+const FILE_PARTIAL_PRECOMPLETE = "partial_precomplete" + TEST_FILE_SUFFIX;
+const FILE_COMPLETE_REMOVEDFILES = "complete_removed-files" + TEST_FILE_SUFFIX;
+const FILE_PARTIAL_REMOVEDFILES = "partial_removed-files" + TEST_FILE_SUFFIX;
+const FILE_UPDATE_IN_PROGRESS_LOCK = "updated.update_in_progress.lock";
+const COMPARE_LOG_SUFFIX = getLogSuffix();
+const LOG_COMPLETE_SUCCESS = "complete_log_success" + COMPARE_LOG_SUFFIX;
+const LOG_PARTIAL_SUCCESS = "partial_log_success" + COMPARE_LOG_SUFFIX;
+const LOG_PARTIAL_FAILURE = "partial_log_failure" + COMPARE_LOG_SUFFIX;
+const LOG_REPLACE_SUCCESS = "replace_log_success";
+
+const USE_EXECV = IS_UNIX && !IS_MACOSX;
+
+const URL_HOST = "http://localhost";
+
+const FILE_APP_BIN = MOZ_APP_NAME + APP_BIN_SUFFIX;
+const FILE_COMPLETE_EXE = "complete.exe";
+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_UPDATER_BIN = "updater" + BIN_SUFFIX;
+const FILE_WRONG_CHANNEL_MAR = "wrong_product_channel.mar";
+
+const PERFORMING_STAGED_UPDATE = "Performing a staged update";
+const CALL_QUIT = "calling QuitProgressUI";
+const REMOVE_OLD_DIST_DIR = "removing old distribution directory";
+const MOVE_OLD_DIST_DIR = "Moving old distribution directory to new location";
+const ERR_UPDATE_IN_PROGRESS = "Update already in progress! Exiting";
+const ERR_RENAME_FILE = "rename_file: failed to rename file";
+const ERR_ENSURE_COPY = "ensure_copy: failed to copy the file";
+const ERR_UNABLE_OPEN_DEST = "unable to open destination file";
+const ERR_BACKUP_DISCARD = "backup_discard: unable to remove";
+const ERR_MOVE_DESTDIR_7 = "Moving destDir to tmpDir failed, err: 7";
+const ERR_BACKUP_CREATE_7 = "backup_create failed: 7";
+const ERR_LOADSOURCEFILE_FAILED = "LoadSourceFile failed";
+
+const LOG_SVC_SUCCESSFUL_LAUNCH = "Process was started... waiting on result.";
+
+// Typical end of a message when calling assert
+const MSG_SHOULD_EQUAL = " should equal the expected value";
+const MSG_SHOULD_EXIST = "the file or directory should exist";
+const MSG_SHOULD_NOT_EXIST = "the file or directory should not exist";
+
+// All we care about is that the last modified time has changed so that Mac OS
+// X Launch Services invalidates its cache so the test allows up to one minute
+// difference in the last modified time.
+const MAC_MAX_TIME_DIFFERENCE = 60000;
+
+// How many of do_execute_soon calls to wait before the test is aborted.
+const MAX_TIMEOUT_RUNS = 20000;
+
+// Time in seconds the helper application should sleep before exiting. The
+// helper can also be made to exit by writing |finish| to its input file.
+const HELPER_SLEEP_TIMEOUT = 180;
+
+// Maximum number of milliseconds the process that is launched can run before
+// the test will try to kill it.
+const APP_TIMER_TIMEOUT = 120000;
+
+// How many of do_timeout calls using FILE_IN_USE_TIMEOUT_MS to wait before the
+// test is aborted.
+const FILE_IN_USE_MAX_TIMEOUT_RUNS = 60;
+const FILE_IN_USE_TIMEOUT_MS = 1000;
+
+const PIPE_TO_NULL = IS_WIN ? ">nul" : "> /dev/null 2>&1";
+
+const LOG_FUNCTION = do_print;
+
+const gHTTPHandlerPath = "updates.xml";
+
+// This default value will be overridden when using the http server.
+var gURLData = URL_HOST + "/";
+
+var gTestID;
+
+var gTestserver;
+
+var gRegisteredServiceCleanup;
+
+var gCheckFunc;
+var gResponseBody;
+var gResponseStatusCode = 200;
+var gRequestURL;
+var gUpdateCount;
+var gUpdates;
+var gStatusCode;
+var gStatusText;
+var gStatusResult;
+
+var gProcess;
+var gAppTimer;
+var gHandle;
+
+var gGREDirOrig;
+var gGREBinDirOrig;
+var gAppDirOrig;
+
+// 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 gSvcOriginalLogContents;
+var gUseTestAppDir = true;
+// Some update staging failures can remove the update. This allows tests to
+// specify that the status file and the active update should not be checked
+// after an update is staged.
+var gStagingRemovedUpdate = false;
+
+var gTimeoutRuns = 0;
+var gFileInUseTimeoutRuns = 0;
+
+// Environment related globals
+var gShouldResetEnv = undefined;
+var gAddedEnvXRENoWindowsCrashDialog = false;
+var gEnvXPCOMDebugBreak;
+var gEnvXPCOMMemLeakLog;
+var gEnvDyldLibraryPath;
+var gEnvLdLibraryPath;
+var gASanOptions;
+
+// 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
+// run_test function.
+var DEBUG_AUS_TEST = true;
+
+const DATA_URI_SPEC = Services.io.newFileURI(do_get_file("../data", false)).spec;
+/* import-globals-from ../data/shared.js */
+Services.scriptloader.loadSubScript(DATA_URI_SPEC + "shared.js", this);
+
+var gTestFiles = [];
+var gTestDirs = [];
+
+// Common files for both successful and failed updates.
+var gTestFilesCommon = [
+ {
+ description: "Should never change",
+ fileName: FILE_UPDATE_SETTINGS_INI,
+ relPathDir: DIR_RESOURCES,
+ originalContents: UPDATE_SETTINGS_CONTENTS,
+ compareContents: UPDATE_SETTINGS_CONTENTS,
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o767,
+ comparePerms: 0o767
+ }, {
+ description: "Should never change",
+ fileName: "channel-prefs.js",
+ relPathDir: DIR_RESOURCES + "defaults/pref/",
+ originalContents: "ShouldNotBeReplaced\n",
+ compareContents: "ShouldNotBeReplaced\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o767,
+ comparePerms: 0o767
+ }];
+
+ // Files for a complete successful update. This can be used for a complete
+ // failed update by calling setTestFilesAndDirsForFailure.
+var gTestFilesCompleteSuccess = [
+ {
+ description: "Added by update.manifest (add)",
+ fileName: "precomplete",
+ relPathDir: DIR_RESOURCES,
+ originalContents: null,
+ compareContents: null,
+ originalFile: FILE_PARTIAL_PRECOMPLETE,
+ compareFile: FILE_COMPLETE_PRECOMPLETE,
+ originalPerms: 0o666,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "searchpluginstext0",
+ relPathDir: DIR_RESOURCES + "searchplugins/",
+ originalContents: "ToBeReplacedWithFromComplete\n",
+ compareContents: "FromComplete\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o775,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "searchpluginspng1.png",
+ relPathDir: DIR_RESOURCES + "searchplugins/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: null,
+ compareFile: "complete.png",
+ originalPerms: null,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "searchpluginspng0.png",
+ relPathDir: DIR_RESOURCES + "searchplugins/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: "partial.png",
+ compareFile: "complete.png",
+ originalPerms: 0o666,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "removed-files",
+ relPathDir: DIR_RESOURCES,
+ originalContents: null,
+ compareContents: null,
+ originalFile: FILE_PARTIAL_REMOVEDFILES,
+ compareFile: FILE_COMPLETE_REMOVEDFILES,
+ originalPerms: 0o666,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest if the parent directory exists (add-if)",
+ fileName: "extensions1text0",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
+ originalContents: null,
+ compareContents: "FromComplete\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: null,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest if the parent directory exists (add-if)",
+ fileName: "extensions1png1.png",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: "partial.png",
+ compareFile: "complete.png",
+ originalPerms: 0o666,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest if the parent directory exists (add-if)",
+ fileName: "extensions1png0.png",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: null,
+ compareFile: "complete.png",
+ originalPerms: null,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest if the parent directory exists (add-if)",
+ fileName: "extensions0text0",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
+ originalContents: "ToBeReplacedWithFromComplete\n",
+ compareContents: "FromComplete\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: null,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest if the parent directory exists (add-if)",
+ fileName: "extensions0png1.png",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: null,
+ compareFile: "complete.png",
+ originalPerms: null,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest if the parent directory exists (add-if)",
+ fileName: "extensions0png0.png",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: null,
+ compareFile: "complete.png",
+ originalPerms: null,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "exe0.exe",
+ relPathDir: DIR_MACOS,
+ originalContents: null,
+ compareContents: null,
+ originalFile: FILE_HELPER_BIN,
+ compareFile: FILE_COMPLETE_EXE,
+ originalPerms: 0o777,
+ comparePerms: 0o755
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "10text0",
+ relPathDir: DIR_RESOURCES + "1/10/",
+ originalContents: "ToBeReplacedWithFromComplete\n",
+ compareContents: "FromComplete\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o767,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "0exe0.exe",
+ relPathDir: DIR_RESOURCES + "0/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: FILE_HELPER_BIN,
+ compareFile: FILE_COMPLETE_EXE,
+ originalPerms: 0o777,
+ comparePerms: 0o755
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "00text1",
+ relPathDir: DIR_RESOURCES + "0/00/",
+ originalContents: "ToBeReplacedWithFromComplete\n",
+ compareContents: "FromComplete\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o677,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "00text0",
+ relPathDir: DIR_RESOURCES + "0/00/",
+ originalContents: "ToBeReplacedWithFromComplete\n",
+ compareContents: "FromComplete\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o775,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "00png0.png",
+ relPathDir: DIR_RESOURCES + "0/00/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: null,
+ compareFile: "complete.png",
+ originalPerms: 0o776,
+ comparePerms: 0o644
+ }, {
+ description: "Removed by precomplete (remove)",
+ fileName: "20text0",
+ relPathDir: DIR_RESOURCES + "2/20/",
+ originalContents: "ToBeDeleted\n",
+ compareContents: null,
+ originalFile: null,
+ compareFile: null,
+ originalPerms: null,
+ comparePerms: null
+ }, {
+ description: "Removed by precomplete (remove)",
+ fileName: "20png0.png",
+ relPathDir: DIR_RESOURCES + "2/20/",
+ originalContents: "ToBeDeleted\n",
+ compareContents: null,
+ originalFile: null,
+ compareFile: null,
+ originalPerms: null,
+ comparePerms: null
+ }];
+
+// Concatenate the common files to the end of the array.
+gTestFilesCompleteSuccess = gTestFilesCompleteSuccess.concat(gTestFilesCommon);
+
+// Files for a partial successful update. This can be used for a partial failed
+// update by calling setTestFilesAndDirsForFailure.
+var gTestFilesPartialSuccess = [
+ {
+ description: "Added by update.manifest (add)",
+ fileName: "precomplete",
+ relPathDir: DIR_RESOURCES,
+ originalContents: null,
+ compareContents: null,
+ originalFile: FILE_COMPLETE_PRECOMPLETE,
+ compareFile: FILE_PARTIAL_PRECOMPLETE,
+ originalPerms: 0o666,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "searchpluginstext0",
+ relPathDir: DIR_RESOURCES + "searchplugins/",
+ originalContents: "ToBeReplacedWithFromPartial\n",
+ compareContents: "FromPartial\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o775,
+ comparePerms: 0o644
+ }, {
+ description: "Patched by update.manifest if the file exists (patch-if)",
+ fileName: "searchpluginspng1.png",
+ relPathDir: DIR_RESOURCES + "searchplugins/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: "complete.png",
+ compareFile: "partial.png",
+ originalPerms: 0o666,
+ comparePerms: 0o666
+ }, {
+ description: "Patched by update.manifest if the file exists (patch-if)",
+ fileName: "searchpluginspng0.png",
+ relPathDir: DIR_RESOURCES + "searchplugins/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: "complete.png",
+ compareFile: "partial.png",
+ originalPerms: 0o666,
+ comparePerms: 0o666
+ }, {
+ description: "Added by update.manifest if the parent directory exists (add-if)",
+ fileName: "extensions1text0",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
+ originalContents: null,
+ compareContents: "FromPartial\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: null,
+ comparePerms: 0o644
+ }, {
+ description: "Patched by update.manifest if the parent directory exists (patch-if)",
+ fileName: "extensions1png1.png",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: "complete.png",
+ compareFile: "partial.png",
+ originalPerms: 0o666,
+ comparePerms: 0o666
+ }, {
+ description: "Patched by update.manifest if the parent directory exists (patch-if)",
+ fileName: "extensions1png0.png",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: "complete.png",
+ compareFile: "partial.png",
+ originalPerms: 0o666,
+ comparePerms: 0o666
+ }, {
+ description: "Added by update.manifest if the parent directory exists (add-if)",
+ fileName: "extensions0text0",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
+ originalContents: "ToBeReplacedWithFromPartial\n",
+ compareContents: "FromPartial\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o644,
+ comparePerms: 0o644
+ }, {
+ description: "Patched by update.manifest if the parent directory exists (patch-if)",
+ fileName: "extensions0png1.png",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: "complete.png",
+ compareFile: "partial.png",
+ originalPerms: 0o644,
+ comparePerms: 0o644
+ }, {
+ description: "Patched by update.manifest if the parent directory exists (patch-if)",
+ fileName: "extensions0png0.png",
+ relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: "complete.png",
+ compareFile: "partial.png",
+ originalPerms: 0o644,
+ comparePerms: 0o644
+ }, {
+ description: "Patched by update.manifest (patch)",
+ fileName: "exe0.exe",
+ relPathDir: DIR_MACOS,
+ originalContents: null,
+ compareContents: null,
+ originalFile: FILE_COMPLETE_EXE,
+ compareFile: FILE_PARTIAL_EXE,
+ originalPerms: 0o755,
+ comparePerms: 0o755
+ }, {
+ description: "Patched by update.manifest (patch)",
+ fileName: "0exe0.exe",
+ relPathDir: DIR_RESOURCES + "0/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: FILE_COMPLETE_EXE,
+ compareFile: FILE_PARTIAL_EXE,
+ originalPerms: 0o755,
+ comparePerms: 0o755
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "00text0",
+ relPathDir: DIR_RESOURCES + "0/00/",
+ originalContents: "ToBeReplacedWithFromPartial\n",
+ compareContents: "FromPartial\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o644,
+ comparePerms: 0o644
+ }, {
+ description: "Patched by update.manifest (patch)",
+ fileName: "00png0.png",
+ relPathDir: DIR_RESOURCES + "0/00/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: "complete.png",
+ compareFile: "partial.png",
+ originalPerms: 0o666,
+ comparePerms: 0o666
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "20text0",
+ relPathDir: DIR_RESOURCES + "2/20/",
+ originalContents: null,
+ compareContents: "FromPartial\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: null,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "20png0.png",
+ relPathDir: DIR_RESOURCES + "2/20/",
+ originalContents: null,
+ compareContents: null,
+ originalFile: null,
+ compareFile: "partial.png",
+ originalPerms: null,
+ comparePerms: 0o644
+ }, {
+ description: "Added by update.manifest (add)",
+ fileName: "00text2",
+ relPathDir: DIR_RESOURCES + "0/00/",
+ originalContents: null,
+ compareContents: "FromPartial\n",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: null,
+ comparePerms: 0o644
+ }, {
+ description: "Removed by update.manifest (remove)",
+ fileName: "10text0",
+ relPathDir: DIR_RESOURCES + "1/10/",
+ originalContents: "ToBeDeleted\n",
+ compareContents: null,
+ originalFile: null,
+ compareFile: null,
+ originalPerms: null,
+ comparePerms: null
+ }, {
+ description: "Removed by update.manifest (remove)",
+ fileName: "00text1",
+ relPathDir: DIR_RESOURCES + "0/00/",
+ originalContents: "ToBeDeleted\n",
+ compareContents: null,
+ originalFile: null,
+ compareFile: null,
+ originalPerms: null,
+ comparePerms: null
+ }];
+
+// Concatenate the common files to the end of the array.
+gTestFilesPartialSuccess = gTestFilesPartialSuccess.concat(gTestFilesCommon);
+
+var gTestDirsCommon = [
+ {
+ relPathDir: DIR_RESOURCES + "3/",
+ dirRemoved: false,
+ files: ["3text0", "3text1"],
+ filesRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "4/",
+ dirRemoved: true,
+ files: ["4text0", "4text1"],
+ filesRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "5/",
+ dirRemoved: true,
+ files: ["5test.exe", "5text0", "5text1"],
+ filesRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "6/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "7/",
+ dirRemoved: true,
+ files: ["7text0", "7text1"],
+ subDirs: ["70/", "71/"],
+ subDirFiles: ["7xtest.exe", "7xtext0", "7xtext1"]
+ }, {
+ relPathDir: DIR_RESOURCES + "8/",
+ dirRemoved: false
+ }, {
+ relPathDir: DIR_RESOURCES + "8/80/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "8/81/",
+ dirRemoved: false,
+ files: ["81text0", "81text1"]
+ }, {
+ relPathDir: DIR_RESOURCES + "8/82/",
+ dirRemoved: false,
+ subDirs: ["820/", "821/"]
+ }, {
+ relPathDir: DIR_RESOURCES + "8/83/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "8/84/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "8/85/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "8/86/",
+ dirRemoved: true,
+ files: ["86text0", "86text1"]
+ }, {
+ relPathDir: DIR_RESOURCES + "8/87/",
+ dirRemoved: true,
+ subDirs: ["870/", "871/"],
+ subDirFiles: ["87xtext0", "87xtext1"]
+ }, {
+ relPathDir: DIR_RESOURCES + "8/88/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "8/89/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "9/90/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "9/91/",
+ dirRemoved: false,
+ files: ["91text0", "91text1"]
+ }, {
+ relPathDir: DIR_RESOURCES + "9/92/",
+ dirRemoved: false,
+ subDirs: ["920/", "921/"]
+ }, {
+ relPathDir: DIR_RESOURCES + "9/93/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "9/94/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "9/95/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "9/96/",
+ dirRemoved: true,
+ files: ["96text0", "96text1"]
+ }, {
+ relPathDir: DIR_RESOURCES + "9/97/",
+ dirRemoved: true,
+ subDirs: ["970/", "971/"],
+ subDirFiles: ["97xtext0", "97xtext1"]
+ }, {
+ relPathDir: DIR_RESOURCES + "9/98/",
+ dirRemoved: true
+ }, {
+ relPathDir: DIR_RESOURCES + "9/99/",
+ dirRemoved: true
+ }];
+
+// Directories for a complete successful update. This array can be used for a
+// complete failed update by calling setTestFilesAndDirsForFailure.
+var gTestDirsCompleteSuccess = [
+ {
+ description: "Removed by precomplete (rmdir)",
+ relPathDir: DIR_RESOURCES + "2/20/",
+ dirRemoved: true
+ }, {
+ description: "Removed by precomplete (rmdir)",
+ relPathDir: DIR_RESOURCES + "2/",
+ dirRemoved: true
+ }];
+
+// Concatenate the common files to the beginning of the array.
+gTestDirsCompleteSuccess = gTestDirsCommon.concat(gTestDirsCompleteSuccess);
+
+// Directories for a partial successful update. This array can be used for a
+// partial failed update by calling setTestFilesAndDirsForFailure.
+var gTestDirsPartialSuccess = [
+ {
+ description: "Removed by update.manifest (rmdir)",
+ relPathDir: DIR_RESOURCES + "1/10/",
+ dirRemoved: true
+ }, {
+ description: "Removed by update.manifest (rmdir)",
+ relPathDir: DIR_RESOURCES + "1/",
+ dirRemoved: true
+ }];
+
+// Concatenate the common files to the beginning of the array.
+gTestDirsPartialSuccess = gTestDirsCommon.concat(gTestDirsPartialSuccess);
+
+// This makes it possible to run most tests on xulrunner where the update
+// channel default preference is not set.
+if (MOZ_APP_NAME == "xulrunner") {
+ try {
+ gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL);
+ } catch (e) {
+ setUpdateChannel("test_channel");
+ }
+}
+
+/**
+ * Helper function for setting up the test environment.
+ */
+function setupTestCommon() {
+ debugDump("start - general test setup");
+
+ Assert.strictEqual(gTestID, undefined,
+ "gTestID should be 'undefined' (setupTestCommon should " +
+ "only be called once)");
+
+ let caller = Components.stack.caller;
+ gTestID = caller.filename.toString().split("/").pop().split(".")[0];
+
+ // Tests that don't work with XULRunner.
+ const XUL_RUNNER_INCOMPATIBLE = ["marAppApplyUpdateAppBinInUseStageSuccess_win",
+ "marAppApplyUpdateStageSuccess",
+ "marAppApplyUpdateSuccess",
+ "marAppApplyUpdateAppBinInUseStageSuccessSvc_win",
+ "marAppApplyUpdateStageSuccessSvc",
+ "marAppApplyUpdateSuccessSvc"];
+ // Replace with Array.prototype.includes when it has stabilized.
+ if (MOZ_APP_NAME == "xulrunner" &&
+ XUL_RUNNER_INCOMPATIBLE.indexOf(gTestID) != -1) {
+ logTestInfo("Unable to run this test on xulrunner");
+ return false;
+ }
+
+ if (IS_SERVICE_TEST && !shouldRunServiceTest()) {
+ return false;
+ }
+
+ do_test_pending();
+
+ setDefaultPrefs();
+
+ // Don't attempt to show a prompt when an update finishes.
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, true);
+
+ gGREDirOrig = getGREDir();
+ gGREBinDirOrig = getGREBinDir();
+ gAppDirOrig = getAppBaseDir();
+
+ let applyDir = getApplyDirFile(null, true).parent;
+
+ // Try to remove the directory used to apply updates and the updates directory
+ // on platforms other than Windows. Since the test hasn't ran yet and the
+ // directory shouldn't exist finished this is non-fatal for the test.
+ if (applyDir.exists()) {
+ debugDump("attempting to remove directory. Path: " + applyDir.path);
+ try {
+ removeDirRecursive(applyDir);
+ } catch (e) {
+ logTestInfo("non-fatal error removing directory. Path: " +
+ applyDir.path + ", Exception: " + e);
+ // When the application doesn't exit properly it can cause the test to
+ // fail again on the second run with an NS_ERROR_FILE_ACCESS_DENIED error
+ // along with no useful information in the test log. To prevent this use
+ // a different directory for the test when it isn't possible to remove the
+ // existing test directory (bug 1294196).
+ gTestID += "_new";
+ logTestInfo("using a new directory for the test by changing gTestID " +
+ "since there is an existing test directory that can't be " +
+ "removed, gTestID: " + gTestID);
+ }
+ }
+
+ if (IS_WIN) {
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED,
+ IS_SERVICE_TEST ? true : false);
+ }
+
+ // adjustGeneralPaths registers a cleanup function that calls end_test when
+ // it is defined as a function.
+ adjustGeneralPaths();
+ // Logged once here instead of in the mock directory provider to lessen test
+ // log spam.
+ debugDump("Updates Directory (UpdRootD) Path: " + getMockUpdRootD().path);
+
+ // This prevents a warning about not being able to find the greprefs.js file
+ // from being logged.
+ let grePrefsFile = getGREDir();
+ if (!grePrefsFile.exists()) {
+ grePrefsFile.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
+ }
+ grePrefsFile.append("greprefs.js");
+ if (!grePrefsFile.exists()) {
+ grePrefsFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
+ }
+
+ // Remove the updates directory on Windows and Mac OS X which is located
+ // outside of the application directory after the call to adjustGeneralPaths
+ // has set it up. Since the test hasn't ran yet and the directory shouldn't
+ // exist this is non-fatal for the test.
+ if (IS_WIN || IS_MACOSX) {
+ let updatesDir = getMockUpdRootD();
+ if (updatesDir.exists()) {
+ debugDump("attempting to remove directory. Path: " + updatesDir.path);
+ try {
+ removeDirRecursive(updatesDir);
+ } catch (e) {
+ logTestInfo("non-fatal error removing directory. Path: " +
+ updatesDir.path + ", Exception: " + e);
+ }
+ }
+ }
+
+ debugDump("finish - general test setup");
+ return true;
+}
+
+/**
+ * Nulls out the most commonly used global vars used by tests to prevent leaks
+ * as needed and attempts to restore the system to its original state.
+ */
+function cleanupTestCommon() {
+ debugDump("start - general test cleanup");
+
+ // Force the update manager to reload the update data to prevent it from
+ // writing the old data to the files that have just been removed.
+ reloadUpdateManagerData();
+
+ if (gChannel) {
+ gPrefRoot.removeObserver(PREF_APP_UPDATE_CHANNEL, observer);
+ }
+
+ // Call app update's observe method passing xpcom-shutdown to test that the
+ // shutdown of app update runs without throwing or leaking. The observer
+ // method is used directly instead of calling notifyObservers so components
+ // outside of the scope of this test don't assert and thereby cause app update
+ // tests to fail.
+ gAUS.observe(null, "xpcom-shutdown", "");
+
+ gTestserver = null;
+
+ if (IS_UNIX) {
+ // This will delete the launch script if it exists.
+ getLaunchScript();
+ }
+
+ if (IS_WIN && MOZ_APP_BASENAME) {
+ let appDir = getApplyDirFile(null, true);
+ let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla";
+ const REG_PATH = "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME +
+ "\\TaskBarIDs";
+ let key = Cc["@mozilla.org/windows-registry-key;1"].
+ createInstance(Ci.nsIWindowsRegKey);
+ try {
+ key.open(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH,
+ Ci.nsIWindowsRegKey.ACCESS_ALL);
+ if (key.hasValue(appDir.path)) {
+ key.removeValue(appDir.path);
+ }
+ } catch (e) {
+ }
+ try {
+ key.open(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, REG_PATH,
+ Ci.nsIWindowsRegKey.ACCESS_ALL);
+ if (key.hasValue(appDir.path)) {
+ key.removeValue(appDir.path);
+ }
+ } catch (e) {
+ }
+ }
+
+ // The updates directory is located outside of the application directory and
+ // needs to be removed on Windows and Mac OS X.
+ if (IS_WIN || IS_MACOSX) {
+ let updatesDir = getMockUpdRootD();
+ // Try to remove the directory used to apply updates. Since the test has
+ // already finished this is non-fatal for the test.
+ if (updatesDir.exists()) {
+ debugDump("attempting to remove directory. Path: " + updatesDir.path);
+ try {
+ removeDirRecursive(updatesDir);
+ } catch (e) {
+ logTestInfo("non-fatal error removing directory. Path: " +
+ updatesDir.path + ", Exception: " + e);
+ }
+ if (IS_MACOSX) {
+ let updatesRootDir = gUpdatesRootDir.clone();
+ while (updatesRootDir.path != updatesDir.path) {
+ if (updatesDir.exists()) {
+ debugDump("attempting to remove directory. Path: " +
+ updatesDir.path);
+ try {
+ // Try to remove the directory without the recursive flag set
+ // since the top level directory has already had its contents
+ // removed and the parent directory might still be used by a
+ // different test.
+ updatesDir.remove(false);
+ } catch (e) {
+ logTestInfo("non-fatal error removing directory. Path: " +
+ updatesDir.path + ", Exception: " + e);
+ if (e == Cr.NS_ERROR_FILE_DIR_NOT_EMPTY) {
+ break;
+ }
+ }
+ }
+ updatesDir = updatesDir.parent;
+ }
+ }
+ }
+ }
+
+ let applyDir = getApplyDirFile(null, true).parent;
+
+ // Try to remove the directory used to apply updates. Since the test has
+ // already finished this is non-fatal for the test.
+ if (applyDir.exists()) {
+ debugDump("attempting to remove directory. Path: " + applyDir.path);
+ try {
+ removeDirRecursive(applyDir);
+ } catch (e) {
+ logTestInfo("non-fatal error removing directory. Path: " +
+ applyDir.path + ", Exception: " + e);
+ }
+ }
+
+ resetEnvironment();
+
+ debugDump("finish - general test cleanup");
+}
+
+/**
+ * Helper function that calls do_test_finished that tracks whether a parallel
+ * run of a test passed when it runs synchronously so the log output can be
+ * inspected.
+ */
+function doTestFinish() {
+ if (DEBUG_AUS_TEST) {
+ // This prevents do_print errors from being printed by the xpcshell test
+ // harness due to nsUpdateService.js logging to the console when the
+ // app.update.log preference is true.
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, false);
+ gAUS.observe(null, "nsPref:changed", PREF_APP_UPDATE_LOG);
+ }
+ do_execute_soon(do_test_finished);
+}
+
+/**
+ * Sets the most commonly used preferences used by tests
+ */
+function setDefaultPrefs() {
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true);
+ if (DEBUG_AUS_TEST) {
+ // Enable Update logging
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true);
+ } else {
+ // Some apps set this preference to true by default
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, false);
+ }
+ // In case telemetry is enabled for xpcshell tests.
+ Services.prefs.setBoolPref(PREF_TOOLKIT_TELEMETRY_ENABLED, false);
+}
+
+/**
+ * Helper function for updater binary tests that sets the appropriate values
+ * to check for update failures.
+ */
+function setTestFilesAndDirsForFailure() {
+ gTestFiles.forEach(function STFADFF_Files(aTestFile) {
+ aTestFile.compareContents = aTestFile.originalContents;
+ aTestFile.compareFile = aTestFile.originalFile;
+ aTestFile.comparePerms = aTestFile.originalPerms;
+ });
+
+ gTestDirs.forEach(function STFADFF_Dirs(aTestDir) {
+ aTestDir.dirRemoved = false;
+ if (aTestDir.filesRemoved) {
+ aTestDir.filesRemoved = false;
+ }
+ });
+}
+
+/**
+ * Helper function for updater binary tests that prevents the distribution
+ * directory files from being created.
+ */
+function preventDistributionFiles() {
+ gTestFiles = gTestFiles.filter(function(aTestFile) {
+ return aTestFile.relPathDir.indexOf("distribution/") == -1;
+ });
+
+ gTestDirs = gTestDirs.filter(function(aTestDir) {
+ return aTestDir.relPathDir.indexOf("distribution/") == -1;
+ });
+}
+
+/**
+ * On Mac OS X this sets the last modified time for the app bundle directory to
+ * a date in the past to test that the last modified time is updated when an
+ * update has been successfully applied (bug 600098).
+ */
+function setAppBundleModTime() {
+ if (!IS_MACOSX) {
+ return;
+ }
+ let now = Date.now();
+ let yesterday = now - (1000 * 60 * 60 * 24);
+ let applyToDir = getApplyDirFile();
+ applyToDir.lastModifiedTime = yesterday;
+}
+
+/**
+ * On Mac OS X this checks that the last modified time for the app bundle
+ * directory has been updated when an update has been successfully applied
+ * (bug 600098).
+ */
+function checkAppBundleModTime() {
+ if (!IS_MACOSX) {
+ return;
+ }
+ let now = Date.now();
+ let applyToDir = getApplyDirFile();
+ let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
+ Assert.ok(timeDiff < MAC_MAX_TIME_DIFFERENCE,
+ "the last modified time on the apply to directory should " +
+ "change after a successful update");
+}
+
+/**
+ * On Mac OS X and Windows this checks if the post update '.running' file exists
+ * to determine if the post update binary was launched.
+ *
+ * @param aShouldExist
+ * Whether the post update '.running' file should exist.
+ */
+function checkPostUpdateRunningFile(aShouldExist) {
+ if (!IS_WIN && !IS_MACOSX) {
+ return;
+ }
+ let postUpdateRunningFile = getPostUpdateFile(".running");
+ if (aShouldExist) {
+ Assert.ok(postUpdateRunningFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(postUpdateRunningFile.path));
+ } else {
+ Assert.ok(!postUpdateRunningFile.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(postUpdateRunningFile.path));
+ }
+}
+
+/**
+ * Initializes the most commonly used settings and creates an instance of the
+ * update service stub.
+ */
+function standardInit() {
+ createAppInfo("xpcshell@tests.mozilla.org", APP_INFO_NAME, "1.0", "2.0");
+ // Initialize the update service stub component
+ initUpdateServiceStub();
+}
+
+/**
+ * Helper function for getting the application version from the application.ini
+ * file. This will look in both the GRE and the application directories for the
+ * application.ini file.
+ *
+ * @return The version string from the application.ini file.
+ */
+function getAppVersion() {
+ // Read the application.ini and use its application version.
+ let iniFile = gGREDirOrig.clone();
+ iniFile.append(FILE_APPLICATION_INI);
+ if (!iniFile.exists()) {
+ iniFile = gGREBinDirOrig.clone();
+ iniFile.append(FILE_APPLICATION_INI);
+ }
+ Assert.ok(iniFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(iniFile.path));
+ let iniParser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
+ getService(Ci.nsIINIParserFactory).
+ createINIParser(iniFile);
+ return iniParser.getString("App", "Version");
+}
+
+/**
+ * Helper function for getting the relative path to the directory where the
+ * application binary is located (e.g. <test_file_leafname>/dir.app/).
+ *
+ * Note: The dir.app subdirectory under <test_file_leafname> is needed for
+ * platforms other than Mac OS X so the tests can run in parallel due to
+ * update staging creating a lock file named moz_update_in_progress.lock in
+ * the parent directory of the installation directory.
+ *
+ * @return The relative path to the directory where application binary is
+ * located.
+ */
+function getApplyDirPath() {
+ return gTestID + "/dir.app/";
+}
+
+/**
+ * Helper function for getting the nsIFile for a file in the directory where the
+ * update will be applied.
+ *
+ * The files for the update are located two directories below the apply to
+ * directory since Mac OS X sets the last modified time for the root directory
+ * to the current time and if the update changes any files in the root directory
+ * then it wouldn't be possible to test (bug 600098).
+ *
+ * @param aRelPath (optional)
+ * The relative path to the file or directory to get from the root of
+ * the test's directory. If not specified the test's directory will be
+ * returned.
+ * @param aAllowNonexistent (optional)
+ * Whether the file must exist. If false or not specified the file must
+ * exist or the function will throw.
+ * @return The nsIFile for the file in the directory where the update will be
+ * applied.
+ * @throws If aAllowNonexistent is not specified or is false and the file or
+ * directory does not exist.
+ */
+function getApplyDirFile(aRelPath, aAllowNonexistent) {
+ let relpath = getApplyDirPath() + (aRelPath ? aRelPath : "");
+ return do_get_file(relpath, aAllowNonexistent);
+}
+
+/**
+ * Helper function for getting the nsIFile for a file in the directory where the
+ * update will be staged.
+ *
+ * The files for the update are located two directories below the stage
+ * directory since Mac OS X sets the last modified time for the root directory
+ * to the current time and if the update changes any files in the root directory
+ * then it wouldn't be possible to test (bug 600098).
+ *
+ * @param aRelPath (optional)
+ * The relative path to the file or directory to get from the root of
+ * the stage directory. If not specified the stage directory will be
+ * returned.
+ * @param aAllowNonexistent (optional)
+ * Whether the file must exist. If false or not specified the file must
+ * exist or the function will throw.
+ * @return The nsIFile for the file in the directory where the update will be
+ * staged.
+ * @throws If aAllowNonexistent is not specified or is false and the file or
+ * directory does not exist.
+ */
+function getStageDirFile(aRelPath, aAllowNonexistent) {
+ if (IS_MACOSX) {
+ let file = getMockUpdRootD();
+ file.append(DIR_UPDATES);
+ file.append(DIR_PATCH);
+ file.append(DIR_UPDATED);
+ if (aRelPath) {
+ let pathParts = aRelPath.split("/");
+ for (let i = 0; i < pathParts.length; i++) {
+ if (pathParts[i]) {
+ file.append(pathParts[i]);
+ }
+ }
+ }
+ if (!aAllowNonexistent) {
+ Assert.ok(file.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(file.path));
+ }
+ return file;
+ }
+
+ let relpath = getApplyDirPath() + DIR_UPDATED + "/" + (aRelPath ? aRelPath : "");
+ return do_get_file(relpath, aAllowNonexistent);
+}
+
+/**
+ * Helper function for getting the relative path to the directory where the
+ * test data files are located.
+ *
+ * @return The relative path to the directory where the test data files are
+ * located.
+ */
+function getTestDirPath() {
+ return "../data/";
+}
+
+/**
+ * Helper function for getting the nsIFile for a file in the test data
+ * directory.
+ *
+ * @param aRelPath (optional)
+ * The relative path to the file or directory to get from the root of
+ * the test's data directory. If not specified the test's data
+ * directory will be returned.
+ * @param aAllowNonExists (optional)
+ * Whether or not to throw an error if the path exists.
+ * If not specified, then false is used.
+ * @return The nsIFile for the file in the test data directory.
+ * @throws If the file or directory does not exist.
+ */
+function getTestDirFile(aRelPath, aAllowNonExists) {
+ let relpath = getTestDirPath() + (aRelPath ? aRelPath : "");
+ return do_get_file(relpath, !!aAllowNonExists);
+}
+
+/**
+ * Helper function for getting the nsIFile for the maintenance service
+ * directory on Windows.
+ *
+ * @return The nsIFile for the maintenance service directory.
+ */
+function getMaintSvcDir() {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ const CSIDL_PROGRAM_FILES = 0x26;
+ const CSIDL_PROGRAM_FILESX86 = 0x2A;
+ // This will return an empty string on our Win XP build systems.
+ let maintSvcDir = getSpecialFolderDir(CSIDL_PROGRAM_FILESX86);
+ if (maintSvcDir) {
+ maintSvcDir.append("Mozilla Maintenance Service");
+ debugDump("using CSIDL_PROGRAM_FILESX86 - maintenance service install " +
+ "directory path: " + maintSvcDir.path);
+ }
+ if (!maintSvcDir || !maintSvcDir.exists()) {
+ maintSvcDir = getSpecialFolderDir(CSIDL_PROGRAM_FILES);
+ if (maintSvcDir) {
+ maintSvcDir.append("Mozilla Maintenance Service");
+ debugDump("using CSIDL_PROGRAM_FILES - maintenance service install " +
+ "directory path: " + maintSvcDir.path);
+ }
+ }
+ if (!maintSvcDir) {
+ do_throw("Unable to find the maintenance service install directory");
+ }
+
+ return maintSvcDir;
+}
+
+/**
+ * Get the nsILocalFile for a Windows special folder determined by the CSIDL
+ * passed.
+ *
+ * @param aCSIDL
+ * The CSIDL for the Windows special folder.
+ * @return The nsILocalFile for the Windows special folder.
+ * @throws If called from a platform other than Windows.
+ */
+function getSpecialFolderDir(aCSIDL) {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ let lib = ctypes.open("shell32");
+ let SHGetSpecialFolderPath = lib.declare("SHGetSpecialFolderPathW",
+ ctypes.winapi_abi,
+ ctypes.bool, /* bool(return) */
+ ctypes.int32_t, /* HWND hwndOwner */
+ ctypes.char16_t.ptr, /* LPTSTR lpszPath */
+ ctypes.int32_t, /* int csidl */
+ ctypes.bool /* BOOL fCreate */);
+
+ let aryPath = ctypes.char16_t.array()(260);
+ let rv = SHGetSpecialFolderPath(0, aryPath, aCSIDL, false);
+ lib.close();
+
+ let path = aryPath.readString(); // Convert the c-string to js-string
+ if (!path) {
+ return null;
+ }
+ debugDump("SHGetSpecialFolderPath returned path: " + path);
+ let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+ dir.initWithPath(path);
+ return dir;
+}
+
+XPCOMUtils.defineLazyGetter(this, "gInstallDirPathHash", function test_gIDPH() {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ // Figure out where we should check for a cached hash value
+ if (!MOZ_APP_BASENAME) {
+ return null;
+ }
+
+ let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla";
+ let appDir = getApplyDirFile(null, true);
+
+ const REG_PATH = "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME +
+ "\\TaskBarIDs";
+ let regKey = Cc["@mozilla.org/windows-registry-key;1"].
+ createInstance(Ci.nsIWindowsRegKey);
+ try {
+ regKey.open(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH,
+ Ci.nsIWindowsRegKey.ACCESS_ALL);
+ regKey.writeStringValue(appDir.path, gTestID);
+ return gTestID;
+ } catch (e) {
+ }
+
+ try {
+ regKey.create(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, REG_PATH,
+ Ci.nsIWindowsRegKey.ACCESS_ALL);
+ regKey.writeStringValue(appDir.path, gTestID);
+ return gTestID;
+ } catch (e) {
+ logTestInfo("failed to create registry key. Registry Path: " + REG_PATH +
+ ", Key Name: " + appDir.path + ", Key Value: " + gTestID +
+ ", Exception " + e);
+ }
+ return null;
+});
+
+XPCOMUtils.defineLazyGetter(this, "gLocalAppDataDir", function test_gLADD() {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ const CSIDL_LOCAL_APPDATA = 0x1c;
+ return getSpecialFolderDir(CSIDL_LOCAL_APPDATA);
+});
+
+XPCOMUtils.defineLazyGetter(this, "gProgFilesDir", function test_gPFD() {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ const CSIDL_PROGRAM_FILES = 0x26;
+ return getSpecialFolderDir(CSIDL_PROGRAM_FILES);
+});
+
+/**
+ * Helper function for getting the update root directory used by the tests. This
+ * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir
+ * in nsXREDirProvider.cpp so an application will be able to find the update
+ * when running a test that launches the application.
+ */
+function getMockUpdRootD() {
+ if (IS_WIN) {
+ return getMockUpdRootDWin();
+ }
+
+ if (IS_MACOSX) {
+ return getMockUpdRootDMac();
+ }
+
+ return getApplyDirFile(DIR_MACOS, true);
+}
+
+/**
+ * Helper function for getting the update root directory used by the tests. This
+ * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir
+ * in nsXREDirProvider.cpp so an application will be able to find the update
+ * when running a test that launches the application.
+ */
+function getMockUpdRootDWin() {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ let localAppDataDir = gLocalAppDataDir.clone();
+ let progFilesDir = gProgFilesDir.clone();
+ let appDir = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile).parent;
+
+ let appDirPath = appDir.path;
+ let relPathUpdates = "";
+ if (gInstallDirPathHash && (MOZ_APP_VENDOR || MOZ_APP_BASENAME)) {
+ relPathUpdates += (MOZ_APP_VENDOR ? MOZ_APP_VENDOR : MOZ_APP_BASENAME) +
+ "\\" + DIR_UPDATES + "\\" + gInstallDirPathHash;
+ }
+
+ if (!relPathUpdates && progFilesDir) {
+ if (appDirPath.length > progFilesDir.path.length) {
+ if (appDirPath.substr(0, progFilesDir.path.length) == progFilesDir.path) {
+ if (MOZ_APP_VENDOR && MOZ_APP_BASENAME) {
+ relPathUpdates += MOZ_APP_VENDOR + "\\" + MOZ_APP_BASENAME;
+ } else {
+ relPathUpdates += MOZ_APP_BASENAME;
+ }
+ relPathUpdates += appDirPath.substr(progFilesDir.path.length);
+ }
+ }
+ }
+
+ if (!relPathUpdates) {
+ if (MOZ_APP_VENDOR && MOZ_APP_BASENAME) {
+ relPathUpdates += MOZ_APP_VENDOR + "\\" + MOZ_APP_BASENAME;
+ } else {
+ relPathUpdates += MOZ_APP_BASENAME;
+ }
+ relPathUpdates += "\\" + MOZ_APP_NAME;
+ }
+
+ let updatesDir = Cc["@mozilla.org/file/local;1"].
+ createInstance(Ci.nsILocalFile);
+ updatesDir.initWithPath(localAppDataDir.path + "\\" + relPathUpdates);
+ return updatesDir;
+}
+
+XPCOMUtils.defineLazyGetter(this, "gUpdatesRootDir", function test_gURD() {
+ if (!IS_MACOSX) {
+ do_throw("Mac OS X only function called by a different platform!");
+ }
+
+ let dir = Services.dirsvc.get("ULibDir", Ci.nsILocalFile);
+ dir.append("Caches");
+ if (MOZ_APP_VENDOR || MOZ_APP_BASENAME) {
+ dir.append(MOZ_APP_VENDOR ? MOZ_APP_VENDOR : MOZ_APP_BASENAME);
+ } else {
+ dir.append("Mozilla");
+ }
+ dir.append(DIR_UPDATES);
+ return dir;
+});
+
+/**
+ * Helper function for getting the update root directory used by the tests. This
+ * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir
+ * in nsXREDirProvider.cpp so an application will be able to find the update
+ * when running a test that launches the application.
+ */
+function getMockUpdRootDMac() {
+ if (!IS_MACOSX) {
+ do_throw("Mac OS X only function called by a different platform!");
+ }
+
+ let appDir = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile).
+ parent.parent.parent;
+ let appDirPath = appDir.path;
+ appDirPath = appDirPath.substr(0, appDirPath.length - 4);
+
+ let pathUpdates = gUpdatesRootDir.path + appDirPath;
+ let updatesDir = Cc["@mozilla.org/file/local;1"].
+ createInstance(Ci.nsILocalFile);
+ updatesDir.initWithPath(pathUpdates);
+ return updatesDir;
+}
+
+/**
+ * Creates an update in progress lock file in the specified directory on
+ * Windows.
+ *
+ * @param aDir
+ * The nsIFile for the directory where the lock file should be created.
+ */
+function createUpdateInProgressLockFile(aDir) {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ let file = aDir.clone();
+ file.append(FILE_UPDATE_IN_PROGRESS_LOCK);
+ file.create(file.NORMAL_FILE_TYPE, 0o444);
+ file.QueryInterface(Ci.nsILocalFileWin);
+ file.fileAttributesWin |= file.WFA_READONLY;
+ file.fileAttributesWin &= ~file.WFA_READWRITE;
+ Assert.ok(file.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(file.path));
+ Assert.ok(!file.isWritable(),
+ "the lock file should not be writeable");
+}
+
+/**
+ * Removes an update in progress lock file in the specified directory on
+ * Windows.
+ *
+ * @param aDir
+ * The nsIFile for the directory where the lock file is located.
+ */
+function removeUpdateInProgressLockFile(aDir) {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ let file = aDir.clone();
+ file.append(FILE_UPDATE_IN_PROGRESS_LOCK);
+ file.QueryInterface(Ci.nsILocalFileWin);
+ file.fileAttributesWin |= file.WFA_READWRITE;
+ file.fileAttributesWin &= ~file.WFA_READONLY;
+ file.remove(false);
+ Assert.ok(!file.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(file.path));
+}
+
+/**
+ * Gets the test updater from the test data direcory.
+ *
+ * @return nsIFIle for the test updater.
+ */
+function getTestUpdater() {
+ let updater = getTestDirFile("updater.app", true);
+ if (!updater.exists()) {
+ updater = getTestDirFile(FILE_UPDATER_BIN);
+ if (!updater.exists()) {
+ do_throw("Unable to find the updater binary!");
+ }
+ }
+ Assert.ok(updater.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(updater.path));
+ return updater;
+}
+
+/**
+ * Copies the test updater to the GRE binary directory and returns the nsIFile
+ * for the copied test updater.
+ *
+ * @return nsIFIle for the copied test updater.
+ */
+function copyTestUpdaterToBinDir() {
+ let testUpdater = getTestUpdater();
+ let updater = getGREBinDir();
+ updater.append(testUpdater.leafName);
+ if (!updater.exists()) {
+ testUpdater.copyToFollowingLinks(updater.parent, updater.leafName);
+ }
+ return updater;
+}
+
+/**
+ * Copies the test updater to the location where it will be launched to apply an
+ * update and returns the nsIFile for the copied test updater.
+ *
+ * @return nsIFIle for the copied test updater.
+ */
+function copyTestUpdaterForRunUsingUpdater() {
+ if (IS_WIN) {
+ return copyTestUpdaterToBinDir();
+ }
+
+ let testUpdater = getTestUpdater();
+ let updater = getUpdatesPatchDir();
+ updater.append(testUpdater.leafName);
+ if (!updater.exists()) {
+ testUpdater.copyToFollowingLinks(updater.parent, updater.leafName);
+ }
+
+ if (IS_MACOSX) {
+ updater.append("Contents");
+ updater.append("MacOS");
+ updater.append("org.mozilla.updater");
+ }
+ return updater;
+}
+
+/**
+ * Logs the contents of an update log and for maintenance service tests this
+ * will log the contents of the latest maintenanceservice.log.
+ *
+ * @param aLogLeafName
+ * The leaf name of the update log.
+ */
+function logUpdateLog(aLogLeafName) {
+ let updateLog = getUpdateLog(aLogLeafName);
+ if (updateLog.exists()) {
+ // xpcshell tests won't display the entire contents so log each line.
+ let updateLogContents = readFileBytes(updateLog).replace(/\r\n/g, "\n");
+ updateLogContents = replaceLogPaths(updateLogContents);
+ let aryLogContents = updateLogContents.split("\n");
+ logTestInfo("contents of " + updateLog.path + ":");
+ aryLogContents.forEach(function RU_LC_FE(aLine) {
+ logTestInfo(aLine);
+ });
+ } else {
+ logTestInfo("update log doesn't exist, path: " + updateLog.path);
+ }
+
+ if (IS_SERVICE_TEST) {
+ let serviceLog = getMaintSvcDir();
+ serviceLog.append("logs");
+ serviceLog.append("maintenanceservice.log");
+ if (serviceLog.exists()) {
+ // xpcshell tests won't display the entire contents so log each line.
+ let serviceLogContents = readFileBytes(serviceLog).replace(/\r\n/g, "\n");
+ serviceLogContents = replaceLogPaths(serviceLogContents);
+ let aryLogContents = serviceLogContents.split("\n");
+ logTestInfo("contents of " + serviceLog.path + ":");
+ aryLogContents.forEach(function RU_LC_FE(aLine) {
+ logTestInfo(aLine);
+ });
+ } else {
+ logTestInfo("maintenance service log doesn't exist, path: " +
+ serviceLog.path);
+ }
+ }
+}
+
+/**
+ * Gets the maintenance service log contents.
+ */
+function readServiceLogFile() {
+ let file = getMaintSvcDir();
+ file.append("logs");
+ file.append("maintenanceservice.log");
+ return readFile(file);
+}
+
+/**
+ * Launches the updater binary to apply an update for updater tests.
+ *
+ * @param aExpectedStatus
+ * The expected value of update.status when the test finishes. For
+ * service tests passing STATE_PENDING or STATE_APPLIED will change the
+ * value to STATE_PENDING_SVC and STATE_APPLIED_SVC respectively.
+ * @param aSwitchApp
+ * If true the update should switch the application with an updated
+ * staged application and if false the update should be applied to the
+ * installed application.
+ * @param aExpectedExitValue
+ * The expected exit value from the updater binary for non-service
+ * tests.
+ * @param aCheckSvcLog
+ * Whether the service log should be checked for service tests.
+ * @param aPatchDirPath (optional)
+ * When specified the patch directory path to use for invalid argument
+ * tests otherwise the normal path will be used.
+ * @param aInstallDirPath (optional)
+ * When specified the install directory path to use for invalid
+ * argument tests otherwise the normal path will be used.
+ * @param aApplyToDirPath (optional)
+ * When specified the apply to / working directory path to use for
+ * invalid argument tests otherwise the normal path will be used.
+ * @param aCallbackPath (optional)
+ * When specified the callback path to use for invalid argument tests
+ * otherwise the normal path will be used.
+ */
+function runUpdate(aExpectedStatus, aSwitchApp, aExpectedExitValue, aCheckSvcLog,
+ aPatchDirPath, aInstallDirPath, aApplyToDirPath,
+ aCallbackPath) {
+ let isInvalidArgTest = !!aPatchDirPath || !!aInstallDirPath ||
+ !!aApplyToDirPath || aCallbackPath;
+
+ let svcOriginalLog;
+ if (IS_SERVICE_TEST) {
+ copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_BIN, false);
+ copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN, false);
+ if (aCheckSvcLog) {
+ svcOriginalLog = readServiceLogFile();
+ }
+ }
+
+ // Copy the updater binary to the directory where it will apply updates.
+ let updateBin = copyTestUpdaterForRunUsingUpdater();
+ Assert.ok(updateBin.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(updateBin.path));
+
+ let updatesDirPath = aPatchDirPath || getUpdatesPatchDir().path;
+ let installDirPath = aInstallDirPath || getApplyDirFile(null, true).path;
+ let applyToDirPath = aApplyToDirPath || getApplyDirFile(null, true).path;
+ let stageDirPath = aApplyToDirPath || getStageDirFile(null, true).path;
+
+ let callbackApp = getApplyDirFile(DIR_RESOURCES + gCallbackBinFile);
+ callbackApp.permissions = PERMS_DIRECTORY;
+
+ setAppBundleModTime();
+
+ let args = [updatesDirPath, installDirPath];
+ if (aSwitchApp) {
+ args[2] = stageDirPath;
+ args[3] = "0/replace";
+ } else {
+ args[2] = applyToDirPath;
+ args[3] = "0";
+ }
+
+ let launchBin = IS_SERVICE_TEST && isInvalidArgTest ? callbackApp : updateBin;
+
+ if (!isInvalidArgTest) {
+ args = args.concat([callbackApp.parent.path, callbackApp.path]);
+ args = args.concat(gCallbackArgs);
+ } else if (IS_SERVICE_TEST) {
+ args = ["launch-service", updateBin.path].concat(args);
+ } else if (aCallbackPath) {
+ args = args.concat([callbackApp.parent.path, aCallbackPath]);
+ }
+
+ debugDump("launching the program: " + launchBin.path + " " + args.join(" "));
+
+ if (aSwitchApp && !isInvalidArgTest) {
+ // We want to set the env vars again
+ gShouldResetEnv = undefined;
+ }
+
+ setEnvironment();
+
+ let process = Cc["@mozilla.org/process/util;1"].
+ createInstance(Ci.nsIProcess);
+ process.init(launchBin);
+ process.run(true, args, args.length);
+
+ resetEnvironment();
+
+ let status = readStatusFile();
+ if ((!IS_SERVICE_TEST && process.exitValue != aExpectedExitValue) ||
+ status != aExpectedStatus) {
+ if (process.exitValue != aExpectedExitValue) {
+ logTestInfo("updater exited with unexpected value! Got: " +
+ process.exitValue + ", Expected: " + aExpectedExitValue);
+ }
+ if (status != aExpectedStatus) {
+ logTestInfo("update status is not the expected status! Got: " + status +
+ ", Expected: " + aExpectedStatus);
+ }
+ logUpdateLog(FILE_LAST_UPDATE_LOG);
+ }
+
+ if (!IS_SERVICE_TEST) {
+ Assert.equal(process.exitValue, aExpectedExitValue,
+ "the process exit value" + MSG_SHOULD_EQUAL);
+ }
+ Assert.equal(status, aExpectedStatus,
+ "the update status" + MSG_SHOULD_EQUAL);
+
+ if (IS_SERVICE_TEST && aCheckSvcLog) {
+ let contents = readServiceLogFile();
+ Assert.notEqual(contents, svcOriginalLog,
+ "the contents of the maintenanceservice.log should not " +
+ "be the same as the original contents");
+ if (!isInvalidArgTest) {
+ Assert.notEqual(contents.indexOf(LOG_SVC_SUCCESSFUL_LAUNCH), -1,
+ "the contents of the maintenanceservice.log should " +
+ "contain the successful launch string");
+ }
+ }
+
+ do_execute_soon(runUpdateFinished);
+}
+
+/**
+ * Launches the helper binary synchronously with the specified arguments for
+ * updater tests.
+ *
+ * @param aArgs
+ * The arguments to pass to the helper binary.
+ * @return the process exit value returned by the helper binary.
+ */
+function runTestHelperSync(aArgs) {
+ let helperBin = getTestDirFile(FILE_HELPER_BIN);
+ let process = Cc["@mozilla.org/process/util;1"].
+ createInstance(Ci.nsIProcess);
+ process.init(helperBin);
+ debugDump("Running " + helperBin.path + " " + aArgs.join(" "));
+ process.run(true, aArgs, aArgs.length);
+ return process.exitValue;
+}
+
+/**
+ * Creates a symlink for updater tests.
+ */
+function createSymlink() {
+ let args = ["setup-symlink", "moz-foo", "moz-bar", "target",
+ getApplyDirFile().path + "/" + DIR_RESOURCES + "link"];
+ let exitValue = runTestHelperSync(args);
+ Assert.equal(exitValue, 0,
+ "the helper process exit value should be 0");
+ getApplyDirFile(DIR_RESOURCES + "link", false).permissions = 0o666;
+ args = ["setup-symlink", "moz-foo2", "moz-bar2", "target2",
+ getApplyDirFile().path + "/" + DIR_RESOURCES + "link2", "change-perm"];
+ exitValue = runTestHelperSync(args);
+ Assert.equal(exitValue, 0,
+ "the helper process exit value should be 0");
+}
+
+/**
+ * Removes a symlink for updater tests.
+ */
+function removeSymlink() {
+ let args = ["remove-symlink", "moz-foo", "moz-bar", "target",
+ getApplyDirFile().path + "/" + DIR_RESOURCES + "link"];
+ let exitValue = runTestHelperSync(args);
+ Assert.equal(exitValue, 0,
+ "the helper process exit value should be 0");
+ args = ["remove-symlink", "moz-foo2", "moz-bar2", "target2",
+ getApplyDirFile().path + "/" + DIR_RESOURCES + "link2"];
+ exitValue = runTestHelperSync(args);
+ Assert.equal(exitValue, 0,
+ "the helper process exit value should be 0");
+}
+
+/**
+ * Checks a symlink for updater tests.
+ */
+function checkSymlink() {
+ let args = ["check-symlink",
+ getApplyDirFile().path + "/" + DIR_RESOURCES + "link"];
+ let exitValue = runTestHelperSync(args);
+ Assert.equal(exitValue, 0,
+ "the helper process exit value should be 0");
+}
+
+/**
+ * Sets the active update and related information for updater tests.
+ */
+function setupActiveUpdate() {
+ let state = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+ let channel = gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL);
+ let patches = getLocalPatchString(null, null, null, null, null, "true",
+ state);
+ let updates = getLocalUpdateString(patches, null, null, null, null, null,
+ null, null, null, null, "true", channel);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeVersionFile(DEFAULT_UPDATE_VERSION);
+ writeStatusFile(state);
+ reloadUpdateManagerData();
+ Assert.ok(!!gUpdateManager.activeUpdate,
+ "the active update should be defined");
+}
+
+/**
+ * Gets the specified update log.
+ *
+ * @param aLogLeafName
+ * The leaf name of the log to get.
+ * @return nsIFile for the update log.
+ */
+function getUpdateLog(aLogLeafName) {
+ let updateLog = getUpdatesDir();
+ if (aLogLeafName == FILE_UPDATE_LOG) {
+ updateLog.append(DIR_PATCH);
+ }
+ updateLog.append(aLogLeafName);
+ return updateLog;
+}
+
+/**
+ * The update-staged observer for the call to nsIUpdateProcessor:processUpdate.
+ */
+const gUpdateStagedObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ debugDump("observe called with topic: " + aTopic + ", data: " + aData);
+ if (aTopic == "update-staged") {
+ Services.obs.removeObserver(gUpdateStagedObserver, "update-staged");
+ // The environment is reset after the update-staged observer topic because
+ // processUpdate in nsIUpdateProcessor uses a new thread and clearing the
+ // environment immediately after calling processUpdate can clear the
+ // environment before the updater is launched.
+ resetEnvironment();
+ // Use do_execute_soon to prevent any failures from propagating to the
+ // update service.
+ do_execute_soon(checkUpdateStagedState.bind(null, aData));
+ }
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
+};
+
+/**
+ * Stages an update using nsIUpdateProcessor:processUpdate for updater tests.
+ *
+ * @param aCheckSvcLog
+ * Whether the service log should be checked for service tests.
+ */
+function stageUpdate(aCheckSvcLog) {
+ debugDump("start - attempting to stage update");
+
+ if (IS_SERVICE_TEST && aCheckSvcLog) {
+ gSvcOriginalLogContents = readServiceLogFile();
+ }
+
+ Services.obs.addObserver(gUpdateStagedObserver, "update-staged", false);
+
+ setAppBundleModTime();
+ setEnvironment();
+ // Stage the update.
+ Cc["@mozilla.org/updates/update-processor;1"].
+ createInstance(Ci.nsIUpdateProcessor).
+ processUpdate(gUpdateManager.activeUpdate);
+
+ // The environment is not reset here because processUpdate in
+ // nsIUpdateProcessor uses a new thread and clearing the environment
+ // immediately after calling processUpdate can clear the environment before
+ // the updater is launched. Instead it is reset after the update-staged
+ // observer topic.
+
+ debugDump("finish - attempting to stage update");
+}
+
+/**
+ * Checks that the update state is correct as well as the expected files are
+ * present after staging and update for updater tests and then calls
+ * stageUpdateFinished.
+ *
+ * @param aUpdateState
+ * The update state received by the observer notification.
+ */
+function checkUpdateStagedState(aUpdateState) {
+ if (IS_WIN) {
+ if (IS_SERVICE_TEST) {
+ waitForServiceStop(false);
+ } else {
+ let updater = getApplyDirFile(FILE_UPDATER_BIN, true);
+ if (isFileInUse(updater)) {
+ do_timeout(FILE_IN_USE_TIMEOUT_MS,
+ checkUpdateStagedState.bind(null, aUpdateState));
+ return;
+ }
+ }
+ }
+
+ Assert.equal(aUpdateState, STATE_AFTER_STAGE,
+ "the notified state" + MSG_SHOULD_EQUAL);
+
+ if (!gStagingRemovedUpdate) {
+ Assert.equal(readStatusState(), STATE_AFTER_STAGE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+
+ Assert.equal(gUpdateManager.activeUpdate.state, STATE_AFTER_STAGE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ }
+
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_STAGE,
+ "the update state" + MSG_SHOULD_EQUAL);
+
+ let log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ let stageDir = getStageDirFile(null, true);
+ if (STATE_AFTER_STAGE == STATE_APPLIED ||
+ STATE_AFTER_STAGE == STATE_APPLIED_SVC) {
+ Assert.ok(stageDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(stageDir.path));
+ } else {
+ Assert.ok(!stageDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(stageDir.path));
+ }
+
+ if (IS_SERVICE_TEST && gSvcOriginalLogContents !== undefined) {
+ let contents = readServiceLogFile();
+ Assert.notEqual(contents, gSvcOriginalLogContents,
+ "the contents of the maintenanceservice.log should not " +
+ "be the same as the original contents");
+ Assert.notEqual(contents.indexOf(LOG_SVC_SUCCESSFUL_LAUNCH), -1,
+ "the contents of the maintenanceservice.log should " +
+ "contain the successful launch string");
+ }
+
+ do_execute_soon(stageUpdateFinished);
+}
+
+/**
+ * Helper function to check whether the maintenance service updater tests should
+ * run. See bug 711660 for more details.
+ *
+ * @return true if the test should run and false if it shouldn't.
+ */
+function shouldRunServiceTest() {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ let binDir = getGREBinDir();
+ let updaterBin = binDir.clone();
+ updaterBin.append(FILE_UPDATER_BIN);
+ Assert.ok(updaterBin.exists(),
+ MSG_SHOULD_EXIST + ", leafName: " + updaterBin.leafName);
+
+ let updaterBinPath = updaterBin.path;
+ if (/ /.test(updaterBinPath)) {
+ updaterBinPath = '"' + updaterBinPath + '"';
+ }
+
+ let isBinSigned = isBinarySigned(updaterBinPath);
+
+ const REG_PATH = "SOFTWARE\\Mozilla\\MaintenanceService\\" +
+ "3932ecacee736d366d6436db0f55bce4";
+ let key = Cc["@mozilla.org/windows-registry-key;1"].
+ createInstance(Ci.nsIWindowsRegKey);
+ try {
+ key.open(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH,
+ Ci.nsIWindowsRegKey.ACCESS_READ | key.WOW64_64);
+ } catch (e) {
+ // The build system could sign the files and not have the test registry key
+ // in which case we should fail the test if the updater binary is signed so
+ // the build system can be fixed by adding the registry key.
+ if (IS_AUTHENTICODE_CHECK_ENABLED) {
+ Assert.ok(!isBinSigned,
+ "the updater.exe binary should not be signed when the test " +
+ "registry key doesn't exist (if it is, build system " +
+ "configuration bug?)");
+ }
+
+ logTestInfo("this test can only run on the buildbot build system at this " +
+ "time");
+ return false;
+ }
+
+ // Check to make sure the service is installed
+ let args = ["wait-for-service-stop", "MozillaMaintenance", "10"];
+ let exitValue = runTestHelperSync(args);
+ Assert.notEqual(exitValue, 0xEE, "the maintenance service should be " +
+ "installed (if not, build system configuration bug?)");
+
+ if (IS_AUTHENTICODE_CHECK_ENABLED) {
+ // The test registry key exists and IS_AUTHENTICODE_CHECK_ENABLED is true
+ // so the binaries should be signed. To run the test locally
+ // DISABLE_UPDATER_AUTHENTICODE_CHECK can be defined.
+ Assert.ok(isBinSigned,
+ "the updater.exe binary should be signed (if not, build system " +
+ "configuration bug?)");
+ }
+
+ // In case the machine is running an old maintenance service or if it
+ // is not installed, and permissions exist to install it. Then install
+ // the newer bin that we have since all of the other checks passed.
+ return attemptServiceInstall();
+}
+
+/**
+ * Helper function to check whether the a binary is signed.
+ *
+ * @param aBinPath
+ * The path to the file to check if it is signed.
+ * @return true if the file is signed and false if it isn't.
+ */
+function isBinarySigned(aBinPath) {
+ let args = ["check-signature", aBinPath];
+ let exitValue = runTestHelperSync(args);
+ if (exitValue != 0) {
+ logTestInfo("binary is not signed. " + FILE_HELPER_BIN + " returned " +
+ exitValue + " for file " + aBinPath);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Helper function for asynchronously setting up the application files required
+ * to launch the application for the updater tests by either copying or creating
+ * symlinks for the files. This is needed for Windows debug builds which can
+ * lock a file that is being copied so that the tests can run in parallel. After
+ * the files have been copied the setupUpdaterTestFinished function will be
+ * called.
+ */
+function setupAppFilesAsync() {
+ gTimeoutRuns++;
+ try {
+ setupAppFiles();
+ } catch (e) {
+ if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
+ do_throw("Exceeded MAX_TIMEOUT_RUNS while trying to setup application " +
+ "files! Exception: " + e);
+ }
+ do_execute_soon(setupAppFilesAsync);
+ return;
+ }
+
+ do_execute_soon(setupUpdaterTestFinished);
+}
+
+/**
+ * Helper function for setting up the application files required to launch the
+ * application for the updater tests by either copying or creating symlinks to
+ * the files.
+ */
+function setupAppFiles() {
+ debugDump("start - copying or creating symlinks to application files " +
+ "for the test");
+
+ let destDir = getApplyDirFile(null, true);
+ if (!destDir.exists()) {
+ try {
+ destDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
+ } catch (e) {
+ logTestInfo("unable to create directory! Path: " + destDir.path +
+ ", Exception: " + e);
+ do_throw(e);
+ }
+ }
+
+ // Required files for the application or the test that aren't listed in the
+ // dependentlibs.list file.
+ let appFiles = [{relPath: FILE_APP_BIN,
+ inGreDir: false},
+ {relPath: FILE_APPLICATION_INI,
+ inGreDir: true},
+ {relPath: "dependentlibs.list",
+ inGreDir: true}];
+
+ // On Linux the updater.png must also be copied
+ if (IS_UNIX && !IS_MACOSX) {
+ appFiles.push({relPath: "icons/updater.png",
+ inGreDir: true});
+ }
+
+ // Read the dependent libs file leafnames from the dependentlibs.list file
+ // into the array.
+ let deplibsFile = gGREDirOrig.clone();
+ deplibsFile.append("dependentlibs.list");
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ fis.init(deplibsFile, 0x01, 0o444, Ci.nsIFileInputStream.CLOSE_ON_EOF);
+ fis.QueryInterface(Ci.nsILineInputStream);
+
+ let hasMore;
+ let line = {};
+ do {
+ hasMore = fis.readLine(line);
+ appFiles.push({relPath: line.value,
+ inGreDir: false});
+ } while (hasMore);
+
+ fis.close();
+
+ appFiles.forEach(function CMAF_FLN_FE(aAppFile) {
+ copyFileToTestAppDir(aAppFile.relPath, aAppFile.inGreDir);
+ });
+
+ copyTestUpdaterToBinDir();
+
+ debugDump("finish - copying or creating symlinks to application files " +
+ "for the test");
+}
+
+/**
+ * Copies the specified files from the dist/bin directory into the test's
+ * application directory.
+ *
+ * @param aFileRelPath
+ * The relative path to the source and the destination of the file to
+ * copy.
+ * @param aInGreDir
+ * Whether the file is located in the GRE directory which is
+ * <bundle>/Contents/Resources on Mac OS X and is the installation
+ * directory on all other platforms. If false the file must be in the
+ * GRE Binary directory which is <bundle>/Contents/MacOS on Mac OS X
+ * and is the installation directory on on all other platforms.
+ */
+function copyFileToTestAppDir(aFileRelPath, aInGreDir) {
+ // gGREDirOrig and gGREBinDirOrig must always be cloned when changing its
+ // properties
+ let srcFile = aInGreDir ? gGREDirOrig.clone() : gGREBinDirOrig.clone();
+ let destFile = aInGreDir ? getGREDir() : getGREBinDir();
+ let fileRelPath = aFileRelPath;
+ let pathParts = fileRelPath.split("/");
+ for (let i = 0; i < pathParts.length; i++) {
+ if (pathParts[i]) {
+ srcFile.append(pathParts[i]);
+ destFile.append(pathParts[i]);
+ }
+ }
+
+ if (IS_MACOSX && !srcFile.exists()) {
+ debugDump("unable to copy file since it doesn't exist! Checking if " +
+ fileRelPath + ".app exists. Path: " + srcFile.path);
+ // gGREDirOrig and gGREBinDirOrig must always be cloned when changing its
+ // properties
+ srcFile = aInGreDir ? gGREDirOrig.clone() : gGREBinDirOrig.clone();
+ destFile = aInGreDir ? getGREDir() : getGREBinDir();
+ for (let i = 0; i < pathParts.length; i++) {
+ if (pathParts[i]) {
+ srcFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : ""));
+ destFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : ""));
+ }
+ }
+ fileRelPath = fileRelPath + ".app";
+ }
+ Assert.ok(srcFile.exists(),
+ MSG_SHOULD_EXIST + ", leafName: " + srcFile.leafName);
+
+ // Symlink libraries. Note that the XUL library on Mac OS X doesn't have a
+ // file extension and shouldSymlink will always be false on Windows.
+ let shouldSymlink = (pathParts[pathParts.length - 1] == "XUL" ||
+ fileRelPath.substr(fileRelPath.length - 3) == ".so" ||
+ fileRelPath.substr(fileRelPath.length - 6) == ".dylib");
+ if (!shouldSymlink) {
+ if (!destFile.exists()) {
+ try {
+ srcFile.copyToFollowingLinks(destFile.parent, destFile.leafName);
+ } catch (e) {
+ // Just in case it is partially copied
+ if (destFile.exists()) {
+ try {
+ destFile.remove(true);
+ } catch (ex) {
+ logTestInfo("unable to remove file that failed to copy! Path: " +
+ destFile.path);
+ }
+ }
+ do_throw("Unable to copy file! Path: " + srcFile.path +
+ ", Exception: " + ex);
+ }
+ }
+ } else {
+ try {
+ if (destFile.exists()) {
+ destFile.remove(false);
+ }
+ let ln = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+ ln.initWithPath("/bin/ln");
+ let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
+ process.init(ln);
+ let args = ["-s", srcFile.path, destFile.path];
+ process.run(true, args, args.length);
+ Assert.ok(destFile.isSymlink(),
+ destFile.leafName + " should be a symlink");
+ } catch (e) {
+ do_throw("Unable to create symlink for file! Path: " + srcFile.path +
+ ", Exception: " + e);
+ }
+ }
+}
+
+/**
+ * Attempts to upgrade the maintenance service if permissions are allowed.
+ * This is useful for XP where we have permission to upgrade in case an
+ * older service installer exists. Also if the user manually installed into
+ * a unprivileged location.
+ *
+ * @return true if the installed service is from this build. If the installed
+ * service is not from this build the test will fail instead of
+ * returning false.
+ */
+function attemptServiceInstall() {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ let maintSvcDir = getMaintSvcDir();
+ Assert.ok(maintSvcDir.exists(),
+ MSG_SHOULD_EXIST + ", leafName: " + maintSvcDir.leafName);
+ let oldMaintSvcBin = maintSvcDir.clone();
+ oldMaintSvcBin.append(FILE_MAINTENANCE_SERVICE_BIN);
+ Assert.ok(oldMaintSvcBin.exists(),
+ MSG_SHOULD_EXIST + ", leafName: " + oldMaintSvcBin.leafName);
+ let buildMaintSvcBin = getGREBinDir();
+ buildMaintSvcBin.append(FILE_MAINTENANCE_SERVICE_BIN);
+ if (readFileBytes(oldMaintSvcBin) == readFileBytes(buildMaintSvcBin)) {
+ debugDump("installed maintenance service binary is the same as the " +
+ "build's maintenance service binary");
+ return true;
+ }
+ let backupMaintSvcBin = maintSvcDir.clone();
+ backupMaintSvcBin.append(FILE_MAINTENANCE_SERVICE_BIN + ".backup");
+ try {
+ if (backupMaintSvcBin.exists()) {
+ backupMaintSvcBin.remove(false);
+ }
+ oldMaintSvcBin.moveTo(maintSvcDir, FILE_MAINTENANCE_SERVICE_BIN + ".backup");
+ buildMaintSvcBin.copyTo(maintSvcDir, FILE_MAINTENANCE_SERVICE_BIN);
+ backupMaintSvcBin.remove(false);
+ } catch (e) {
+ // Restore the original file in case the moveTo was successful.
+ if (backupMaintSvcBin.exists()) {
+ oldMaintSvcBin = maintSvcDir.clone();
+ oldMaintSvcBin.append(FILE_MAINTENANCE_SERVICE_BIN);
+ if (!oldMaintSvcBin.exists()) {
+ backupMaintSvcBin.moveTo(maintSvcDir, FILE_MAINTENANCE_SERVICE_BIN);
+ }
+ }
+ Assert.ok(false, "should be able copy the test maintenance service to " +
+ "the maintenance service directory (if not, build system " +
+ "configuration bug?), path: " + maintSvcDir.path);
+ }
+
+ return true;
+}
+
+/**
+ * Waits for the applications that are launched by the maintenance service to
+ * stop.
+ */
+function waitServiceApps() {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ // maintenanceservice_installer.exe is started async during updates.
+ waitForApplicationStop("maintenanceservice_installer.exe");
+ // maintenanceservice_tmp.exe is started async from the service installer.
+ waitForApplicationStop("maintenanceservice_tmp.exe");
+ // In case the SCM thinks the service is stopped, but process still exists.
+ waitForApplicationStop("maintenanceservice.exe");
+}
+
+/**
+ * Waits for the maintenance service to stop.
+ */
+function waitForServiceStop(aFailTest) {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ waitServiceApps();
+ debugDump("waiting for the maintenance service to stop if necessary");
+ // Use the helper bin to ensure the service is stopped. If not stopped, then
+ // wait for the service to stop (at most 120 seconds).
+ let args = ["wait-for-service-stop", "MozillaMaintenance", "120"];
+ let exitValue = runTestHelperSync(args);
+ Assert.notEqual(exitValue, 0xEE,
+ "the maintenance service should exist");
+ if (exitValue != 0) {
+ if (aFailTest) {
+ Assert.ok(false, "the maintenance service should stop, process exit " +
+ "value: " + exitValue);
+ }
+ logTestInfo("maintenance service did not stop which may cause test " +
+ "failures later, process exit value: " + exitValue);
+ } else {
+ debugDump("service stopped");
+ }
+ waitServiceApps();
+}
+
+/**
+ * Waits for the specified application to stop.
+ *
+ * @param aApplication
+ * The application binary name to wait until it has stopped.
+ */
+function waitForApplicationStop(aApplication) {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ debugDump("waiting for " + aApplication + " to stop if necessary");
+ // Use the helper bin to ensure the application is stopped. If not stopped,
+ // then wait for it to stop (at most 120 seconds).
+ let args = ["wait-for-application-exit", aApplication, "120"];
+ let exitValue = runTestHelperSync(args);
+ Assert.equal(exitValue, 0,
+ "the process should have stopped, process name: " +
+ aApplication);
+}
+
+
+/**
+ * Gets the platform specific shell binary that is launched using nsIProcess and
+ * in turn launches a binary used for the test (e.g. application, updater,
+ * etc.). A shell is used so debug console output can be redirected to a file so
+ * it doesn't end up in the test log.
+ *
+ * @return nsIFile for the shell binary to launch using nsIProcess.
+ */
+function getLaunchBin() {
+ let launchBin;
+ if (IS_WIN) {
+ launchBin = Services.dirsvc.get("WinD", Ci.nsIFile);
+ launchBin.append("System32");
+ launchBin.append("cmd.exe");
+ } else {
+ launchBin = Cc["@mozilla.org/file/local;1"].
+ createInstance(Ci.nsILocalFile);
+ launchBin.initWithPath("/bin/sh");
+ }
+ Assert.ok(launchBin.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(launchBin.path));
+
+ return launchBin;
+}
+
+
+/**
+ * Locks a Windows directory.
+ *
+ * @param aDirPath
+ * The test file object that describes the file to make in use.
+ */
+function lockDirectory(aDirPath) {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ debugDump("start - locking installation directory");
+ const LPCWSTR = ctypes.char16_t.ptr;
+ const DWORD = ctypes.uint32_t;
+ const LPVOID = ctypes.voidptr_t;
+ const GENERIC_READ = 0x80000000;
+ const FILE_SHARE_READ = 1;
+ const FILE_SHARE_WRITE = 2;
+ const OPEN_EXISTING = 3;
+ const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
+ const INVALID_HANDLE_VALUE = LPVOID(0xffffffff);
+ let kernel32 = ctypes.open("kernel32");
+ let CreateFile = kernel32.declare("CreateFileW", ctypes.default_abi,
+ LPVOID, LPCWSTR, DWORD, DWORD,
+ LPVOID, DWORD, DWORD, LPVOID);
+ gHandle = CreateFile(aDirPath, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, LPVOID(0),
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, LPVOID(0));
+ Assert.notEqual(gHandle.toString(), INVALID_HANDLE_VALUE.toString(),
+ "the handle should not equal INVALID_HANDLE_VALUE");
+ kernel32.close();
+ debugDump("finish - locking installation directory");
+}
+
+/**
+ * Launches the test helper binary to make it in use for updater tests and then
+ * calls waitForHelperSleep.
+ *
+ * @param aTestFile
+ * The test file object that describes the file to make in use.
+ */
+function runHelperFileInUse(aRelPath, aCopyTestHelper) {
+ logTestInfo("aRelPath: " + aRelPath);
+ // Launch an existing file so it is in use during the update.
+ let helperBin = getTestDirFile(FILE_HELPER_BIN);
+ let fileInUseBin = getApplyDirFile(aRelPath);
+ if (aCopyTestHelper) {
+ fileInUseBin.remove(false);
+ helperBin.copyTo(fileInUseBin.parent, fileInUseBin.leafName);
+ }
+ fileInUseBin.permissions = PERMS_DIRECTORY;
+ let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
+ HELPER_SLEEP_TIMEOUT];
+ let fileInUseProcess = Cc["@mozilla.org/process/util;1"].
+ createInstance(Ci.nsIProcess);
+ fileInUseProcess.init(fileInUseBin);
+ fileInUseProcess.run(false, args, args.length);
+
+ do_execute_soon(waitForHelperSleep);
+}
+
+/**
+ * Launches the test helper binary and locks a file specified on the command
+ * line for updater tests and then calls waitForHelperSleep.
+ *
+ * @param aTestFile
+ * The test file object that describes the file to lock.
+ */
+function runHelperLockFile(aTestFile) {
+ // Exclusively lock an existing file so it is in use during the update.
+ let helperBin = getTestDirFile(FILE_HELPER_BIN);
+ let helperDestDir = getApplyDirFile(DIR_RESOURCES);
+ helperBin.copyTo(helperDestDir, FILE_HELPER_BIN);
+ helperBin = getApplyDirFile(DIR_RESOURCES + FILE_HELPER_BIN);
+ // Strip off the first two directories so the path has to be from the helper's
+ // working directory.
+ let lockFileRelPath = aTestFile.relPathDir.split("/");
+ if (IS_MACOSX) {
+ lockFileRelPath = lockFileRelPath.slice(2);
+ }
+ lockFileRelPath = lockFileRelPath.join("/") + "/" + aTestFile.fileName;
+ let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
+ HELPER_SLEEP_TIMEOUT, lockFileRelPath];
+ let helperProcess = Cc["@mozilla.org/process/util;1"].
+ createInstance(Ci.nsIProcess);
+ helperProcess.init(helperBin);
+ helperProcess.run(false, args, args.length);
+
+ do_execute_soon(waitForHelperSleep);
+}
+
+/**
+ * Helper function that waits until the helper has completed its operations and
+ * calls waitForHelperSleepFinished when it is finished.
+ */
+function waitForHelperSleep() {
+ gTimeoutRuns++;
+ // Give the lock file process time to lock the file before updating otherwise
+ // this test can fail intermittently on Windows debug builds.
+ let output = getApplyDirFile(DIR_RESOURCES + "output", true);
+ if (readFile(output) != "sleeping\n") {
+ if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
+ do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the helper to " +
+ "finish its operation. Path: " + output.path);
+ }
+ // Uses do_timeout instead of do_execute_soon to lessen log spew.
+ do_timeout(FILE_IN_USE_TIMEOUT_MS, waitForHelperSleep);
+ return;
+ }
+ try {
+ output.remove(false);
+ } catch (e) {
+ if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
+ do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the helper " +
+ "message file to no longer be in use. Path: " + output.path);
+ }
+ debugDump("failed to remove file. Path: " + output.path);
+ // Uses do_timeout instead of do_execute_soon to lessen log spew.
+ do_timeout(FILE_IN_USE_TIMEOUT_MS, waitForHelperSleep);
+ return;
+ }
+ waitForHelperSleepFinished();
+}
+
+/**
+ * Helper function that waits until the helper has finished its operations
+ * before calling waitForHelperFinishFileUnlock to verify that the helper's
+ * input and output directories are no longer in use.
+ */
+function waitForHelperFinished() {
+ // Give the lock file process time to lock the file before updating otherwise
+ // this test can fail intermittently on Windows debug builds.
+ let output = getApplyDirFile(DIR_RESOURCES + "output", true);
+ if (readFile(output) != "finished\n") {
+ // Uses do_timeout instead of do_execute_soon to lessen log spew.
+ do_timeout(FILE_IN_USE_TIMEOUT_MS, waitForHelperFinished);
+ return;
+ }
+ // Give the lock file process time to unlock the file before deleting the
+ // input and output files.
+ waitForHelperFinishFileUnlock();
+}
+
+/**
+ * Helper function that waits until the helper's input and output files are no
+ * longer in use before calling waitForHelperExitFinished.
+ */
+function waitForHelperFinishFileUnlock() {
+ try {
+ let output = getApplyDirFile(DIR_RESOURCES + "output", true);
+ if (output.exists()) {
+ output.remove(false);
+ }
+ let input = getApplyDirFile(DIR_RESOURCES + "input", true);
+ if (input.exists()) {
+ input.remove(false);
+ }
+ } catch (e) {
+ // Give the lock file process time to unlock the file before deleting the
+ // input and output files.
+ do_execute_soon(waitForHelperFinishFileUnlock);
+ return;
+ }
+ do_execute_soon(waitForHelperExitFinished);
+}
+
+/**
+ * Helper function to tell the helper to finish and exit its sleep state.
+ */
+function waitForHelperExit() {
+ let input = getApplyDirFile(DIR_RESOURCES + "input", true);
+ writeFile(input, "finish\n");
+ waitForHelperFinished();
+}
+
+/**
+ * Helper function for updater binary tests that creates the files and
+ * directories used by the test.
+ *
+ * @param aMarFile
+ * The mar file for the update test.
+ * @param aPostUpdateAsync
+ * When null the updater.ini is not created otherwise this parameter
+ * is passed to createUpdaterINI.
+ * @param aPostUpdateExeRelPathPrefix
+ * When aPostUpdateAsync null this value is ignored otherwise it is
+ * passed to createUpdaterINI.
+ */
+function setupUpdaterTest(aMarFile, aPostUpdateAsync,
+ aPostUpdateExeRelPathPrefix = "") {
+ let updatesPatchDir = getUpdatesPatchDir();
+ if (!updatesPatchDir.exists()) {
+ updatesPatchDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
+ }
+ // Copy the mar that will be applied
+ let mar = getTestDirFile(aMarFile);
+ mar.copyToFollowingLinks(updatesPatchDir, FILE_UPDATE_MAR);
+
+ let helperBin = getTestDirFile(FILE_HELPER_BIN);
+ helperBin.permissions = PERMS_DIRECTORY;
+ let afterApplyBinDir = getApplyDirFile(DIR_RESOURCES, true);
+ helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile);
+ helperBin.copyToFollowingLinks(afterApplyBinDir, gPostUpdateBinFile);
+
+ gTestFiles.forEach(function SUT_TF_FE(aTestFile) {
+ if (aTestFile.originalFile || aTestFile.originalContents) {
+ let testDir = getApplyDirFile(aTestFile.relPathDir, true);
+ if (!testDir.exists()) {
+ testDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
+ }
+
+ let testFile;
+ if (aTestFile.originalFile) {
+ testFile = getTestDirFile(aTestFile.originalFile);
+ testFile.copyToFollowingLinks(testDir, aTestFile.fileName);
+ testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName);
+ } else {
+ testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName,
+ true);
+ writeFile(testFile, aTestFile.originalContents);
+ }
+
+ // Skip these tests on Windows since chmod doesn't really set permissions
+ // on Windows.
+ if (!IS_WIN && aTestFile.originalPerms) {
+ testFile.permissions = aTestFile.originalPerms;
+ // Store the actual permissions on the file for reference later after
+ // setting the permissions.
+ if (!aTestFile.comparePerms) {
+ aTestFile.comparePerms = testFile.permissions;
+ }
+ }
+ }
+ });
+
+ // 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(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
+ }
+
+ if (aTestDir.files) {
+ aTestDir.files.forEach(function SUT_TD_F_FE(aTestFile) {
+ let testFile = getApplyDirFile(aTestDir.relPathDir + aTestFile, true);
+ if (!testFile.exists()) {
+ testFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
+ }
+ });
+ }
+
+ if (aTestDir.subDirs) {
+ aTestDir.subDirs.forEach(function SUT_TD_SD_FE(aSubDir) {
+ let testSubDir = getApplyDirFile(aTestDir.relPathDir + aSubDir, true);
+ if (!testSubDir.exists()) {
+ testSubDir.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
+ }
+
+ if (aTestDir.subDirFiles) {
+ aTestDir.subDirFiles.forEach(function SUT_TD_SDF_FE(aTestFile) {
+ let testFile = getApplyDirFile(aTestDir.relPathDir + aSubDir + aTestFile, true);
+ if (!testFile.exists()) {
+ testFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
+ }
+ });
+ }
+ });
+ }
+ });
+
+ setupActiveUpdate();
+
+ if (aPostUpdateAsync !== null) {
+ createUpdaterINI(aPostUpdateAsync, aPostUpdateExeRelPathPrefix);
+ }
+
+ setupAppFilesAsync();
+}
+
+/**
+ * Helper function for updater binary tests that creates the update-settings.ini
+ * file.
+ */
+function createUpdateSettingsINI() {
+ let ini = getApplyDirFile(DIR_RESOURCES + FILE_UPDATE_SETTINGS_INI, true);
+ writeFile(ini, 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.
+ * @param aExeRelPathPrefix
+ * A string to prefix the ExeRelPath values in the updater.ini.
+ */
+function createUpdaterINI(aIsExeAsync, aExeRelPathPrefix) {
+ 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";
+ }
+ }
+
+ if (aExeRelPathPrefix && IS_WIN) {
+ aExeRelPathPrefix = aExeRelPathPrefix.replace("/", "\\");
+ }
+
+ let exeRelPathMac = "ExeRelPath=" + aExeRelPathPrefix + DIR_RESOURCES +
+ gPostUpdateBinFile + "\n";
+ let exeRelPathWin = "ExeRelPath=" + aExeRelPathPrefix + gPostUpdateBinFile + "\n";
+ let updaterIniContents = "[Strings]\n" +
+ "Title=Update Test\n" +
+ "Info=Running update test " + gTestID + "\n\n" +
+ "[PostUpdateMac]\n" +
+ exeRelPathMac +
+ exeArg +
+ exeAsync +
+ "\n" +
+ "[PostUpdateWin]\n" +
+ exeRelPathWin +
+ exeArg +
+ exeAsync;
+ let updaterIni = getApplyDirFile(DIR_RESOURCES + FILE_UPDATER_INI, true);
+ writeFile(updaterIni, updaterIniContents);
+}
+
+/**
+ * Gets the message log path used for assert checks to lessen the length printed
+ * to the log file.
+ *
+ * @param aPath
+ * The path to shorten for the log file.
+ * @return the message including the shortened path for the log file.
+ */
+function getMsgPath(aPath) {
+ return ", path: " + replaceLogPaths(aPath);
+}
+
+/**
+ * Helper function that replaces the common part of paths in the update log's
+ * contents with <test_dir_path> for paths to the the test directory and
+ * <update_dir_path> for paths to the update directory. This is needed since
+ * Assert.equal will truncate what it prints to the xpcshell log file.
+ *
+ * @param aLogContents
+ * The update log file's contents.
+ * @return the log contents with the paths replaced.
+ */
+function replaceLogPaths(aLogContents) {
+ let logContents = aLogContents;
+ // Remove the majority of the path up to the test directory. This is needed
+ // since Assert.equal won't print long strings to the test logs.
+ let testDirPath = do_get_file(gTestID, false).path;
+ if (IS_WIN) {
+ // Replace \\ with \\\\ so the regexp works.
+ testDirPath = testDirPath.replace(/\\/g, "\\\\");
+ }
+ logContents = logContents.replace(new RegExp(testDirPath, "g"),
+ "<test_dir_path>/" + gTestID);
+ let updatesDirPath = getMockUpdRootD().path;
+ if (IS_WIN) {
+ // Replace \\ with \\\\ so the regexp works.
+ updatesDirPath = updatesDirPath.replace(/\\/g, "\\\\");
+ }
+ logContents = logContents.replace(new RegExp(updatesDirPath, "g"),
+ "<update_dir_path>/" + gTestID);
+ if (IS_WIN) {
+ // Replace \ with /
+ logContents = logContents.replace(/\\/g, "/");
+ }
+ return logContents;
+}
+
+/**
+ * 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.
+ * @param aStaged
+ * If the update log file is for a staged update.
+ * @param aReplace
+ * If the update log file is for a replace update.
+ * @param aExcludeDistDir
+ * Removes lines containing the distribution directory from the log
+ * file to compare the update log with.
+ */
+function checkUpdateLogContents(aCompareLogFile, aStaged = false,
+ aReplace = false, aExcludeDistDir = false) {
+ if (IS_UNIX && !IS_MACOSX) {
+ // The order that files are returned when enumerating the file system on
+ // Linux is not deterministic so skip checking the logs.
+ return;
+ }
+
+ let updateLog = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ let updateLogContents = readFileBytes(updateLog);
+
+ // The channel-prefs.js is defined in gTestFilesCommon which will always be
+ // located to the end of gTestFiles when it is present.
+ if (gTestFiles.length > 1 &&
+ gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" &&
+ !gTestFiles[gTestFiles.length - 1].originalContents) {
+ updateLogContents = updateLogContents.replace(/.*defaults\/.*/g, "");
+ }
+
+ if (gTestFiles.length > 2 &&
+ gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI &&
+ !gTestFiles[gTestFiles.length - 2].originalContents) {
+ updateLogContents = updateLogContents.replace(/.*update-settings.ini.*/g, "");
+ }
+
+ // Skip the source/destination lines since they contain absolute paths.
+ // These could be changed to relative paths using <test_dir_path> and
+ // <update_dir_path>
+ updateLogContents = updateLogContents.replace(/PATCH DIRECTORY.*/g, "");
+ updateLogContents = updateLogContents.replace(/INSTALLATION DIRECTORY.*/g, "");
+ updateLogContents = updateLogContents.replace(/WORKING DIRECTORY.*/g, "");
+ // Skip lines that log failed attempts to open the callback executable.
+ updateLogContents = updateLogContents.replace(/NS_main: callback app file .*/g, "");
+
+ if (IS_MACOSX) {
+ // Skip lines that log moving the distribution directory for Mac v2 signing.
+ updateLogContents = updateLogContents.replace(/Moving old [^\n]*\nrename_file: .*/g, "");
+ updateLogContents = updateLogContents.replace(/New distribution directory .*/g, "");
+ }
+
+ if (IS_WIN) {
+ // The FindFile results when enumerating the filesystem on Windows is not
+ // determistic so the results matching the following need to be fixed.
+ let re = new RegExp("([^\n]* 7\/7text1[^\n]*)\n" +
+ "([^\n]* 7\/7text0[^\n]*)\n", "g");
+ updateLogContents = updateLogContents.replace(re, "$2\n$1\n");
+ }
+
+ if (aReplace) {
+ // Remove the lines which contain absolute paths
+ updateLogContents = updateLogContents.replace(/^Begin moving.*$/mg, "");
+ updateLogContents = updateLogContents.replace(/^ensure_remove: failed to remove file: .*$/mg, "");
+ updateLogContents = updateLogContents.replace(/^ensure_remove_recursive: unable to remove directory: .*$/mg, "");
+ updateLogContents = updateLogContents.replace(/^Removing tmpDir failed, err: -1$/mg, "");
+ updateLogContents = updateLogContents.replace(/^remove_recursive_on_reboot: .*$/mg, "");
+ }
+
+ // Remove carriage returns.
+ updateLogContents = updateLogContents.replace(/\r/g, "");
+ // Replace error codes since they are different on each platform.
+ updateLogContents = updateLogContents.replace(/, err:.*\n/g, "\n");
+ // Replace to make the log parsing happy.
+ updateLogContents = updateLogContents.replace(/non-fatal error /g, "");
+ // Remove consecutive newlines
+ updateLogContents = updateLogContents.replace(/\n+/g, "\n");
+ // Remove leading and trailing newlines
+ updateLogContents = updateLogContents.replace(/^\n|\n$/g, "");
+ // Replace the log paths with <test_dir_path> and <update_dir_path>
+ updateLogContents = replaceLogPaths(updateLogContents);
+
+ let compareLogContents = "";
+ if (aCompareLogFile) {
+ compareLogContents = readFileBytes(getTestDirFile(aCompareLogFile));
+ }
+
+ if (aStaged) {
+ compareLogContents = PERFORMING_STAGED_UPDATE + "\n" + compareLogContents;
+ }
+
+ // The channel-prefs.js is defined in gTestFilesCommon which will always be
+ // located to the end of gTestFiles.
+ if (gTestFiles.length > 1 &&
+ gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" &&
+ !gTestFiles[gTestFiles.length - 1].originalContents) {
+ compareLogContents = compareLogContents.replace(/.*defaults\/.*/g, "");
+ }
+
+ if (gTestFiles.length > 2 &&
+ gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI &&
+ !gTestFiles[gTestFiles.length - 2].originalContents) {
+ compareLogContents = compareLogContents.replace(/.*update-settings.ini.*/g, "");
+ }
+
+ if (aExcludeDistDir) {
+ compareLogContents = compareLogContents.replace(/.*distribution\/.*/g, "");
+ }
+
+ // Remove leading and trailing newlines
+ compareLogContents = compareLogContents.replace(/\n+/g, "\n");
+ // Remove leading and trailing newlines
+ compareLogContents = compareLogContents.replace(/^\n|\n$/g, "");
+
+ // Don't write the contents of the file to the log to reduce log spam
+ // unless there is a failure.
+ if (compareLogContents == updateLogContents) {
+ Assert.ok(true, "the update log contents" + MSG_SHOULD_EQUAL);
+ } else {
+ logTestInfo("the update log contents are not correct");
+ logUpdateLog(FILE_LAST_UPDATE_LOG);
+ let aryLog = updateLogContents.split("\n");
+ let aryCompare = compareLogContents.split("\n");
+ // Pushing an empty string to both arrays makes it so either array's length
+ // can be used in the for loop below without going out of bounds.
+ aryLog.push("");
+ aryCompare.push("");
+ // xpcshell tests won't display the entire contents so log the first
+ // incorrect line.
+ for (let i = 0; i < aryLog.length; ++i) {
+ if (aryLog[i] != aryCompare[i]) {
+ logTestInfo("the first incorrect line in the update log is: " +
+ aryLog[i]);
+ Assert.equal(aryLog[i], aryCompare[i],
+ "the update log contents" + MSG_SHOULD_EQUAL);
+ }
+ }
+ // This should never happen!
+ do_throw("Unable to find incorrect update log contents!");
+ }
+}
+
+/**
+ * Helper function to check if the update log contains a string.
+ *
+ * @param aCheckString
+ * The string to check if the update log contains.
+ */
+function checkUpdateLogContains(aCheckString) {
+ let updateLog = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ let updateLogContents = readFileBytes(updateLog).replace(/\r\n/g, "\n");
+ updateLogContents = replaceLogPaths(updateLogContents);
+ Assert.notEqual(updateLogContents.indexOf(aCheckString), -1,
+ "the update log contents should contain value: " +
+ aCheckString);
+}
+
+/**
+ * Helper function for updater binary tests for verifying the state of files and
+ * directories after a successful update.
+ *
+ * @param aGetFileFunc
+ * The function used to get the files in the directory to be checked.
+ * @param aStageDirExists
+ * If true the staging directory will be tested for existence and if
+ * false the staging directory will be tested for non-existence.
+ * @param aToBeDeletedDirExists
+ * On Windows, if true the tobedeleted directory will be tested for
+ * existence and if false the tobedeleted directory will be tested for
+ * non-existence. On all othere platforms it will be tested for
+ * non-existence.
+ */
+function checkFilesAfterUpdateSuccess(aGetFileFunc, aStageDirExists = false,
+ aToBeDeletedDirExists = false) {
+ debugDump("testing contents of files after a successful update");
+ gTestFiles.forEach(function CFAUS_TF_FE(aTestFile) {
+ let testFile = aGetFileFunc(aTestFile.relPathDir + aTestFile.fileName, true);
+ debugDump("testing file: " + testFile.path);
+ if (aTestFile.compareFile || aTestFile.compareContents) {
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+
+ // Skip these tests on Windows since chmod doesn't really set permissions
+ // on Windows.
+ if (!IS_WIN && aTestFile.comparePerms) {
+ // Check if the permssions as set in the complete mar file are correct.
+ Assert.equal(testFile.permissions & 0xfff,
+ aTestFile.comparePerms & 0xfff,
+ "the file permissions" + MSG_SHOULD_EQUAL);
+ }
+
+ let fileContents1 = readFileBytes(testFile);
+ let fileContents2 = aTestFile.compareFile ?
+ readFileBytes(getTestDirFile(aTestFile.compareFile)) :
+ aTestFile.compareContents;
+ // Don't write the contents of the file to the log to reduce log spam
+ // unless there is a failure.
+ if (fileContents1 == fileContents2) {
+ Assert.ok(true, "the file contents" + MSG_SHOULD_EQUAL);
+ } else {
+ Assert.equal(fileContents1, fileContents2,
+ "the file contents" + MSG_SHOULD_EQUAL);
+ }
+ } else {
+ Assert.ok(!testFile.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(testFile.path));
+ }
+ });
+
+ debugDump("testing operations specified in removed-files were performed " +
+ "after a successful update");
+ gTestDirs.forEach(function CFAUS_TD_FE(aTestDir) {
+ let testDir = aGetFileFunc(aTestDir.relPathDir, true);
+ debugDump("testing directory: " + testDir.path);
+ if (aTestDir.dirRemoved) {
+ Assert.ok(!testDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(testDir.path));
+ } else {
+ Assert.ok(testDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testDir.path));
+
+ if (aTestDir.files) {
+ aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) {
+ let testFile = aGetFileFunc(aTestDir.relPathDir + aTestFile, true);
+ if (aTestDir.filesRemoved) {
+ Assert.ok(!testFile.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(testFile.path));
+ } else {
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+ }
+ });
+ }
+
+ if (aTestDir.subDirs) {
+ aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) {
+ let testSubDir = aGetFileFunc(aTestDir.relPathDir + aSubDir, true);
+ Assert.ok(testSubDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testSubDir.path));
+ if (aTestDir.subDirFiles) {
+ aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) {
+ let testFile = aGetFileFunc(aTestDir.relPathDir +
+ aSubDir + aTestFile, true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+ });
+ }
+ });
+ }
+ }
+ });
+
+ checkFilesAfterUpdateCommon(aGetFileFunc, aStageDirExists,
+ aToBeDeletedDirExists);
+}
+
+/**
+ * Helper function for updater binary tests for verifying the state of files and
+ * directories after a failed update.
+ *
+ * @param aGetFileFunc
+ * The function used to get the files in the directory to be checked.
+ * @param aStageDirExists
+ * If true the staging directory will be tested for existence and if
+ * false the staging directory will be tested for non-existence.
+ * @param aToBeDeletedDirExists
+ * On Windows, if true the tobedeleted directory will be tested for
+ * existence and if false the tobedeleted directory will be tested for
+ * non-existence. On all othere platforms it will be tested for
+ * non-existence.
+ */
+function checkFilesAfterUpdateFailure(aGetFileFunc, aStageDirExists = false,
+ aToBeDeletedDirExists = false) {
+ debugDump("testing contents of files after a failed update");
+ gTestFiles.forEach(function CFAUF_TF_FE(aTestFile) {
+ let testFile = aGetFileFunc(aTestFile.relPathDir + aTestFile.fileName, true);
+ debugDump("testing file: " + testFile.path);
+ if (aTestFile.compareFile || aTestFile.compareContents) {
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+
+ // Skip these tests on Windows since chmod doesn't really set permissions
+ // on Windows.
+ if (!IS_WIN && aTestFile.comparePerms) {
+ // Check the original permssions are retained on the file.
+ Assert.equal(testFile.permissions & 0xfff,
+ aTestFile.comparePerms & 0xfff,
+ "the file permissions" + MSG_SHOULD_EQUAL);
+ }
+
+ let fileContents1 = readFileBytes(testFile);
+ let fileContents2 = aTestFile.compareFile ?
+ readFileBytes(getTestDirFile(aTestFile.compareFile)) :
+ aTestFile.compareContents;
+ // Don't write the contents of the file to the log to reduce log spam
+ // unless there is a failure.
+ if (fileContents1 == fileContents2) {
+ Assert.ok(true, "the file contents" + MSG_SHOULD_EQUAL);
+ } else {
+ Assert.equal(fileContents1, fileContents2,
+ "the file contents" + MSG_SHOULD_EQUAL);
+ }
+ } else {
+ Assert.ok(!testFile.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(testFile.path));
+ }
+ });
+
+ debugDump("testing operations specified in removed-files were not " +
+ "performed after a failed update");
+ gTestDirs.forEach(function CFAUF_TD_FE(aTestDir) {
+ let testDir = aGetFileFunc(aTestDir.relPathDir, true);
+ Assert.ok(testDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testDir.path));
+
+ if (aTestDir.files) {
+ aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) {
+ let testFile = aGetFileFunc(aTestDir.relPathDir + aTestFile, true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+ });
+ }
+
+ if (aTestDir.subDirs) {
+ aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) {
+ let testSubDir = aGetFileFunc(aTestDir.relPathDir + aSubDir, true);
+ Assert.ok(testSubDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testSubDir.path));
+ if (aTestDir.subDirFiles) {
+ aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) {
+ let testFile = aGetFileFunc(aTestDir.relPathDir +
+ aSubDir + aTestFile, true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+ });
+ }
+ });
+ }
+ });
+
+ checkFilesAfterUpdateCommon(aGetFileFunc, aStageDirExists,
+ aToBeDeletedDirExists);
+}
+
+/**
+ * Helper function for updater binary tests for verifying the state of common
+ * files and directories after a successful or failed update.
+ *
+ * @param aGetFileFunc
+ * the function used to get the files in the directory to be checked.
+ * @param aStageDirExists
+ * If true the staging directory will be tested for existence and if
+ * false the staging directory will be tested for non-existence.
+ * @param aToBeDeletedDirExists
+ * On Windows, if true the tobedeleted directory will be tested for
+ * existence and if false the tobedeleted directory will be tested for
+ * non-existence. On all othere platforms it will be tested for
+ * non-existence.
+ */
+function checkFilesAfterUpdateCommon(aGetFileFunc, aStageDirExists,
+ aToBeDeletedDirExists) {
+ debugDump("testing extra directories");
+ let stageDir = getStageDirFile(null, true);
+ if (aStageDirExists) {
+ Assert.ok(stageDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(stageDir.path));
+ } else {
+ Assert.ok(!stageDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(stageDir.path));
+ }
+
+ let toBeDeletedDirExists = IS_WIN ? aToBeDeletedDirExists : false;
+ let toBeDeletedDir = getApplyDirFile(DIR_TOBEDELETED, true);
+ if (toBeDeletedDirExists) {
+ Assert.ok(toBeDeletedDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(toBeDeletedDir.path));
+ } else {
+ Assert.ok(!toBeDeletedDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(toBeDeletedDir.path));
+ }
+
+ let updatingDir = getApplyDirFile("updating", true);
+ Assert.ok(!updatingDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(updatingDir.path));
+
+ if (stageDir.exists()) {
+ updatingDir = stageDir.clone();
+ updatingDir.append("updating");
+ Assert.ok(!updatingDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(updatingDir.path));
+ }
+
+ debugDump("testing backup files should not be left behind in the " +
+ "application directory");
+ let applyToDir = getApplyDirFile(null, true);
+ checkFilesInDirRecursive(applyToDir, checkForBackupFiles);
+
+ if (stageDir.exists()) {
+ debugDump("testing backup files should not be left behind in the " +
+ "staging directory");
+ applyToDir = getApplyDirFile(null, true);
+ checkFilesInDirRecursive(stageDir, checkForBackupFiles);
+ }
+}
+
+/**
+ * Helper function for updater binary tests for verifying the contents of the
+ * updater callback application log which should contain the arguments passed to
+ * the callback application.
+ */
+function checkCallbackLog() {
+ let appLaunchLog = getApplyDirFile(DIR_RESOURCES + gCallbackArgs[1], true);
+ if (!appLaunchLog.exists()) {
+ // Uses do_timeout instead of do_execute_soon to lessen log spew.
+ do_timeout(FILE_IN_USE_TIMEOUT_MS, checkCallbackLog);
+ return;
+ }
+
+ let expectedLogContents = gCallbackArgs.join("\n") + "\n";
+ let logContents = readFile(appLaunchLog);
+ // 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 after gTimeoutRuns is greater than MAX_TIMEOUT_RUNS or
+ // the test harness times out the test.
+ if (logContents != expectedLogContents) {
+ gTimeoutRuns++;
+ if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
+ logTestInfo("callback log contents are not correct");
+ // This file doesn't contain full paths so there is no need to call
+ // replaceLogPaths.
+ let aryLog = logContents.split("\n");
+ let aryCompare = expectedLogContents.split("\n");
+ // Pushing an empty string to both arrays makes it so either array's length
+ // can be used in the for loop below without going out of bounds.
+ aryLog.push("");
+ aryCompare.push("");
+ // xpcshell tests won't display the entire contents so log the incorrect
+ // line.
+ for (let i = 0; i < aryLog.length; ++i) {
+ if (aryLog[i] != aryCompare[i]) {
+ logTestInfo("the first incorrect line in the callback log is: " +
+ aryLog[i]);
+ Assert.equal(aryLog[i], aryCompare[i],
+ "the callback log contents" + MSG_SHOULD_EQUAL);
+ }
+ }
+ // This should never happen!
+ do_throw("Unable to find incorrect callback log contents!");
+ }
+ // Uses do_timeout instead of do_execute_soon to lessen log spew.
+ do_timeout(FILE_IN_USE_TIMEOUT_MS, checkCallbackLog);
+ return;
+ }
+ Assert.ok(true, "the callback log contents" + MSG_SHOULD_EQUAL);
+
+ 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(DIR_RESOURCES + gPostUpdateBinFile + aSuffix, true);
+}
+
+/**
+ * Checks the contents of the updater post update binary log. When completed
+ * checkPostUpdateAppLogFinished will be called.
+ */
+function checkPostUpdateAppLog() {
+ // Only Mac OS X and Windows support post update.
+ if (IS_MACOSX || IS_WIN) {
+ gTimeoutRuns++;
+ let postUpdateLog = getPostUpdateFile(".log");
+ if (!postUpdateLog.exists()) {
+ debugDump("postUpdateLog does not exist. Path: " + postUpdateLog.path);
+ 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_execute_soon(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 after gTimeoutRuns is greater than MAX_TIMEOUT_RUNS or
+ // the test harness times out the test.
+ 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_execute_soon(checkPostUpdateAppLog);
+ return;
+ }
+ Assert.ok(true, "the post update log contents" + MSG_SHOULD_EQUAL);
+ }
+
+ do_execute_soon(checkPostUpdateAppLogFinished);
+}
+
+/**
+ * Helper function to check if a file is in use on Windows by making a copy of
+ * a file and attempting to delete the original file. If the deletion is
+ * successful the copy of the original file is renamed to the original file's
+ * name and if the deletion is not successful the copy of the original file is
+ * deleted.
+ *
+ * @param aFile
+ * An nsIFile for the file to be checked if it is in use.
+ * @return true if the file can't be deleted and false otherwise.
+ */
+function isFileInUse(aFile) {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ if (!aFile.exists()) {
+ debugDump("file does not exist, path: " + aFile.path);
+ return false;
+ }
+
+ let fileBak = aFile.parent;
+ fileBak.append(aFile.leafName + ".bak");
+ try {
+ if (fileBak.exists()) {
+ fileBak.remove(false);
+ }
+ aFile.copyTo(aFile.parent, fileBak.leafName);
+ aFile.remove(false);
+ fileBak.moveTo(aFile.parent, aFile.leafName);
+ debugDump("file is not in use, path: " + aFile.path);
+ return false;
+ } catch (e) {
+ debugDump("file in use, path: " + aFile.path + ", exception: " + e);
+ try {
+ if (fileBak.exists()) {
+ fileBak.remove(false);
+ }
+ } catch (ex) {
+ logTestInfo("unable to remove backup file, path: " +
+ fileBak.path + ", exception: " + ex);
+ }
+ }
+ return true;
+}
+
+/**
+ * Waits until files that are in use that break tests are no longer in use and
+ * then calls doTestFinish to end the test.
+ */
+function waitForFilesInUse() {
+ if (IS_WIN) {
+ let fileNames = [FILE_APP_BIN, FILE_UPDATER_BIN,
+ FILE_MAINTENANCE_SERVICE_INSTALLER_BIN];
+ for (let i = 0; i < fileNames.length; ++i) {
+ let file = getApplyDirFile(fileNames[i], true);
+ if (isFileInUse(file)) {
+ do_timeout(FILE_IN_USE_TIMEOUT_MS, waitForFilesInUse);
+ return;
+ }
+ }
+ }
+
+ debugDump("calling doTestFinish");
+ doTestFinish();
+}
+
+/**
+ * Helper function for updater binary tests for verifying there are no update
+ * backup files left behind after an update.
+ *
+ * @param aFile
+ * An nsIFile to check if it has moz-backup for its extension.
+ */
+function checkForBackupFiles(aFile) {
+ Assert.notEqual(getFileExtension(aFile), "moz-backup",
+ "the file's extension should not equal moz-backup" +
+ getMsgPath(aFile.path));
+}
+
+/**
+ * Helper function for updater binary tests for recursively enumerating a
+ * directory and calling a callback function with the file as a parameter for
+ * each file found.
+ *
+ * @param aDir
+ * A nsIFile for the directory to be deleted
+ * @param aCallback
+ * A callback function that will be called with the file as a
+ * parameter for each file found.
+ */
+function checkFilesInDirRecursive(aDir, aCallback) {
+ if (!aDir.exists()) {
+ do_throw("Directory must exist!");
+ }
+
+ let dirEntries = aDir.directoryEntries;
+ while (dirEntries.hasMoreElements()) {
+ let entry = dirEntries.getNext().QueryInterface(Ci.nsIFile);
+
+ if (entry.exists()) {
+ if (entry.isDirectory()) {
+ checkFilesInDirRecursive(entry, aCallback);
+ } else {
+ aCallback(entry);
+ }
+ }
+ }
+}
+
+
+/**
+ * Helper function to override the update prompt component to verify whether it
+ * is called or not.
+ *
+ * @param aCallback
+ * The callback to call if the update prompt component is called.
+ */
+function overrideUpdatePrompt(aCallback) {
+ Cu.import("resource://testing-common/MockRegistrar.jsm");
+ MockRegistrar.register("@mozilla.org/updates/update-prompt;1", UpdatePrompt, [aCallback]);
+}
+
+function UpdatePrompt(aCallback) {
+ this._callback = aCallback;
+
+ let fns = ["checkForUpdates", "showUpdateAvailable", "showUpdateDownloaded",
+ "showUpdateError", "showUpdateHistory", "showUpdateInstalled"];
+
+ fns.forEach(function UP_fns(aPromptFn) {
+ UpdatePrompt.prototype[aPromptFn] = function() {
+ if (!this._callback) {
+ return;
+ }
+
+ let callback = this._callback[aPromptFn];
+ if (!callback) {
+ return;
+ }
+
+ callback.apply(this._callback,
+ Array.prototype.slice.call(arguments));
+ };
+ });
+}
+
+UpdatePrompt.prototype = {
+ flags: Ci.nsIClassInfo.SINGLETON,
+ getScriptableHelper: () => null,
+ getInterfaces: function(aCount) {
+ let interfaces = [Ci.nsISupports, Ci.nsIUpdatePrompt];
+ aCount.value = interfaces.length;
+ return interfaces;
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIClassInfo, Ci.nsIUpdatePrompt])
+};
+
+/* Update check listener */
+const updateCheckListener = {
+ onProgress: function UCL_onProgress(aRequest, aPosition, aTotalSize) {
+ },
+
+ onCheckComplete: function UCL_onCheckComplete(aRequest, aUpdates, aUpdateCount) {
+ gRequestURL = aRequest.channel.originalURI.spec;
+ gUpdateCount = aUpdateCount;
+ gUpdates = aUpdates;
+ debugDump("url = " + gRequestURL + ", " +
+ "request.status = " + aRequest.status + ", " +
+ "updateCount = " + aUpdateCount);
+ // Use a timeout to allow the XHR to complete
+ do_execute_soon(gCheckFunc);
+ },
+
+ onError: function UCL_onError(aRequest, aUpdate) {
+ gRequestURL = aRequest.channel.originalURI.spec;
+ gStatusCode = aRequest.status;
+ if (gStatusCode == 0) {
+ gStatusCode = aRequest.channel.QueryInterface(Ci.nsIRequest).status;
+ }
+ gStatusText = aUpdate.statusText ? aUpdate.statusText : null;
+ debugDump("url = " + gRequestURL + ", " +
+ "request.status = " + gStatusCode + ", " +
+ "update.statusText = " + gStatusText);
+ // Use a timeout to allow the XHR to complete
+ do_execute_soon(gCheckFunc.bind(null, aRequest, aUpdate));
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateCheckListener])
+};
+
+/* Update download listener - nsIRequestObserver */
+const downloadListener = {
+ onStartRequest: function DL_onStartRequest(aRequest, aContext) {
+ },
+
+ onProgress: function DL_onProgress(aRequest, aContext, aProgress, aMaxProgress) {
+ },
+
+ onStatus: function DL_onStatus(aRequest, aContext, aStatus, aStatusText) {
+ },
+
+ onStopRequest: function DL_onStopRequest(aRequest, aContext, aStatus) {
+ gStatusResult = aStatus;
+ // Use a timeout to allow the request to complete
+ do_execute_soon(gCheckFunc);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver,
+ Ci.nsIProgressEventSink])
+};
+
+/**
+ * Helper for starting the http server used by the tests
+ */
+function start_httpserver() {
+ let dir = getTestDirFile();
+ debugDump("http server directory path: " + dir.path);
+
+ if (!dir.isDirectory()) {
+ do_throw("A file instead of a directory was specified for HttpServer " +
+ "registerDirectory! Path: " + dir.path);
+ }
+
+ let { HttpServer } = Cu.import("resource://testing-common/httpd.js", {});
+ gTestserver = new HttpServer();
+ gTestserver.registerDirectory("/", dir);
+ gTestserver.registerPathHandler("/" + gHTTPHandlerPath, pathHandler);
+ gTestserver.start(-1);
+ let testserverPort = gTestserver.identity.primaryPort;
+ gURLData = URL_HOST + ":" + testserverPort + "/";
+ debugDump("http server port = " + testserverPort);
+}
+
+/**
+ * Custom path handler for the http server
+ *
+ * @param aMetadata
+ * The http metadata for the request.
+ * @param aResponse
+ * The http response for the request.
+ */
+function pathHandler(aMetadata, aResponse) {
+ aResponse.setHeader("Content-Type", "text/xml", false);
+ aResponse.setStatusLine(aMetadata.httpVersion, gResponseStatusCode, "OK");
+ aResponse.bodyOutputStream.write(gResponseBody, gResponseBody.length);
+}
+
+/**
+ * Helper for stopping the http server used by the tests
+ *
+ * @param aCallback
+ * The callback to call after stopping the http server.
+ */
+function stop_httpserver(aCallback) {
+ Assert.ok(!!aCallback, "the aCallback parameter should be defined");
+ gTestserver.stop(aCallback);
+}
+
+/**
+ * Creates an nsIXULAppInfo
+ *
+ * @param aID
+ * The ID of the test application
+ * @param aName
+ * A name for the test application
+ * @param aVersion
+ * The version of the application
+ * @param aPlatformVersion
+ * The gecko version of the application
+ */
+function createAppInfo(aID, aName, aVersion, aPlatformVersion) {
+ const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
+ const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
+ let ifaces = [Ci.nsIXULAppInfo, Ci.nsIXULRuntime];
+ if (IS_WIN) {
+ ifaces.push(Ci.nsIWinAppHelper);
+ }
+ const XULAppInfo = {
+ vendor: APP_INFO_VENDOR,
+ name: aName,
+ ID: aID,
+ version: aVersion,
+ appBuildID: "2007010101",
+ platformVersion: aPlatformVersion,
+ platformBuildID: "2007010101",
+ inSafeMode: false,
+ logConsoleErrors: true,
+ OS: "XPCShell",
+ XPCOMABI: "noarch-spidermonkey",
+
+ QueryInterface: XPCOMUtils.generateQI(ifaces)
+ };
+
+ const XULAppInfoFactory = {
+ createInstance: function(aOuter, aIID) {
+ if (aOuter == null) {
+ return XULAppInfo.QueryInterface(aIID);
+ }
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ }
+ };
+
+ let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
+ registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
+ XULAPPINFO_CONTRACTID, XULAppInfoFactory);
+}
+
+/**
+ * Returns the platform specific arguments used by nsIProcess when launching
+ * the application.
+ *
+ * @param aExtraArgs (optional)
+ * An array of extra arguments to append to the default arguments.
+ * @return an array of arguments to be passed to nsIProcess.
+ *
+ * Note: a shell is necessary to pipe the application's console output which
+ * would otherwise pollute the xpcshell log.
+ *
+ * Command line arguments used when launching the application:
+ * -no-remote prevents shell integration from being affected by an existing
+ * application process.
+ * -test-process-updates makes the application exit after being relaunched by
+ * the updater.
+ * the platform specific string defined by PIPE_TO_NULL to output both stdout
+ * and stderr to null. This is needed to prevent output from the application
+ * from ending up in the xpchsell log.
+ */
+function getProcessArgs(aExtraArgs) {
+ if (!aExtraArgs) {
+ aExtraArgs = [];
+ }
+
+ let appBinPath = getApplyDirFile(DIR_MACOS + FILE_APP_BIN, false).path;
+ if (/ /.test(appBinPath)) {
+ appBinPath = '"' + appBinPath + '"';
+ }
+
+ let args;
+ if (IS_UNIX) {
+ let launchScript = getLaunchScript();
+ // Precreate the script with executable permissions
+ launchScript.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
+
+ let scriptContents = "#! /bin/sh\n";
+ scriptContents += appBinPath + " -no-remote -test-process-updates " +
+ aExtraArgs.join(" ") + " " + PIPE_TO_NULL;
+ writeFile(launchScript, scriptContents);
+ debugDump("created " + launchScript.path + " containing:\n" +
+ scriptContents);
+ args = [launchScript.path];
+ } else {
+ args = ["/D", "/Q", "/C", appBinPath, "-no-remote", "-test-process-updates"].
+ concat(aExtraArgs).concat([PIPE_TO_NULL]);
+ }
+ return args;
+}
+
+/**
+ * Gets a file path for the application to dump its arguments into. This is used
+ * to verify that a callback application is launched.
+ *
+ * @return the file for the application to dump its arguments into.
+ */
+function getAppArgsLogPath() {
+ let appArgsLog = do_get_file("/" + gTestID + "_app_args_log", true);
+ if (appArgsLog.exists()) {
+ appArgsLog.remove(false);
+ }
+ let appArgsLogPath = appArgsLog.path;
+ if (/ /.test(appArgsLogPath)) {
+ appArgsLogPath = '"' + appArgsLogPath + '"';
+ }
+ return appArgsLogPath;
+}
+
+/**
+ * Gets the nsIFile reference for the shell script to launch the application. If
+ * the file exists it will be removed by this function.
+ *
+ * @return the nsIFile for the shell script to launch the application.
+ */
+function getLaunchScript() {
+ let launchScript = do_get_file("/" + gTestID + "_launch.sh", true);
+ if (launchScript.exists()) {
+ launchScript.remove(false);
+ }
+ return launchScript;
+}
+
+/**
+ * Makes GreD, XREExeF, and UpdRootD point to unique file system locations so
+ * xpcshell tests can run in parallel and to keep the environment clean.
+ */
+function adjustGeneralPaths() {
+ let dirProvider = {
+ getFile: function AGP_DP_getFile(aProp, aPersistent) {
+ aPersistent.value = true;
+ switch (aProp) {
+ case NS_GRE_DIR:
+ if (gUseTestAppDir) {
+ return getApplyDirFile(DIR_RESOURCES, true);
+ }
+ break;
+ case NS_GRE_BIN_DIR:
+ if (gUseTestAppDir) {
+ return getApplyDirFile(DIR_MACOS, true);
+ }
+ break;
+ case XRE_EXECUTABLE_FILE:
+ if (gUseTestAppDir) {
+ return getApplyDirFile(DIR_MACOS + FILE_APP_BIN, true);
+ }
+ break;
+ case XRE_UPDATE_ROOT_DIR:
+ return getMockUpdRootD();
+ }
+ return null;
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider])
+ };
+ let ds = Services.dirsvc.QueryInterface(Ci.nsIDirectoryService);
+ ds.QueryInterface(Ci.nsIProperties).undefine(NS_GRE_DIR);
+ ds.QueryInterface(Ci.nsIProperties).undefine(NS_GRE_BIN_DIR);
+ ds.QueryInterface(Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE);
+ ds.registerProvider(dirProvider);
+ do_register_cleanup(function AGP_cleanup() {
+ debugDump("start - unregistering directory provider");
+
+ if (gAppTimer) {
+ debugDump("start - cancel app timer");
+ gAppTimer.cancel();
+ gAppTimer = null;
+ debugDump("finish - cancel app timer");
+ }
+
+ if (gProcess && gProcess.isRunning) {
+ debugDump("start - kill process");
+ try {
+ gProcess.kill();
+ } catch (e) {
+ debugDump("kill process failed. Exception: " + e);
+ }
+ gProcess = null;
+ debugDump("finish - kill process");
+ }
+
+ if (gHandle) {
+ try {
+ debugDump("start - closing handle");
+ let kernel32 = ctypes.open("kernel32");
+ let CloseHandle = kernel32.declare("CloseHandle", ctypes.default_abi,
+ ctypes.bool, /* return*/
+ ctypes.voidptr_t /* handle*/);
+ if (!CloseHandle(gHandle)) {
+ debugDump("call to CloseHandle failed");
+ }
+ kernel32.close();
+ gHandle = null;
+ debugDump("finish - closing handle");
+ } catch (e) {
+ debugDump("call to CloseHandle failed. Exception: " + e);
+ }
+ }
+
+ // Call end_test first before the directory provider is unregistered
+ if (typeof end_test == typeof Function) {
+ debugDump("calling end_test");
+ end_test();
+ }
+
+ ds.unregisterProvider(dirProvider);
+ cleanupTestCommon();
+
+ debugDump("finish - unregistering directory provider");
+ });
+}
+
+/**
+ * The timer callback to kill the process if it takes too long.
+ */
+const gAppTimerCallback = {
+ notify: function TC_notify(aTimer) {
+ gAppTimer = null;
+ if (gProcess.isRunning) {
+ logTestInfo("attempting to kill process");
+ gProcess.kill();
+ }
+ Assert.ok(false, "launch application timer expired");
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback])
+};
+
+/**
+ * Launches an application to apply an update.
+ */
+function runUpdateUsingApp(aExpectedStatus) {
+ /**
+ * The observer for the call to nsIProcess:runAsync. When completed
+ * runUpdateFinished will be called.
+ */
+ const processObserver = {
+ observe: function PO_observe(aSubject, aTopic, aData) {
+ debugDump("topic: " + aTopic + ", process exitValue: " +
+ gProcess.exitValue);
+ resetEnvironment();
+ if (gAppTimer) {
+ gAppTimer.cancel();
+ gAppTimer = null;
+ }
+ Assert.equal(gProcess.exitValue, 0,
+ "the application process exit value should be 0");
+ Assert.equal(aTopic, "process-finished",
+ "the application process observer topic should be " +
+ "process-finished");
+
+ if (IS_SERVICE_TEST) {
+ waitForServiceStop(false);
+ }
+
+ do_execute_soon(afterAppExits);
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
+ };
+
+ function afterAppExits() {
+ gTimeoutRuns++;
+
+ if (IS_WIN) {
+ waitForApplicationStop(FILE_UPDATER_BIN);
+ }
+
+ let status;
+ try {
+ status = readStatusFile();
+ } catch (e) {
+ logTestInfo("error reading status file, exception: " + e);
+ }
+ // Don't proceed until the update's status is the expected value.
+ if (status != aExpectedStatus) {
+ if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
+ logUpdateLog(FILE_UPDATE_LOG);
+ do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update " +
+ "status to equal: " +
+ aExpectedStatus +
+ ", current status: " + status);
+ } else {
+ do_timeout(FILE_IN_USE_TIMEOUT_MS, afterAppExits);
+ }
+ return;
+ }
+
+ // Don't check for an update log when the code in nsUpdateDriver.cpp skips
+ // updating.
+ if (aExpectedStatus != STATE_PENDING &&
+ aExpectedStatus != STATE_PENDING_SVC &&
+ aExpectedStatus != STATE_APPLIED &&
+ aExpectedStatus != STATE_APPLIED_SVC) {
+ // Don't proceed until the update log has been created.
+ let log = getUpdateLog(FILE_UPDATE_LOG);
+ if (!log.exists()) {
+ if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
+ do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the update " +
+ "log to be created. Path: " + log.path);
+ }
+ do_timeout(FILE_IN_USE_TIMEOUT_MS, afterAppExits);
+ return;
+ }
+ }
+
+ do_execute_soon(runUpdateFinished);
+ }
+
+ debugDump("start - launching application to apply update");
+
+ let appBin = getApplyDirFile(DIR_MACOS + FILE_APP_BIN, false);
+
+ let launchBin = getLaunchBin();
+ let args = getProcessArgs();
+ debugDump("launching " + launchBin.path + " " + args.join(" "));
+
+ gProcess = Cc["@mozilla.org/process/util;1"].
+ createInstance(Ci.nsIProcess);
+ gProcess.init(launchBin);
+
+ gAppTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ gAppTimer.initWithCallback(gAppTimerCallback, APP_TIMER_TIMEOUT,
+ Ci.nsITimer.TYPE_ONE_SHOT);
+
+ setEnvironment();
+ debugDump("launching application");
+ gProcess.runAsync(args, args.length, processObserver);
+
+ debugDump("finish - launching application to apply update");
+}
+
+/**
+ * Sets the environment that will be used by the application process when it is
+ * launched.
+ */
+function setEnvironment() {
+ // Prevent setting the environment more than once.
+ if (gShouldResetEnv !== undefined) {
+ return;
+ }
+
+ gShouldResetEnv = true;
+
+ // See bug 1279108.
+ if (gEnv.exists("ASAN_OPTIONS")) {
+ gASanOptions = gEnv.get("ASAN_OPTIONS");
+ gEnv.set("ASAN_OPTIONS", gASanOptions + ":detect_leaks=0");
+ } else {
+ gEnv.set("ASAN_OPTIONS", "detect_leaks=0");
+ }
+
+ if (IS_WIN && !gEnv.exists("XRE_NO_WINDOWS_CRASH_DIALOG")) {
+ gAddedEnvXRENoWindowsCrashDialog = true;
+ debugDump("setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
+ "variable to 1... previously it didn't exist");
+ gEnv.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1");
+ }
+
+ if (IS_UNIX) {
+ let appGreBinDir = gGREBinDirOrig.clone();
+ let envGreBinDir = Cc["@mozilla.org/file/local;1"].
+ createInstance(Ci.nsILocalFile);
+ let shouldSetEnv = true;
+ if (IS_MACOSX) {
+ if (gEnv.exists("DYLD_LIBRARY_PATH")) {
+ gEnvDyldLibraryPath = gEnv.get("DYLD_LIBRARY_PATH");
+ envGreBinDir.initWithPath(gEnvDyldLibraryPath);
+ if (envGreBinDir.path == appGreBinDir.path) {
+ gEnvDyldLibraryPath = null;
+ shouldSetEnv = false;
+ }
+ }
+
+ if (shouldSetEnv) {
+ debugDump("setting DYLD_LIBRARY_PATH environment variable value to " +
+ appGreBinDir.path);
+ gEnv.set("DYLD_LIBRARY_PATH", appGreBinDir.path);
+ }
+ } else {
+ if (gEnv.exists("LD_LIBRARY_PATH")) {
+ gEnvLdLibraryPath = gEnv.get("LD_LIBRARY_PATH");
+ envGreBinDir.initWithPath(gEnvLdLibraryPath);
+ if (envGreBinDir.path == appGreBinDir.path) {
+ gEnvLdLibraryPath = null;
+ shouldSetEnv = false;
+ }
+ }
+
+ if (shouldSetEnv) {
+ debugDump("setting LD_LIBRARY_PATH environment variable value to " +
+ appGreBinDir.path);
+ gEnv.set("LD_LIBRARY_PATH", appGreBinDir.path);
+ }
+ }
+ }
+
+ if (gEnv.exists("XPCOM_MEM_LEAK_LOG")) {
+ gEnvXPCOMMemLeakLog = gEnv.get("XPCOM_MEM_LEAK_LOG");
+ debugDump("removing the XPCOM_MEM_LEAK_LOG environment variable... " +
+ "previous value " + gEnvXPCOMMemLeakLog);
+ gEnv.set("XPCOM_MEM_LEAK_LOG", "");
+ }
+
+ if (gEnv.exists("XPCOM_DEBUG_BREAK")) {
+ gEnvXPCOMDebugBreak = gEnv.get("XPCOM_DEBUG_BREAK");
+ debugDump("setting the XPCOM_DEBUG_BREAK environment variable to " +
+ "warn... previous value " + gEnvXPCOMDebugBreak);
+ } else {
+ debugDump("setting the XPCOM_DEBUG_BREAK environment variable to " +
+ "warn... previously it didn't exist");
+ }
+
+ gEnv.set("XPCOM_DEBUG_BREAK", "warn");
+
+ if (IS_SERVICE_TEST) {
+ debugDump("setting MOZ_NO_SERVICE_FALLBACK environment variable to 1");
+ gEnv.set("MOZ_NO_SERVICE_FALLBACK", "1");
+ }
+}
+
+/**
+ * Sets the environment back to the original values after launching the
+ * application.
+ */
+function resetEnvironment() {
+ // Prevent resetting the environment more than once.
+ if (gShouldResetEnv !== true) {
+ return;
+ }
+
+ gShouldResetEnv = false;
+
+ // Restore previous ASAN_OPTIONS if there were any.
+ gEnv.set("ASAN_OPTIONS", gASanOptions ? gASanOptions : "");
+
+ if (gEnvXPCOMMemLeakLog) {
+ debugDump("setting the XPCOM_MEM_LEAK_LOG environment variable back to " +
+ gEnvXPCOMMemLeakLog);
+ gEnv.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog);
+ }
+
+ if (gEnvXPCOMDebugBreak) {
+ debugDump("setting the XPCOM_DEBUG_BREAK environment variable back to " +
+ gEnvXPCOMDebugBreak);
+ gEnv.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak);
+ } else if (gEnv.exists("XPCOM_DEBUG_BREAK")) {
+ debugDump("clearing the XPCOM_DEBUG_BREAK environment variable");
+ gEnv.set("XPCOM_DEBUG_BREAK", "");
+ }
+
+ if (IS_UNIX) {
+ if (IS_MACOSX) {
+ if (gEnvDyldLibraryPath) {
+ debugDump("setting DYLD_LIBRARY_PATH environment variable value " +
+ "back to " + gEnvDyldLibraryPath);
+ gEnv.set("DYLD_LIBRARY_PATH", gEnvDyldLibraryPath);
+ } else if (gEnvDyldLibraryPath !== null) {
+ debugDump("removing DYLD_LIBRARY_PATH environment variable");
+ gEnv.set("DYLD_LIBRARY_PATH", "");
+ }
+ } else if (gEnvLdLibraryPath) {
+ debugDump("setting LD_LIBRARY_PATH environment variable value back " +
+ "to " + gEnvLdLibraryPath);
+ gEnv.set("LD_LIBRARY_PATH", gEnvLdLibraryPath);
+ } else if (gEnvLdLibraryPath !== null) {
+ debugDump("removing LD_LIBRARY_PATH environment variable");
+ gEnv.set("LD_LIBRARY_PATH", "");
+ }
+ }
+
+ if (IS_WIN && gAddedEnvXRENoWindowsCrashDialog) {
+ debugDump("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
+ "variable");
+ gEnv.set("XRE_NO_WINDOWS_CRASH_DIALOG", "");
+ }
+
+ if (IS_SERVICE_TEST) {
+ debugDump("removing MOZ_NO_SERVICE_FALLBACK environment variable");
+ gEnv.set("MOZ_NO_SERVICE_FALLBACK", "");
+ }
+}
diff --git a/toolkit/mozapps/update/tests/moz.build b/toolkit/mozapps/update/tests/moz.build
new file mode 100644
index 000000000..842ec7f90
--- /dev/null
+++ b/toolkit/mozapps/update/tests/moz.build
@@ -0,0 +1,101 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+HAS_MISC_RULE = True
+
+FINAL_TARGET = '_tests/xpcshell/toolkit/mozapps/update/tests/data'
+
+MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
+
+XPCSHELL_TESTS_MANIFESTS += [
+ 'unit_aus_update/xpcshell.ini',
+ 'unit_base_updater/xpcshell.ini'
+]
+
+if CONFIG['MOZ_MAINTENANCE_SERVICE']:
+ XPCSHELL_TESTS_MANIFESTS += ['unit_service_updater/xpcshell.ini']
+
+SimplePrograms([
+ 'TestAUSHelper',
+ 'TestAUSReadStrings',
+])
+
+LOCAL_INCLUDES += [
+ '/toolkit/mozapps/update',
+ '/toolkit/mozapps/update/common',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ USE_LIBS += [
+ 'updatecommon-standalone',
+ ]
+
+ OS_LIBS += [
+ 'shlwapi',
+ ]
+else:
+ USE_LIBS += [
+ 'updatecommon',
+ ]
+
+for var in ('MOZ_APP_NAME', 'MOZ_APP_BASENAME', 'MOZ_APP_DISPLAYNAME',
+ 'MOZ_APP_VENDOR', 'BIN_SUFFIX', 'MOZ_DEBUG'):
+ DEFINES[var] = CONFIG[var]
+
+DEFINES['NS_NO_XPCOM'] = True
+
+if CONFIG['MOZ_MAINTENANCE_SERVICE']:
+ DEFINES['MOZ_MAINTENANCE_SERVICE'] = CONFIG['MOZ_MAINTENANCE_SERVICE']
+
+# For debugging purposes only
+#DEFINES['DISABLE_UPDATER_AUTHENTICODE_CHECK'] = True
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ DEFINES['UNICODE'] = True
+ DEFINES['_UNICODE'] = True
+ USE_STATIC_LIBS = True
+ if CONFIG['GNU_CC']:
+ WIN32_EXE_LDFLAGS += ['-municode']
+
+TEST_HARNESS_FILES.testing.mochitest.chrome.toolkit.mozapps.update.tests.data += [
+ 'data/shared.js',
+ 'data/sharedUpdateXML.js',
+ 'data/simple.mar',
+]
+
+FINAL_TARGET_FILES += [
+ 'data/complete.exe',
+ 'data/complete.mar',
+ 'data/complete.png',
+ 'data/complete_log_success_mac',
+ 'data/complete_log_success_win',
+ 'data/complete_mac.mar',
+ 'data/complete_precomplete',
+ 'data/complete_precomplete_mac',
+ 'data/complete_removed-files',
+ 'data/complete_removed-files_mac',
+ 'data/complete_update_manifest',
+ 'data/old_version.mar',
+ 'data/partial.exe',
+ 'data/partial.mar',
+ 'data/partial.png',
+ 'data/partial_log_failure_mac',
+ 'data/partial_log_failure_win',
+ 'data/partial_log_success_mac',
+ 'data/partial_log_success_win',
+ 'data/partial_mac.mar',
+ 'data/partial_precomplete',
+ 'data/partial_precomplete_mac',
+ 'data/partial_removed-files',
+ 'data/partial_removed-files_mac',
+ 'data/partial_update_manifest',
+ 'data/replace_log_success',
+ 'data/shared.js',
+ 'data/sharedUpdateXML.js',
+ 'data/simple.mar',
+ 'data/wrong_product_channel.mar',
+ 'data/xpcshellUtilsAUS.js',
+]
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/.eslintrc.js b/toolkit/mozapps/update/tests/unit_aus_update/.eslintrc.js
new file mode 100644
index 000000000..d35787cd2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/canCheckForAndCanApplyUpdates.js b/toolkit/mozapps/update/tests/unit_aus_update/canCheckForAndCanApplyUpdates.js
new file mode 100644
index 000000000..1985df959
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/canCheckForAndCanApplyUpdates.js
@@ -0,0 +1,138 @@
+/* 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/.
+ */
+
+function run_test() {
+ setupTestCommon();
+
+ // Verify write access to the custom app dir
+ debugDump("testing write access to the application directory");
+ let testFile = getCurrentProcessDir();
+ testFile.append("update_write_access_test");
+ testFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
+ Assert.ok(testFile.exists(), MSG_SHOULD_EXIST);
+ testFile.remove(false);
+ Assert.ok(!testFile.exists(), MSG_SHOULD_NOT_EXIST);
+
+ standardInit();
+
+ if (IS_WIN) {
+ // Create a mutex to prevent being able to check for or apply updates.
+ debugDump("attempting to create mutex");
+ let handle = createMutex(getPerInstallationMutexName());
+ Assert.ok(!!handle, "the update mutex should have been created");
+
+ // Check if available updates cannot be checked for when there is a mutex
+ // for this installation.
+ Assert.ok(!gAUS.canCheckForUpdates, "should not be able to check for " +
+ "updates when there is an update mutex");
+
+ // Check if updates cannot be applied when there is a mutex for this
+ // installation.
+ Assert.ok(!gAUS.canApplyUpdates, "should not be able to apply updates " +
+ "when there is an update mutex");
+
+ debugDump("destroying mutex");
+ closeHandle(handle);
+ }
+
+ // Check if available updates can be checked for
+ Assert.ok(gAUS.canCheckForUpdates, "should be able to check for updates");
+ // Check if updates can be applied
+ Assert.ok(gAUS.canApplyUpdates, "should be able to apply updates");
+
+ if (IS_WIN) {
+ // Attempt to create a mutex when application update has already created one
+ // with the same name.
+ debugDump("attempting to create mutex");
+ let handle = createMutex(getPerInstallationMutexName());
+
+ Assert.ok(!handle, "should not be able to create the update mutex when " +
+ "the application has created the update mutex");
+ }
+
+ doTestFinish();
+}
+
+/**
+ * Determines a unique mutex name for the installation.
+ *
+ * @return Global mutex path.
+ */
+function getPerInstallationMutexName() {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ let hasher = Cc["@mozilla.org/security/hash;1"].
+ createInstance(Ci.nsICryptoHash);
+ hasher.init(hasher.SHA1);
+
+ let exeFile = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsILocalFile);
+
+ let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+ createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+ let data = converter.convertToByteArray(exeFile.path.toLowerCase());
+
+ hasher.update(data, data.length);
+ return "Global\\MozillaUpdateMutex-" + hasher.finish(true);
+}
+
+/**
+ * Closes a Win32 handle.
+ *
+ * @param aHandle
+ * The handle to close.
+ */
+function closeHandle(aHandle) {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ let lib = ctypes.open("kernel32.dll");
+ let CloseHandle = lib.declare("CloseHandle",
+ ctypes.winapi_abi,
+ ctypes.int32_t, /* success */
+ ctypes.void_t.ptr); /* handle */
+ CloseHandle(aHandle);
+ lib.close();
+}
+
+/**
+ * Creates a mutex.
+ *
+ * @param aName
+ * The name for the mutex.
+ * @return The Win32 handle to the mutex.
+ */
+function createMutex(aName) {
+ if (!IS_WIN) {
+ do_throw("Windows only function called by a different platform!");
+ }
+
+ const INITIAL_OWN = 1;
+ const ERROR_ALREADY_EXISTS = 0xB7;
+ let lib = ctypes.open("kernel32.dll");
+ let CreateMutexW = lib.declare("CreateMutexW",
+ ctypes.winapi_abi,
+ ctypes.void_t.ptr, /* return handle */
+ ctypes.void_t.ptr, /* security attributes */
+ ctypes.int32_t, /* initial owner */
+ ctypes.char16_t.ptr); /* name */
+
+ let handle = CreateMutexW(null, INITIAL_OWN, aName);
+ lib.close();
+ let alreadyExists = ctypes.winLastError == ERROR_ALREADY_EXISTS;
+ if (handle && !handle.isNull() && alreadyExists) {
+ closeHandle(handle);
+ handle = null;
+ }
+
+ if (handle && handle.isNull()) {
+ handle = null;
+ }
+
+ return handle;
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js
new file mode 100644
index 000000000..a0a95af1b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForDifferentChannel.js
@@ -0,0 +1,46 @@
+/* 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/.
+ */
+
+/* General Update Manager Tests */
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing removal of an active update for a channel that is not" +
+ "valid due to switching channels (Bug 486275).");
+
+ let patches = getLocalPatchString(null, null, null, null, null, null,
+ STATE_DOWNLOADING);
+ let updates = getLocalUpdateString(patches, null, null, "version 1.0", "1.0");
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_DOWNLOADING);
+
+ patches = getLocalPatchString(null, null, null, null, null, null,
+ STATE_FAILED);
+ updates = getLocalUpdateString(patches, null, "Existing", "version 3.0",
+ "3.0", "3.0", null, null, null, null,
+ getString("patchApplyFailure"));
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false);
+
+ setUpdateChannel("original_channel");
+
+ standardInit();
+
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager update count" + MSG_SHOULD_EQUAL);
+ let update = gUpdateManager.getUpdateAt(0);
+ Assert.equal(update.name, "Existing",
+ "the update's name" + MSG_SHOULD_EQUAL);
+
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "there should not be an active update");
+ // Verify that the active-update.xml file has had the update from the old
+ // channel removed.
+ let file = getUpdatesXMLFile(true);
+ Assert.equal(readFile(file), getLocalUpdatesXMLString(""),
+ "the contents of active-update.xml" + MSG_SHOULD_EQUAL);
+
+ doTestFinish();
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js
new file mode 100644
index 000000000..fc4f09787
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForOlderAppVersion.js
@@ -0,0 +1,29 @@
+/* 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/.
+ */
+
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing cleanup of an update download in progress for an " +
+ "older version of the application on startup (Bug 485624)");
+
+ let patches = getLocalPatchString(null, null, null, null, null, null,
+ STATE_DOWNLOADING);
+ let updates = getLocalUpdateString(patches, null, null, "version 0.9", "0.9");
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_DOWNLOADING);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+
+ standardInit();
+
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "there should not be an active update");
+ Assert.equal(gUpdateManager.updateCount, 0,
+ "the update manager update count" + MSG_SHOULD_EQUAL);
+
+ doTestFinish();
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js
new file mode 100644
index 000000000..b2d8ecbc6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingForSameVersionAndBuildID.js
@@ -0,0 +1,30 @@
+/* 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/.
+ */
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing removal of an update download in progress for the " +
+ "same version of the application with the same application " +
+ "build id on startup (Bug 536547)");
+
+ let patches = getLocalPatchString(null, null, null, null, null, null,
+ STATE_DOWNLOADING);
+ let updates = getLocalUpdateString(patches, null, null, "version 1.0", "1.0",
+ "2007010101");
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_DOWNLOADING);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+
+ standardInit();
+
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "there should not be an active update");
+ Assert.equal(gUpdateManager.updateCount, 0,
+ "the update manager update count" + MSG_SHOULD_EQUAL);
+
+ doTestFinish();
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js
new file mode 100644
index 000000000..13e4aeaf6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupDownloadingIncorrectStatus.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing update cleanup when reading the status file returns " +
+ "STATUS_NONE and the update xml has an update with " +
+ "STATE_DOWNLOADING (Bug 539717).");
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ let patches = getLocalPatchString(null, null, null, null, null, null,
+ STATE_DOWNLOADING);
+ let updates = getLocalUpdateString(patches);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_NONE);
+
+ standardInit();
+
+ let dir = getUpdatesDir();
+ dir.append(DIR_PATCH);
+ Assert.ok(dir.exists(), MSG_SHOULD_EXIST);
+
+ let statusFile = dir.clone();
+ statusFile.append(FILE_UPDATE_STATUS);
+ Assert.ok(!statusFile.exists(), MSG_SHOULD_NOT_EXIST);
+
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "there should not be an active update");
+ Assert.equal(gUpdateManager.updateCount, 0,
+ "the update manager update count" + MSG_SHOULD_EQUAL);
+
+ doTestFinish();
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js
new file mode 100644
index 000000000..7661da82d
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupPendingVersionFileIncorrectStatus.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing update cleanup when reading the status file returns " +
+ "STATUS_NONE, the version file is for a newer version, and the " +
+ "update xml has an update with STATE_PENDING (Bug 601701).");
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ let patches = getLocalPatchString(null, null, null, null, null, null,
+ STATE_PENDING);
+ let updates = getLocalUpdateString(patches);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeVersionFile("99.9");
+
+ standardInit();
+
+ // Check that there is no activeUpdate first so the updates directory is
+ // cleaned up by the UpdateManager before the remaining tests.
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "there should not be an active update");
+ Assert.equal(gUpdateManager.updateCount, 0,
+ "the update manager update count" + MSG_SHOULD_EQUAL);
+
+ let dir = getUpdatesDir();
+ dir.append(DIR_PATCH);
+ Assert.ok(dir.exists(), MSG_SHOULD_EXIST);
+
+ let versionFile = dir.clone();
+ versionFile.append(FILE_UPDATE_VERSION);
+ Assert.ok(!versionFile.exists(), MSG_SHOULD_NOT_EXIST);
+
+ doTestFinish();
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupSuccessLogMove.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupSuccessLogMove.js
new file mode 100644
index 000000000..d683b9931
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupSuccessLogMove.js
@@ -0,0 +1,37 @@
+/* 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/.
+ */
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing that the update.log is moved after a successful update");
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ let patches = getLocalPatchString(null, null, null, null, null, null,
+ STATE_PENDING);
+ let updates = getLocalUpdateString(patches);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_SUCCEEDED);
+
+ let log = getUpdateLog(FILE_UPDATE_LOG);
+ writeFile(log, "Last Update Log");
+
+ standardInit();
+
+ Assert.ok(!log.exists(), MSG_SHOULD_NOT_EXIST);
+
+ log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ Assert.ok(log.exists(), MSG_SHOULD_EXIST);
+ Assert.equal(readFile(log), "Last Update Log",
+ "the last update log contents" + MSG_SHOULD_EQUAL);
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ Assert.ok(!log.exists(), MSG_SHOULD_NOT_EXIST);
+
+ let dir = getUpdatesPatchDir();
+ Assert.ok(dir.exists(), MSG_SHOULD_EXIST);
+
+ doTestFinish();
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/cleanupSuccessLogsFIFO.js b/toolkit/mozapps/update/tests/unit_aus_update/cleanupSuccessLogsFIFO.js
new file mode 100644
index 000000000..8be93d0ff
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/cleanupSuccessLogsFIFO.js
@@ -0,0 +1,45 @@
+/* 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/.
+ */
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing update logs are first in first out deleted");
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ let patches = getLocalPatchString(null, null, null, null, null, null,
+ STATE_PENDING);
+ let updates = getLocalUpdateString(patches);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_SUCCEEDED);
+
+ let log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ writeFile(log, "Backup Update Log");
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ writeFile(log, "To Be Deleted Backup Update Log");
+
+ log = getUpdateLog(FILE_UPDATE_LOG);
+ writeFile(log, "Last Update Log");
+
+ standardInit();
+
+ Assert.ok(!log.exists(), MSG_SHOULD_NOT_EXIST);
+
+ log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ Assert.ok(log.exists(), MSG_SHOULD_EXIST);
+ Assert.equal(readFile(log), "Last Update Log",
+ "the last update log contents" + MSG_SHOULD_EQUAL);
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ Assert.ok(log.exists(), MSG_SHOULD_EXIST);
+ Assert.equal(readFile(log), "Backup Update Log",
+ "the backup update log contents" + MSG_SHOULD_EQUAL);
+
+ let dir = getUpdatesPatchDir();
+ Assert.ok(dir.exists(), MSG_SHOULD_EXIST);
+
+ doTestFinish();
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js b/toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js
new file mode 100644
index 000000000..b715fb56e
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/downloadAndHashCheckMar.js
@@ -0,0 +1,161 @@
+/* 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/.
+ */
+
+var gNextRunFunc;
+var gExpectedStatusResult;
+
+function run_test() {
+ // The network code that downloads the mar file accesses the profile to cache
+ // the download, but the profile is only available after calling
+ // do_get_profile in xpcshell tests. This prevents an error from being logged.
+ do_get_profile();
+
+ setupTestCommon();
+
+ debugDump("testing mar download and mar hash verification");
+
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, false);
+ start_httpserver();
+ setUpdateURL(gURLData + gHTTPHandlerPath);
+ standardInit();
+ // Only perform the non hash check tests when mar signing is enabled since the
+ // update service doesn't perform hash checks when mar signing is enabled.
+ if (MOZ_VERIFY_MAR_SIGNATURE) {
+ do_execute_soon(run_test_pt11);
+ } else {
+ do_execute_soon(run_test_pt1);
+ }
+}
+
+// The HttpServer must be stopped before calling do_test_finished
+function finish_test() {
+ stop_httpserver(doTestFinish);
+}
+
+// Helper function for testing mar downloads that have the correct size
+// specified in the update xml.
+function run_test_helper_pt1(aMsg, aExpectedStatusResult, aNextRunFunc) {
+ gUpdates = null;
+ gUpdateCount = null;
+ gStatusResult = null;
+ gCheckFunc = check_test_helper_pt1_1;
+ gNextRunFunc = aNextRunFunc;
+ gExpectedStatusResult = aExpectedStatusResult;
+ debugDump(aMsg, Components.stack.caller);
+ gUpdateChecker.checkForUpdates(updateCheckListener, true);
+}
+
+function check_test_helper_pt1_1() {
+ Assert.equal(gUpdateCount, 1,
+ "the update count" + MSG_SHOULD_EQUAL);
+ gCheckFunc = check_test_helper_pt1_2;
+ let bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount);
+ let state = gAUS.downloadUpdate(bestUpdate, false);
+ if (state == STATE_NONE || state == STATE_FAILED) {
+ do_throw("nsIApplicationUpdateService:downloadUpdate returned " + state);
+ }
+ gAUS.addDownloadListener(downloadListener);
+}
+
+function check_test_helper_pt1_2() {
+ Assert.equal(gStatusResult, gExpectedStatusResult,
+ "the download status result" + MSG_SHOULD_EQUAL);
+ gAUS.removeDownloadListener(downloadListener);
+ gNextRunFunc();
+}
+
+function setResponseBody(aHashFunction, aHashValue, aSize) {
+ let patches = getRemotePatchString(null, null,
+ aHashFunction, aHashValue, aSize);
+ let updates = getRemoteUpdateString(patches);
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+}
+
+// mar download with a valid MD5 hash
+function run_test_pt1() {
+ setResponseBody("MD5", MD5_HASH_SIMPLE_MAR);
+ run_test_helper_pt1("mar download with a valid MD5 hash",
+ Cr.NS_OK, run_test_pt2);
+}
+
+// mar download with an invalid MD5 hash
+function run_test_pt2() {
+ setResponseBody("MD5", MD5_HASH_SIMPLE_MAR + "0");
+ run_test_helper_pt1("mar download with an invalid MD5 hash",
+ Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt3);
+}
+
+// mar download with a valid SHA1 hash
+function run_test_pt3() {
+ setResponseBody("SHA1", SHA1_HASH_SIMPLE_MAR);
+ run_test_helper_pt1("mar download with a valid SHA1 hash",
+ Cr.NS_OK, run_test_pt4);
+}
+
+// mar download with an invalid SHA1 hash
+function run_test_pt4() {
+ setResponseBody("SHA1", SHA1_HASH_SIMPLE_MAR + "0");
+ run_test_helper_pt1("mar download with an invalid SHA1 hash",
+ Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt5);
+}
+
+// mar download with a valid SHA256 hash
+function run_test_pt5() {
+ setResponseBody("SHA256", SHA256_HASH_SIMPLE_MAR);
+ run_test_helper_pt1("mar download with a valid SHA256 hash",
+ Cr.NS_OK, run_test_pt6);
+}
+
+// mar download with an invalid SHA256 hash
+function run_test_pt6() {
+ setResponseBody("SHA256", SHA256_HASH_SIMPLE_MAR + "0");
+ run_test_helper_pt1("mar download with an invalid SHA256 hash",
+ Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt7);
+}
+
+// mar download with a valid SHA384 hash
+function run_test_pt7() {
+ setResponseBody("SHA384", SHA384_HASH_SIMPLE_MAR);
+ run_test_helper_pt1("mar download with a valid SHA384 hash",
+ Cr.NS_OK, run_test_pt8);
+}
+
+// mar download with an invalid SHA384 hash
+function run_test_pt8() {
+ setResponseBody("SHA384", SHA384_HASH_SIMPLE_MAR + "0");
+ run_test_helper_pt1("mar download with an invalid SHA384 hash",
+ Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt9);
+}
+
+// mar download with a valid SHA512 hash
+function run_test_pt9() {
+ setResponseBody("SHA512", SHA512_HASH_SIMPLE_MAR);
+ run_test_helper_pt1("mar download with a valid SHA512 hash",
+ Cr.NS_OK, run_test_pt10);
+}
+
+// mar download with an invalid SHA512 hash
+function run_test_pt10() {
+ setResponseBody("SHA512", SHA512_HASH_SIMPLE_MAR + "0");
+ run_test_helper_pt1("mar download with an invalid SHA512 hash",
+ Cr.NS_ERROR_CORRUPTED_CONTENT, run_test_pt11);
+}
+
+// mar download with the mar not found
+function run_test_pt11() {
+ let patches = getRemotePatchString(null, gURLData + "missing.mar");
+ let updates = getRemoteUpdateString(patches);
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+ run_test_helper_pt1("mar download with the mar not found",
+ Cr.NS_ERROR_UNEXPECTED, run_test_pt12);
+}
+
+// mar download with a valid MD5 hash but invalid file size
+function run_test_pt12() {
+ const arbitraryFileSize = 1024000;
+ setResponseBody("MD5", MD5_HASH_SIMPLE_MAR, arbitraryFileSize);
+ run_test_helper_pt1("mar download with a valid MD5 hash but invalid file size",
+ Cr.NS_ERROR_UNEXPECTED, finish_test);
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/downloadCompleteAfterPartialFailure.js b/toolkit/mozapps/update/tests/unit_aus_update/downloadCompleteAfterPartialFailure.js
new file mode 100644
index 000000000..159033792
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/downloadCompleteAfterPartialFailure.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://testing-common/MockRegistrar.jsm");
+
+const WindowWatcher = {
+ getNewPrompter: function WW_getNewPrompter(aParent) {
+ Assert.ok(!aParent,
+ "the aParent parameter should not be defined");
+ return {
+ alert: function WW_GNP_alert(aTitle, aText) {
+ let title = getString("updaterIOErrorTitle");
+ Assert.equal(aTitle, title,
+ "the ui string for title" + MSG_SHOULD_EQUAL);
+ let text = gUpdateBundle.formatStringFromName("updaterIOErrorMsg",
+ [Services.appinfo.name,
+ Services.appinfo.name], 2);
+ Assert.equal(aText, text,
+ "the ui string for message" + MSG_SHOULD_EQUAL);
+
+ doTestFinish();
+ }
+ };
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher])
+};
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing download a complete on partial failure. Calling " +
+ "nsIUpdatePrompt::showUpdateError should call getNewPrompter " +
+ "and alert on the object returned by getNewPrompter when the " +
+ "update.state == " + STATE_FAILED + " and the update.errorCode " +
+ "== " + WRITE_ERROR + " (Bug 595059).");
+
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, false);
+
+ let windowWatcherCID =
+ MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
+ WindowWatcher);
+ do_register_cleanup(() => {
+ MockRegistrar.unregister(windowWatcherCID);
+ });
+
+ standardInit();
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ let url = URL_HOST + "/" + FILE_COMPLETE_MAR;
+ let patches = getLocalPatchString("complete", url, null, null, null, null,
+ STATE_FAILED);
+ let updates = getLocalUpdateString(patches, null, null, "version 1.0", "1.0",
+ null, null, null, null, url);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_FAILED);
+
+ reloadUpdateManagerData();
+
+ let update = gUpdateManager.activeUpdate;
+ update.errorCode = WRITE_ERROR;
+ let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
+ createInstance(Ci.nsIUpdatePrompt);
+ prompter.showUpdateError(update);
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/downloadInterruptedRecovery.js b/toolkit/mozapps/update/tests/unit_aus_update/downloadInterruptedRecovery.js
new file mode 100644
index 000000000..ef2da26af
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/downloadInterruptedRecovery.js
@@ -0,0 +1,225 @@
+/* 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/.
+ */
+
+/* General MAR File Download Tests */
+
+Components.utils.import("resource://testing-common/MockRegistrar.jsm");
+const INC_CONTRACT_ID = "@mozilla.org/network/incremental-download;1";
+
+// gIncrementalDownloadErrorType is used to loop through each of the connection
+// error types in the Mock incremental downloader.
+var gIncrementalDownloadErrorType = 0;
+
+var gNextRunFunc;
+var gExpectedStatusResult;
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing mar downloads, mar hash verification, and " +
+ "mar download interrupted recovery");
+
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, false);
+ start_httpserver();
+ setUpdateURL(gURLData + gHTTPHandlerPath);
+ standardInit();
+ do_execute_soon(run_test_pt1);
+}
+
+// The HttpServer must be stopped before calling do_test_finished
+function finish_test() {
+ stop_httpserver(doTestFinish);
+}
+
+// Helper function for testing mar downloads that have the correct size
+// specified in the update xml.
+function run_test_helper_pt1(aMsg, aExpectedStatusResult, aNextRunFunc) {
+ gUpdates = null;
+ gUpdateCount = null;
+ gStatusResult = null;
+ gCheckFunc = check_test_helper_pt1_1;
+ gNextRunFunc = aNextRunFunc;
+ gExpectedStatusResult = aExpectedStatusResult;
+ debugDump(aMsg, Components.stack.caller);
+ gUpdateChecker.checkForUpdates(updateCheckListener, true);
+}
+
+function check_test_helper_pt1_1() {
+ Assert.equal(gUpdateCount, 1,
+ "the update count" + MSG_SHOULD_EQUAL);
+ gCheckFunc = check_test_helper_pt1_2;
+ let bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount);
+ let state = gAUS.downloadUpdate(bestUpdate, false);
+ if (state == STATE_NONE || state == STATE_FAILED) {
+ do_throw("nsIApplicationUpdateService:downloadUpdate returned " + state);
+ }
+ gAUS.addDownloadListener(downloadListener);
+}
+
+function check_test_helper_pt1_2() {
+ Assert.equal(gStatusResult, gExpectedStatusResult,
+ "the download status result" + MSG_SHOULD_EQUAL);
+ gAUS.removeDownloadListener(downloadListener);
+ gNextRunFunc();
+}
+
+function setResponseBody(aHashFunction, aHashValue, aSize) {
+ let patches = getRemotePatchString(null, null,
+ aHashFunction, aHashValue, aSize);
+ let updates = getRemoteUpdateString(patches);
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+}
+
+function initMockIncrementalDownload() {
+ let incrementalDownloadCID =
+ MockRegistrar.register(INC_CONTRACT_ID, IncrementalDownload);
+ do_register_cleanup(() => {
+ MockRegistrar.unregister(incrementalDownloadCID);
+ });
+}
+
+/* This Mock incremental downloader is used to verify that connection
+ * interrupts work correctly in updater code. The implementation of
+ * the mock incremental downloader is very simple, it simply copies
+ * the file to the destination location.
+ */
+
+function IncrementalDownload() {
+ this.wrappedJSObject = this;
+}
+
+IncrementalDownload.prototype = {
+ /* nsIIncrementalDownload */
+ init: function(uri, file, chunkSize, intervalInSeconds) {
+ this._destination = file;
+ this._URI = uri;
+ this._finalURI = uri;
+ },
+
+ start: function(observer, ctxt) {
+ let tm = Cc["@mozilla.org/thread-manager;1"].
+ getService(Ci.nsIThreadManager);
+ // Do the actual operation async to give a chance for observers
+ // to add themselves.
+ tm.mainThread.dispatch(function() {
+ this._observer = observer.QueryInterface(Ci.nsIRequestObserver);
+ this._ctxt = ctxt;
+ this._observer.onStartRequest(this, this._ctxt);
+ let mar = getTestDirFile(FILE_SIMPLE_MAR);
+ mar.copyTo(this._destination.parent, this._destination.leafName);
+ let status = Cr.NS_OK;
+ switch (gIncrementalDownloadErrorType++) {
+ case 0:
+ status = Cr.NS_ERROR_NET_RESET;
+ break;
+ case 1:
+ status = Cr.NS_ERROR_CONNECTION_REFUSED;
+ break;
+ case 2:
+ status = Cr.NS_ERROR_NET_RESET;
+ break;
+ case 3:
+ status = Cr.NS_OK;
+ break;
+ case 4:
+ status = Cr.NS_ERROR_OFFLINE;
+ // After we report offline, we want to eventually show offline
+ // status being changed to online.
+ let tm2 = Cc["@mozilla.org/thread-manager;1"].
+ getService(Ci.nsIThreadManager);
+ tm2.mainThread.dispatch(function() {
+ Services.obs.notifyObservers(gAUS,
+ "network:offline-status-changed",
+ "online");
+ }, Ci.nsIThread.DISPATCH_NORMAL);
+ break;
+ }
+ this._observer.onStopRequest(this, this._ctxt, status);
+ }.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
+ },
+
+ get URI() {
+ return this._URI;
+ },
+
+ get currentSize() {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ get destination() {
+ return this._destination;
+ },
+
+ get finalURI() {
+ return this._finalURI;
+ },
+
+ get totalSize() {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ /* nsIRequest */
+ cancel: function(aStatus) {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+ suspend: function() {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+ isPending: function() {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+ _loadFlags: 0,
+ get loadFlags() {
+ return this._loadFlags;
+ },
+ set loadFlags(val) {
+ this._loadFlags = val;
+ },
+
+ _loadGroup: null,
+ get loadGroup() {
+ return this._loadGroup;
+ },
+ set loadGroup(val) {
+ this._loadGroup = val;
+ },
+
+ _name: "",
+ get name() {
+ return this._name;
+ },
+
+ _status: 0,
+ get status() {
+ return this._status;
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIIncrementalDownload])
+};
+
+// Test disconnecting during an update
+function run_test_pt1() {
+ initMockIncrementalDownload();
+ setResponseBody("MD5", MD5_HASH_SIMPLE_MAR);
+ run_test_helper_pt1("mar download with connection interruption",
+ Cr.NS_OK, run_test_pt2);
+}
+
+// Test disconnecting during an update
+function run_test_pt2() {
+ gIncrementalDownloadErrorType = 0;
+ Services.prefs.setIntPref(PREF_APP_UPDATE_SOCKET_MAXERRORS, 2);
+ Services.prefs.setIntPref(PREF_APP_UPDATE_RETRYTIMEOUT, 0);
+ setResponseBody("MD5", MD5_HASH_SIMPLE_MAR);
+ run_test_helper_pt1("mar download with connection interruption without recovery",
+ Cr.NS_ERROR_NET_RESET, run_test_pt3);
+}
+
+// Test entering offline mode while downloading
+function run_test_pt3() {
+ gIncrementalDownloadErrorType = 4;
+ setResponseBody("MD5", MD5_HASH_SIMPLE_MAR);
+ run_test_helper_pt1("mar download with offline mode",
+ Cr.NS_OK, finish_test);
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js b/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js
new file mode 100644
index 000000000..ca065f573
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/downloadResumeForSameAppVersion.js
@@ -0,0 +1,37 @@
+/* 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/.
+ */
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing resuming an update download in progress for the same " +
+ "version of the application on startup (Bug 485624)");
+
+ let patches = getLocalPatchString(null, null, null, null, null, null,
+ STATE_DOWNLOADING);
+ let updates = getLocalUpdateString(patches, null, null, "1.0", "1.0");
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_DOWNLOADING);
+
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+
+ standardInit();
+
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.activeUpdate.state, STATE_DOWNLOADING,
+ "the update manager activeUpdate state attribute" +
+ MSG_SHOULD_EQUAL);
+
+ // Pause the download and reload the Update Manager with an empty update so
+ // the Application Update Service doesn't write the update xml files during
+ // xpcom-shutdown which will leave behind the test directory.
+ gAUS.pauseDownload();
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), true);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ reloadUpdateManagerData();
+
+ do_execute_soon(doTestFinish);
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/head_update.js b/toolkit/mozapps/update/tests/unit_aus_update/head_update.js
new file mode 100644
index 000000000..9715c5828
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/head_update.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const IS_SERVICE_TEST = false;
+
+/* import-globals-from ../data/xpcshellUtilsAUS.js */
+load("../data/xpcshellUtilsAUS.js");
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/remoteUpdateXML.js b/toolkit/mozapps/update/tests/unit_aus_update/remoteUpdateXML.js
new file mode 100644
index 000000000..831c87257
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/remoteUpdateXML.js
@@ -0,0 +1,285 @@
+/* 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/.
+ */
+
+var gNextRunFunc;
+var gExpectedCount;
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing remote update xml attributes");
+
+ start_httpserver();
+ setUpdateURL(gURLData + gHTTPHandlerPath);
+ setUpdateChannel("test_channel");
+
+ // This test expects that the app.update.download.backgroundInterval
+ // preference doesn't already exist.
+ Services.prefs.deleteBranch("app.update.download.backgroundInterval");
+
+ standardInit();
+ do_execute_soon(run_test_pt01);
+}
+
+// Helper function for testing update counts returned from an update xml
+function run_test_helper_pt1(aMsg, aExpectedCount, aNextRunFunc) {
+ gUpdates = null;
+ gUpdateCount = null;
+ gCheckFunc = check_test_helper_pt1;
+ gNextRunFunc = aNextRunFunc;
+ gExpectedCount = aExpectedCount;
+ debugDump(aMsg, Components.stack.caller);
+ gUpdateChecker.checkForUpdates(updateCheckListener, true);
+}
+
+function check_test_helper_pt1() {
+ Assert.equal(gUpdateCount, gExpectedCount,
+ "the update count" + MSG_SHOULD_EQUAL);
+ gNextRunFunc();
+}
+
+// update xml not found
+function run_test_pt01() {
+ run_test_helper_pt1("testing update xml not available",
+ null, run_test_pt02);
+}
+
+// one update available and the update's property values
+function run_test_pt02() {
+ debugDump("testing one update available and the update's property values");
+ gUpdates = null;
+ gUpdateCount = null;
+ gCheckFunc = check_test_pt02;
+ let patches = getRemotePatchString("complete", "http://complete/", "SHA1",
+ "98db9dad8e1d80eda7e1170d0187d6f53e477059",
+ "9856459");
+ patches += getRemotePatchString("partial", "http://partial/", "SHA1",
+ "e6678ca40ae7582316acdeddf3c133c9c8577de4",
+ "1316138");
+ let updates = getRemoteUpdateString(patches, "minor", "Minor Test",
+ "version 2.1a1pre", "2.1a1pre",
+ "20080811053724",
+ "http://details/",
+ "true",
+ "true", "345600", "1200",
+ "custom1_attr=\"custom1 value\"",
+ "custom2_attr=\"custom2 value\"");
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+ gUpdateChecker.checkForUpdates(updateCheckListener, true);
+}
+
+function check_test_pt02() {
+ // XXXrstrong - not specifying a detailsURL will cause a leak due to bug 470244
+ // and until this is fixed this will not test the value for detailsURL when it
+ // isn't specified in the update xml.
+// let defaultDetailsURL;
+// try {
+ // Try using a default details URL supplied by the distribution
+ // if the update XML does not supply one.
+// let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
+// getService(Ci.nsIURLFormatter);
+// defaultDetailsURL = formatter.formatURLPref(PREF_APP_UPDATE_URL_DETAILS);
+// } catch (e) {
+// defaultDetailsURL = "";
+// }
+
+ Assert.equal(gUpdateCount, 1,
+ "the update count" + MSG_SHOULD_EQUAL);
+ let bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount).QueryInterface(Ci.nsIPropertyBag);
+ Assert.equal(bestUpdate.type, "minor",
+ "the update type attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.name, "Minor Test",
+ "the update name attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.displayVersion, "version 2.1a1pre",
+ "the update displayVersion attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.appVersion, "2.1a1pre",
+ "the update appVersion attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.buildID, "20080811053724",
+ "the update buildID attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.detailsURL, "http://details/",
+ "the update detailsURL attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(bestUpdate.showPrompt,
+ "the update showPrompt attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(bestUpdate.showNeverForVersion,
+ "the update showNeverForVersion attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.promptWaitTime, "345600",
+ "the update promptWaitTime attribute" + MSG_SHOULD_EQUAL);
+ // The default and maximum value for backgroundInterval is 600
+ Assert.equal(bestUpdate.getProperty("backgroundInterval"), "600",
+ "the update backgroundInterval attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.serviceURL, gURLData + gHTTPHandlerPath + "?force=1",
+ "the update serviceURL attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.channel, "test_channel",
+ "the update channel attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!bestUpdate.isCompleteUpdate,
+ "the update isCompleteUpdate attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!bestUpdate.isSecurityUpdate,
+ "the update isSecurityUpdate attribute" + MSG_SHOULD_EQUAL);
+ // Check that installDate is within 10 seconds of the current date.
+ Assert.ok((Date.now() - bestUpdate.installDate) < 10000,
+ "the update installDate attribute should be within 10 seconds of " +
+ "the current time");
+ Assert.ok(!bestUpdate.statusText,
+ "the update statusText attribute" + MSG_SHOULD_EQUAL);
+ // nsIUpdate:state returns an empty string when no action has been performed
+ // on an available update
+ Assert.equal(bestUpdate.state, "",
+ "the update state attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.errorCode, 0,
+ "the update errorCode attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.patchCount, 2,
+ "the update patchCount attribute" + MSG_SHOULD_EQUAL);
+ // XXX TODO - test nsIUpdate:serialize
+
+ Assert.equal(bestUpdate.getProperty("custom1_attr"), "custom1 value",
+ "the update custom1_attr property" + MSG_SHOULD_EQUAL);
+ Assert.equal(bestUpdate.getProperty("custom2_attr"), "custom2 value",
+ "the update custom2_attr property" + MSG_SHOULD_EQUAL);
+
+ let patch = bestUpdate.getPatchAt(0);
+ Assert.equal(patch.type, "complete",
+ "the update patch type attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.URL, "http://complete/",
+ "the update patch URL attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.hashFunction, "SHA1",
+ "the update patch hashFunction attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.hashValue, "98db9dad8e1d80eda7e1170d0187d6f53e477059",
+ "the update patch hashValue attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.size, 9856459,
+ "the update patch size attribute" + MSG_SHOULD_EQUAL);
+ // The value for patch.state can be the string 'null' as a valid value. This
+ // is confusing if it returns null which is an invalid value since the test
+ // failure output will show a failure for null == null. To lessen the
+ // confusion first check that the typeof for patch.state is string.
+ Assert.equal(typeof patch.state, "string",
+ "the update patch state typeof value should equal |string|");
+ Assert.equal(patch.state, STATE_NONE,
+ "the update patch state attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!patch.selected,
+ "the update patch selected attribute" + MSG_SHOULD_EQUAL);
+ // XXX TODO - test nsIUpdatePatch:serialize
+
+ patch = bestUpdate.getPatchAt(1);
+ Assert.equal(patch.type, "partial",
+ "the update patch type attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.URL, "http://partial/",
+ "the update patch URL attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.hashFunction, "SHA1",
+ "the update patch hashFunction attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.hashValue, "e6678ca40ae7582316acdeddf3c133c9c8577de4",
+ "the update patch hashValue attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.size, 1316138,
+ "the update patch size attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.state, STATE_NONE,
+ "the update patch state attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!patch.selected,
+ "the update patch selected attribute" + MSG_SHOULD_EQUAL);
+ // XXX TODO - test nsIUpdatePatch:serialize
+
+ run_test_pt03();
+}
+
+// Empty update xml (an empty xml file returns a root node name of parsererror)
+function run_test_pt03() {
+ gResponseBody = "<parsererror/>";
+ run_test_helper_pt1("testing empty update xml",
+ null, run_test_pt04);
+}
+
+// no updates available
+function run_test_pt04() {
+ gResponseBody = getRemoteUpdatesXMLString("");
+ run_test_helper_pt1("testing no updates available",
+ 0, run_test_pt05);
+}
+
+// one update available with two patches
+function run_test_pt05() {
+ let patches = getRemotePatchString("complete");
+ patches += getRemotePatchString("partial");
+ let updates = getRemoteUpdateString(patches);
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+ run_test_helper_pt1("testing one update available",
+ 1, run_test_pt06);
+}
+
+// three updates available each with two patches
+function run_test_pt06() {
+ let patches = getRemotePatchString("complete");
+ patches += getRemotePatchString("partial");
+ let updates = getRemoteUpdateString(patches);
+ updates += getRemoteUpdateString(patches);
+ updates += getRemoteUpdateString(patches);
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+ run_test_helper_pt1("testing three updates available",
+ 3, run_test_pt07);
+}
+
+// one update with complete and partial patches with size 0 specified in the
+// update xml
+function run_test_pt07() {
+ let patches = getRemotePatchString("complete", null, null, null, "0");
+ patches += getRemotePatchString("partial", null, null, null, "0");
+ let updates = getRemoteUpdateString(patches);
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+ run_test_helper_pt1("testing one update with complete and partial " +
+ "patches with size 0", 0, run_test_pt08);
+}
+
+// one update with complete patch with size 0 specified in the update xml
+function run_test_pt08() {
+ let patches = getRemotePatchString("complete", null, null, null, "0");
+ let updates = getRemoteUpdateString(patches);
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+ run_test_helper_pt1("testing one update with complete patch with size 0",
+ 0, run_test_pt9);
+}
+
+// one update with partial patch with size 0 specified in the update xml
+function run_test_pt9() {
+ let patches = getRemotePatchString("partial", null, null, null, "0");
+ let updates = getRemoteUpdateString(patches);
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+ run_test_helper_pt1("testing one update with partial patch with size 0",
+ 0, run_test_pt10);
+}
+
+// check that updates for older versions of the application aren't selected
+function run_test_pt10() {
+ let patches = getRemotePatchString("complete");
+ patches += getRemotePatchString("partial");
+ let updates = getRemoteUpdateString(patches, "minor", null, null, "1.0pre");
+ updates += getRemoteUpdateString(patches, "minor", null, null, "1.0a");
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+ run_test_helper_pt1("testing two updates older than the current version",
+ 2, check_test_pt10);
+}
+
+function check_test_pt10() {
+ let bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount);
+ Assert.ok(!bestUpdate,
+ "there should be no update available");
+ run_test_pt11();
+}
+
+// check that updates for the current version of the application are selected
+function run_test_pt11() {
+ let patches = getRemotePatchString("complete");
+ patches += getRemotePatchString("partial");
+ let updates = getRemoteUpdateString(patches, "minor", null, "version 1.0");
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+ run_test_helper_pt1("testing one update equal to the current version",
+ 1, check_test_pt11);
+}
+
+function check_test_pt11() {
+ let bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount);
+ Assert.ok(!!bestUpdate,
+ "there should be one update available");
+ Assert.equal(bestUpdate.displayVersion, "version 1.0",
+ "the update displayVersion attribute" + MSG_SHOULD_EQUAL);
+
+ doTestFinish();
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/uiAutoPref.js b/toolkit/mozapps/update/tests/unit_aus_update/uiAutoPref.js
new file mode 100644
index 000000000..ee1c40bfd
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/uiAutoPref.js
@@ -0,0 +1,75 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://testing-common/MockRegistrar.jsm");
+
+const WindowWatcher = {
+ openWindow: function(aParent, aUrl, aName, aFeatures, aArgs) {
+ gCheckFunc();
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher])
+};
+
+const WindowMediator = {
+ getMostRecentWindow: function(aWindowType) {
+ do_execute_soon(check_status);
+ return { getInterface: XPCOMUtils.generateQI([Ci.nsIDOMWindow]) };
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowMediator])
+};
+
+function run_test() {
+ setupTestCommon();
+ // Calling do_get_profile prevents an error from being logged
+ do_get_profile();
+
+ debugDump("testing that an update download doesn't start when the " +
+ PREF_APP_UPDATE_AUTO + " preference is false");
+
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_AUTO, false);
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, false);
+
+ start_httpserver();
+ setUpdateURL(gURLData + gHTTPHandlerPath);
+ standardInit();
+
+ let windowWatcherCID =
+ MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
+ WindowWatcher);
+ let windowMediatorCID =
+ MockRegistrar.register("@mozilla.org/appshell/window-mediator;1",
+ WindowMediator);
+ do_register_cleanup(() => {
+ MockRegistrar.unregister(windowWatcherCID);
+ MockRegistrar.unregister(windowMediatorCID);
+ });
+
+ gCheckFunc = check_showUpdateAvailable;
+ let patches = getRemotePatchString("complete");
+ let updates = getRemoteUpdateString(patches, "minor", null, null, "1.0");
+ gResponseBody = getRemoteUpdatesXMLString(updates);
+ gAUS.notify(null);
+}
+
+function check_status() {
+ let status = readStatusFile();
+ Assert.notEqual(status, STATE_DOWNLOADING,
+ "the update state" + MSG_SHOULD_EQUAL);
+
+ // Pause the download and reload the Update Manager with an empty update so
+ // the Application Update Service doesn't write the update xml files during
+ // xpcom-shutdown which will leave behind the test directory.
+ gAUS.pauseDownload();
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), true);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ reloadUpdateManagerData();
+
+ do_execute_soon(doTestFinish);
+}
+
+function check_showUpdateAvailable() {
+ do_throw("showUpdateAvailable should not have called openWindow!");
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/uiSilentPref.js b/toolkit/mozapps/update/tests/unit_aus_update/uiSilentPref.js
new file mode 100644
index 000000000..25110be8c
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/uiSilentPref.js
@@ -0,0 +1,76 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://testing-common/MockRegistrar.jsm");
+
+/**
+ * Test that nsIUpdatePrompt doesn't display UI for showUpdateAvailable and
+ * showUpdateError when the app.update.silent preference is true.
+ */
+
+const WindowWatcher = {
+ openWindow: function(aParent, aUrl, aName, aFeatures, aArgs) {
+ gCheckFunc();
+ },
+
+ getNewPrompter: function(aParent) {
+ gCheckFunc();
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher])
+};
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing nsIUpdatePrompt notifications should not be seen " +
+ "when the " + PREF_APP_UPDATE_SILENT + " preference is true");
+
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, true);
+
+ let windowWatcherCID =
+ MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
+ WindowWatcher);
+ do_register_cleanup(() => {
+ MockRegistrar.unregister(windowWatcherCID);
+ });
+
+ standardInit();
+
+ debugDump("testing showUpdateAvailable should not call openWindow");
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
+ let patches = getLocalPatchString(null, null, null, null, null, null,
+ STATE_FAILED);
+ let updates = getLocalUpdateString(patches);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_FAILED);
+ reloadUpdateManagerData();
+
+ gCheckFunc = check_showUpdateAvailable;
+ let update = gUpdateManager.activeUpdate;
+ gUP.showUpdateAvailable(update);
+ // Report a successful check after the call to showUpdateAvailable since it
+ // didn't throw and otherwise it would report no tests run.
+ Assert.ok(true,
+ "calling showUpdateAvailable should not attempt to open a window");
+
+ debugDump("testing showUpdateError should not call getNewPrompter");
+ gCheckFunc = check_showUpdateError;
+ update.errorCode = WRITE_ERROR;
+ gUP.showUpdateError(update);
+ // Report a successful check after the call to showUpdateError since it
+ // didn't throw and otherwise it would report no tests run.
+ Assert.ok(true,
+ "calling showUpdateError should not attempt to open a window");
+
+ doTestFinish();
+}
+
+function check_showUpdateAvailable() {
+ do_throw("showUpdateAvailable should not have called openWindow!");
+}
+
+function check_showUpdateError() {
+ do_throw("showUpdateError should not have seen getNewPrompter!");
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/uiUnsupportedAlreadyNotified.js b/toolkit/mozapps/update/tests/unit_aus_update/uiUnsupportedAlreadyNotified.js
new file mode 100644
index 000000000..5b694ed30
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/uiUnsupportedAlreadyNotified.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+
+const WindowWatcher = {
+ openWindow: function(aParent, aUrl, aName, aFeatures, aArgs) {
+ check_showUpdateAvailable();
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher])
+};
+
+const WindowMediator = {
+ getMostRecentWindow: function(aWindowType) {
+ return null;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowMediator])
+};
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing nsIUpdatePrompt notifications should not be displayed " +
+ "when showUpdateAvailable is called for an unsupported system " +
+ "update when the unsupported notification has already been " +
+ "shown (bug 843497)");
+
+ start_httpserver();
+ setUpdateURL(gURLData + gHTTPHandlerPath);
+ standardInit();
+
+ let windowWatcherCID =
+ MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
+ WindowWatcher);
+ let windowMediatorCID =
+ MockRegistrar.register("@mozilla.org/appshell/window-mediator;1",
+ WindowMediator);
+ do_register_cleanup(() => {
+ MockRegistrar.unregister(windowWatcherCID);
+ MockRegistrar.unregister(windowMediatorCID);
+ });
+
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, false);
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED, true);
+ // This preference is used to determine when the background update check has
+ // completed since a successful check will clear the preference.
+ Services.prefs.setIntPref(PREF_APP_UPDATE_BACKGROUNDERRORS, 1);
+
+ gResponseBody = getRemoteUpdatesXMLString(" <update type=\"major\" " +
+ "name=\"Unsupported Update\" " +
+ "unsupported=\"true\" " +
+ "detailsURL=\"" + URL_HOST +
+ "\"></update>\n");
+ gAUS.notify(null);
+ do_execute_soon(check_test);
+}
+
+function check_test() {
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS)) {
+ do_execute_soon(check_test);
+ return;
+ }
+ Assert.ok(true,
+ PREF_APP_UPDATE_BACKGROUNDERRORS + " preference should not exist");
+
+ stop_httpserver(doTestFinish);
+}
+
+function check_showUpdateAvailable() {
+ do_throw("showUpdateAvailable should not have called openWindow!");
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/updateManagerXML.js b/toolkit/mozapps/update/tests/unit_aus_update/updateManagerXML.js
new file mode 100644
index 000000000..e46469455
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/updateManagerXML.js
@@ -0,0 +1,177 @@
+/* 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/.
+ */
+
+function run_test() {
+ setupTestCommon();
+
+ debugDump("testing addition of a successful update to " + FILE_UPDATES_XML +
+ " and verification of update properties including the format " +
+ "prior to bug 530872");
+
+ setUpdateChannel("test_channel");
+
+ // This test expects that the app.update.download.backgroundInterval
+ // preference doesn't already exist.
+ Services.prefs.deleteBranch("app.update.download.backgroundInterval");
+
+ // XXXrstrong - not specifying a detailsURL will cause a leak due to bug 470244
+ // and until bug 470244 is fixed this will not test the value for detailsURL
+ // when it isn't specified in the update xml.
+ let patches = getLocalPatchString("partial", "http://partial/", "SHA256",
+ "cd43", "86", "true", STATE_PENDING);
+ let updates = getLocalUpdateString(patches, "major", "New", "version 4",
+ "4.0", "20070811053724",
+ "http://details1/",
+ "http://service1/", "1238441300314",
+ "test status text", "false",
+ "test_channel", "true", "true", "true",
+ "345600", "300", "3.0",
+ "custom1_attr=\"custom1 value\"",
+ "custom2_attr=\"custom2 value\"");
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ writeStatusFile(STATE_SUCCEEDED);
+
+ patches = getLocalPatchString("complete", "http://complete/", "SHA1", "6232",
+ "75", "true", STATE_FAILED);
+ updates = getLocalUpdateString(patches, "major", "Existing", null, "3.0",
+ null,
+ "http://details2/",
+ "http://service2/", null,
+ getString("patchApplyFailure"), "true",
+ "test_channel", "false", null, null, "691200",
+ null, null,
+ "custom3_attr=\"custom3 value\"",
+ "custom4_attr=\"custom4 value\"");
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), false);
+
+ standardInit();
+
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the update manager activeUpdate attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 2,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+
+ debugDump("checking the activeUpdate properties");
+ let update = gUpdateManager.getUpdateAt(0).QueryInterface(Ci.nsIPropertyBag);
+ Assert.equal(update.state, STATE_SUCCEEDED,
+ "the update state attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.type, "major",
+ "the update type attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.name, "New",
+ "the update name attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.displayVersion, "version 4",
+ "the update displayVersion attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.appVersion, "4.0",
+ "the update appVersion attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.buildID, "20070811053724",
+ "the update buildID attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.detailsURL, "http://details1/",
+ "the update detailsURL attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.serviceURL, "http://service1/",
+ "the update serviceURL attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.installDate, "1238441300314",
+ "the update installDate attribute" + MSG_SHOULD_EQUAL);
+ // statusText is updated
+ Assert.equal(update.statusText, getString("installSuccess"),
+ "the update statusText attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!update.isCompleteUpdate,
+ "the update isCompleteUpdate attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.channel, "test_channel",
+ "the update channel attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!!update.showPrompt,
+ "the update showPrompt attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!!update.showNeverForVersion,
+ "the update showNeverForVersion attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.promptWaitTime, "345600",
+ "the update promptWaitTime attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.getProperty("backgroundInterval"), "300",
+ "the update backgroundInterval attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.previousAppVersion, "3.0",
+ "the update previousAppVersion attribute" + MSG_SHOULD_EQUAL);
+ // Custom attributes
+ Assert.equal(update.getProperty("custom1_attr"), "custom1 value",
+ "the update custom1_attr property" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.getProperty("custom2_attr"), "custom2 value",
+ "the update custom2_attr property" + MSG_SHOULD_EQUAL);
+
+ debugDump("checking the activeUpdate patch properties");
+ let patch = update.selectedPatch;
+ Assert.equal(patch.type, "partial",
+ "the update patch type attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.URL, "http://partial/",
+ "the update patch URL attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.hashFunction, "SHA256",
+ "the update patch hashFunction attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.hashValue, "cd43",
+ "the update patch hashValue attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.size, "86",
+ "the update patch size attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!!patch.selected,
+ "the update patch selected attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.state, STATE_SUCCEEDED,
+ "the update patch state attribute" + MSG_SHOULD_EQUAL);
+
+ debugDump("checking the first update properties");
+ update = gUpdateManager.getUpdateAt(1).QueryInterface(Ci.nsIPropertyBag);
+ Assert.equal(update.state, STATE_FAILED,
+ "the update state attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.name, "Existing",
+ "the update name attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.type, "major",
+ "the update type attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.displayVersion, "3.0",
+ "the update displayVersion attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.appVersion, "3.0",
+ "the update appVersion attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.detailsURL, "http://details2/",
+ "the update detailsURL attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.serviceURL, "http://service2/",
+ "the update serviceURL attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.installDate, "1238441400314",
+ "the update installDate attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.statusText, getString("patchApplyFailure"),
+ "the update statusText attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.buildID, "20080811053724",
+ "the update buildID attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!!update.isCompleteUpdate,
+ "the update isCompleteUpdate attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.channel, "test_channel",
+ "the update channel attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!update.showPrompt,
+ "the update showPrompt attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!update.showNeverForVersion,
+ "the update showNeverForVersion attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.promptWaitTime, "691200",
+ "the update promptWaitTime attribute" + MSG_SHOULD_EQUAL);
+ // The default and maximum value for backgroundInterval is 600
+ Assert.equal(update.getProperty("backgroundInterval"), "600",
+ "the update backgroundInterval attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.previousAppVersion, null,
+ "the update previousAppVersion attribute" + MSG_SHOULD_EQUAL);
+ // Custom attributes
+ Assert.equal(update.getProperty("custom3_attr"), "custom3 value",
+ "the update custom3_attr property" + MSG_SHOULD_EQUAL);
+ Assert.equal(update.getProperty("custom4_attr"), "custom4 value",
+ "the update custom4_attr property" + MSG_SHOULD_EQUAL);
+
+ debugDump("checking the first update patch properties");
+ patch = update.selectedPatch;
+ Assert.equal(patch.type, "complete",
+ "the update patch type attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.URL, "http://complete/",
+ "the update patch URL attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.hashFunction, "SHA1",
+ "the update patch hashFunction attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.hashValue, "6232",
+ "the update patch hashValue attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.size, "75",
+ "the update patch size attribute" + MSG_SHOULD_EQUAL);
+ Assert.ok(!!patch.selected,
+ "the update patch selected attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(patch.state, STATE_FAILED,
+ "the update patch state attribute" + MSG_SHOULD_EQUAL);
+
+ doTestFinish();
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/urlConstruction.js b/toolkit/mozapps/update/tests/unit_aus_update/urlConstruction.js
new file mode 100644
index 000000000..db7d90094
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/urlConstruction.js
@@ -0,0 +1,305 @@
+/* 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/.
+ */
+
+/* General URL Construction Tests */
+
+const URL_PREFIX = URL_HOST + "/";
+
+var gAppInfo;
+
+// Since gUpdateChecker.checkForUpdates uses XMLHttpRequest and XMLHttpRequest
+// can be slow it combines the checks whenever possible.
+function run_test() {
+ // This test needs access to omni.ja to read the update.locale file so don't
+ // use a custom directory for the application directory.
+ gUseTestAppDir = false;
+ setupTestCommon();
+
+ standardInit();
+ gAppInfo = Cc["@mozilla.org/xre/app-info;1"].
+ getService(Ci.nsIXULAppInfo).
+ QueryInterface(Ci.nsIXULRuntime);
+ do_execute_soon(run_test_pt1);
+}
+
+
+// url constructed with:
+// %PRODUCT%
+// %VERSION%
+// %BUILD_ID%
+// %BUILD_TARGET%
+// %LOCALE%
+// %CHANNEL%
+// %PLATFORM_VERSION%
+// %OS_VERSION%
+// %SYSTEM_CAPABILITIES%
+// %DISTRIBUTION%
+// %DISTRIBUTION_VERSION%
+function run_test_pt1() {
+ gCheckFunc = check_test_pt1;
+ // The code that gets the locale accesses the profile which is only available
+ // after calling do_get_profile in xpcshell tests. This prevents an error from
+ // being logged.
+ do_get_profile();
+
+ setUpdateChannel("test_channel");
+ gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_ID, "test_distro");
+ gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_VERSION, "test_distro_version");
+
+ let url = URL_PREFIX + "%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/" +
+ "%LOCALE%/%CHANNEL%/%PLATFORM_VERSION%/%OS_VERSION%/" +
+ "%SYSTEM_CAPABILITIES%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/" +
+ "updates.xml";
+ debugDump("testing url construction - url: " + url);
+ setUpdateURL(url);
+ try {
+ gUpdateChecker.checkForUpdates(updateCheckListener, true);
+ } catch (e) {
+ debugDump("The following error is most likely due to a missing " +
+ "update.locale file");
+ do_throw(e);
+ }
+}
+
+function check_test_pt1() {
+ let url = URL_PREFIX + gAppInfo.name + "/" + gAppInfo.version + "/" +
+ gAppInfo.appBuildID + "/" + gAppInfo.OS + "_" + getABI() + "/" +
+ INSTALL_LOCALE + "/test_channel/" + gAppInfo.platformVersion + "/" +
+ getOSVersion() + "/" + getSystemCapabilities() +
+ "/test_distro/test_distro_version/updates.xml?force=1";
+ // Log the urls since Assert.equal won't print the entire urls to the log.
+ if (gRequestURL != url) {
+ logTestInfo("expected url: " + url);
+ logTestInfo("returned url: " + gRequestURL);
+ }
+ Assert.equal(gRequestURL, url,
+ "the url" + MSG_SHOULD_EQUAL);
+ run_test_pt2();
+}
+
+// url constructed with:
+// %CHANNEL% with distribution partners
+// %CUSTOM% parameter
+// force param when there already is a param - bug 454357
+function run_test_pt2() {
+ gCheckFunc = check_test_pt2;
+ let url = URL_PREFIX + "%CHANNEL%/updates.xml?custom=%CUSTOM%";
+ debugDump("testing url constructed with %CHANNEL% - " + url);
+ setUpdateURL(url);
+ gDefaultPrefBranch.setCharPref(PREFBRANCH_APP_PARTNER + "test_partner1",
+ "test_partner1");
+ gDefaultPrefBranch.setCharPref(PREFBRANCH_APP_PARTNER + "test_partner2",
+ "test_partner2");
+ Services.prefs.setCharPref("app.update.custom", "custom");
+ gUpdateChecker.checkForUpdates(updateCheckListener, true);
+}
+
+function check_test_pt2() {
+ let url = URL_PREFIX + "test_channel-cck-test_partner1-test_partner2/" +
+ "updates.xml?custom=custom&force=1";
+ Assert.equal(gRequestURL, url,
+ "the url" + MSG_SHOULD_EQUAL);
+ doTestFinish();
+}
+
+function getABI() {
+ let abi;
+ try {
+ abi = gAppInfo.XPCOMABI;
+ } catch (e) {
+ do_throw("nsIXULAppInfo:XPCOMABI not defined\n");
+ }
+
+ if (IS_MACOSX) {
+ // Mac universal build should report a different ABI than either macppc
+ // or mactel. This is necessary since nsUpdateService.js will set the ABI to
+ // Universal-gcc3 for Mac universal builds.
+ let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
+ getService(Ci.nsIMacUtils);
+
+ if (macutils.isUniversalBinary) {
+ abi += "-u-" + macutils.architecturesInBinary;
+ }
+ } else if (IS_WIN) {
+ // Windows build should report the CPU architecture that it's running on.
+ abi += "-" + getProcArchitecture();
+ }
+ return abi;
+}
+
+function getOSVersion() {
+ let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
+ let osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
+
+ if (IS_WIN) {
+ try {
+ let servicePack = getServicePack();
+ osVersion += "." + servicePack;
+ } catch (e) {
+ do_throw("Failure obtaining service pack: " + e);
+ }
+
+ if ("5.0" === sysInfo.getProperty("version")) { // Win2K
+ osVersion += " (unknown)";
+ } else {
+ try {
+ osVersion += " (" + getProcArchitecture() + ")";
+ } catch (e) {
+ do_throw("Failed to obtain processor architecture: " + e);
+ }
+ }
+ }
+
+ if (osVersion) {
+ try {
+ osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
+ } catch (e) {
+ // Not all platforms have a secondary widget library, so an error is
+ // nothing to worry about.
+ }
+ osVersion = encodeURIComponent(osVersion);
+ }
+ return osVersion;
+}
+
+function getServicePack() {
+ // NOTE: This function is a helper function and not a test. Thus,
+ // it uses throw() instead of do_throw(). Any tests that use this function
+ // should catch exceptions thrown in this function and deal with them
+ // appropriately (usually by calling do_throw).
+ const BYTE = ctypes.uint8_t;
+ const WORD = ctypes.uint16_t;
+ const DWORD = ctypes.uint32_t;
+ const WCHAR = ctypes.char16_t;
+ const BOOL = ctypes.int;
+
+ // This structure is described at:
+ // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
+ const SZCSDVERSIONLENGTH = 128;
+ const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
+ [
+ {dwOSVersionInfoSize: DWORD},
+ {dwMajorVersion: DWORD},
+ {dwMinorVersion: DWORD},
+ {dwBuildNumber: DWORD},
+ {dwPlatformId: DWORD},
+ {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
+ {wServicePackMajor: WORD},
+ {wServicePackMinor: WORD},
+ {wSuiteMask: WORD},
+ {wProductType: BYTE},
+ {wReserved: BYTE}
+ ]);
+
+ let kernel32 = ctypes.open("kernel32");
+ try {
+ let GetVersionEx = kernel32.declare("GetVersionExW",
+ ctypes.default_abi,
+ BOOL,
+ OSVERSIONINFOEXW.ptr);
+ let winVer = OSVERSIONINFOEXW();
+ winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
+
+ if (0 === GetVersionEx(winVer.address())) {
+ // Using "throw" instead of "do_throw" (see NOTE above)
+ throw ("Failure in GetVersionEx (returned 0)");
+ }
+
+ return winVer.wServicePackMajor + "." + winVer.wServicePackMinor;
+ } finally {
+ kernel32.close();
+ }
+}
+
+function getProcArchitecture() {
+ // NOTE: This function is a helper function and not a test. Thus,
+ // it uses throw() instead of do_throw(). Any tests that use this function
+ // should catch exceptions thrown in this function and deal with them
+ // appropriately (usually by calling do_throw).
+ const WORD = ctypes.uint16_t;
+ const DWORD = ctypes.uint32_t;
+
+ // This structure is described at:
+ // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
+ const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
+ [
+ {wProcessorArchitecture: WORD},
+ {wReserved: WORD},
+ {dwPageSize: DWORD},
+ {lpMinimumApplicationAddress: ctypes.voidptr_t},
+ {lpMaximumApplicationAddress: ctypes.voidptr_t},
+ {dwActiveProcessorMask: DWORD.ptr},
+ {dwNumberOfProcessors: DWORD},
+ {dwProcessorType: DWORD},
+ {dwAllocationGranularity: DWORD},
+ {wProcessorLevel: WORD},
+ {wProcessorRevision: WORD}
+ ]);
+
+ let kernel32 = ctypes.open("kernel32");
+ try {
+ let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
+ ctypes.default_abi,
+ ctypes.void_t,
+ SYSTEM_INFO.ptr);
+ let sysInfo = SYSTEM_INFO();
+ // Default to unknown
+ sysInfo.wProcessorArchitecture = 0xffff;
+
+ GetNativeSystemInfo(sysInfo.address());
+ switch (sysInfo.wProcessorArchitecture) {
+ case 9:
+ return "x64";
+ case 6:
+ return "IA64";
+ case 0:
+ return "x86";
+ default:
+ // Using "throw" instead of "do_throw" (see NOTE above)
+ throw ("Unknown architecture returned from GetNativeSystemInfo: " + sysInfo.wProcessorArchitecture);
+ }
+ } finally {
+ kernel32.close();
+ }
+}
+
+/**
+ * Provides system capability information for application update though it may
+ * be used by other consumers.
+ */
+function getSystemCapabilities() {
+ if (IS_WIN) {
+ const PF_MMX_INSTRUCTIONS_AVAILABLE = 3; // MMX
+ const PF_XMMI_INSTRUCTIONS_AVAILABLE = 6; // SSE
+ const PF_XMMI64_INSTRUCTIONS_AVAILABLE = 10; // SSE2
+ const PF_SSE3_INSTRUCTIONS_AVAILABLE = 13; // SSE3
+
+ let lib = ctypes.open("kernel32.dll");
+ let IsProcessorFeaturePresent = lib.declare("IsProcessorFeaturePresent",
+ ctypes.winapi_abi,
+ ctypes.int32_t, /* success */
+ ctypes.uint32_t); /* DWORD */
+ let instructionSet = "unknown";
+ try {
+ if (IsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE)) {
+ instructionSet = "SSE3";
+ } else if (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE)) {
+ instructionSet = "SSE2";
+ } else if (IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE)) {
+ instructionSet = "SSE";
+ } else if (IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE)) {
+ instructionSet = "MMX";
+ }
+ } catch (e) {
+ Cu.reportError("Error getting processor instruction set. " +
+ "Exception: " + e);
+ }
+
+ lib.close();
+ return instructionSet;
+ }
+
+ return "NA";
+}
diff --git a/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini b/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini
new file mode 100644
index 000000000..0d2205046
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini
@@ -0,0 +1,27 @@
+; 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/.
+
+[DEFAULT]
+tags = appupdate
+head = head_update.js
+tail =
+
+[canCheckForAndCanApplyUpdates.js]
+[urlConstruction.js]
+[updateManagerXML.js]
+[remoteUpdateXML.js]
+[downloadAndHashCheckMar.js]
+[cleanupDownloadingForOlderAppVersion.js]
+[cleanupDownloadingForDifferentChannel.js]
+[cleanupDownloadingForSameVersionAndBuildID.js]
+[cleanupDownloadingIncorrectStatus.js]
+[cleanupPendingVersionFileIncorrectStatus.js]
+[cleanupSuccessLogMove.js]
+[cleanupSuccessLogsFIFO.js]
+[downloadInterruptedRecovery.js]
+[downloadResumeForSameAppVersion.js]
+[downloadCompleteAfterPartialFailure.js]
+[uiSilentPref.js]
+[uiUnsupportedAlreadyNotified.js]
+[uiAutoPref.js]
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/.eslintrc.js b/toolkit/mozapps/update/tests/unit_base_updater/.eslintrc.js
new file mode 100644
index 000000000..d35787cd2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/head_update.js b/toolkit/mozapps/update/tests/unit_base_updater/head_update.js
new file mode 100644
index 000000000..9715c5828
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/head_update.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const IS_SERVICE_TEST = false;
+
+/* import-globals-from ../data/xpcshellUtilsAUS.js */
+load("../data/xpcshellUtilsAUS.js");
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js
new file mode 100644
index 000000000..f3f767394
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFileNotInInstallDirFailure.js
@@ -0,0 +1,37 @@
+/* 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/.
+ */
+
+/* Callback file not in install directory or a sub-directory of the install
+ directory failure */
+
+const STATE_AFTER_RUNUPDATE = STATE_FAILED_INVALID_CALLBACK_DIR_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = getTestDirFile(FILE_HELPER_BIN).path;
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, null, null, path);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js
new file mode 100644
index 000000000..969f84f9d
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgCallbackFilePathTooLongFailure.js
@@ -0,0 +1,45 @@
+/* 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/.
+ */
+
+/* Too long callback file path failure test */
+
+const STATE_AFTER_RUNUPDATE = STATE_FAILED_INVALID_CALLBACK_PATH_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = "123456789";
+ if (IS_WIN) {
+ path = "\\" + path;
+ path = path.repeat(30); // 300 characters
+ path = "C:" + path;
+ } else {
+ path = "/" + path;
+ path = path.repeat(1000); // 10000 characters
+ }
+
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, null, null, path);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js
new file mode 100644
index 000000000..70e03646a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js
@@ -0,0 +1,47 @@
+/* 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/.
+ */
+
+/* Too long install directory path failure test */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_INSTALL_DIR_PATH_ERROR
+ : STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = "123456789";
+ if (IS_WIN) {
+ path = "\\" + path;
+ path = path.repeat(30); // 300 characters
+ path = "C:" + path;
+ } else {
+ path = "/" + path;
+ path = path.repeat(1000); // 10000 characters
+ }
+
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, path, null, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js
new file mode 100644
index 000000000..330578de6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js
@@ -0,0 +1,44 @@
+/* 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/.
+ */
+
+/* Install directory path traversal failure test */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_INSTALL_DIR_PATH_ERROR
+ : STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = "123456789";
+ if (IS_WIN) {
+ path = "C:\\" + path + "\\..\\" + path;
+ } else {
+ path = "/" + path + "/../" + path;
+ }
+
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, path, null, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js
new file mode 100644
index 000000000..8ddb34af0
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js
@@ -0,0 +1,38 @@
+/* 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/.
+ */
+
+/* Different install and working directories for a regular update failure */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_ERROR
+ : STATE_FAILED_INVALID_APPLYTO_DIR_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = getApplyDirFile("..", false).path;
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, null, path, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js
new file mode 100644
index 000000000..c8ae3f0c6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgPatchDirPathTraversalFailure.js
@@ -0,0 +1,43 @@
+/* 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/.
+ */
+
+/* Patch directory path traversal failure test */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = getUpdatesPatchDir();
+ if (IS_WIN) {
+ path = path + "\\..\\";
+ } else {
+ path = path + "/../";
+ }
+
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, path, null, null, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js
new file mode 100644
index 000000000..e9b227657
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js
@@ -0,0 +1,38 @@
+/* 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/.
+ */
+
+/* Different install and working directories for a regular update failure */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR
+ : STATE_FAILED_INVALID_APPLYTO_DIR_STAGED_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = getApplyDirFile("..", false).path;
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true, null, null, path, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js
new file mode 100644
index 000000000..87bbad4aa
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js
@@ -0,0 +1,38 @@
+/* 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/.
+ */
+
+/* Working directory path local UNC failure test */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_WORKING_DIR_PATH_ERROR
+ : STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = "\\\\.\\" + getApplyDirFile(null, false).path;
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, null, path, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js
new file mode 100644
index 000000000..a550909b2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js
@@ -0,0 +1,37 @@
+/* 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/.
+ */
+
+/* Relative working directory path failure test */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_WORKING_DIR_PATH_ERROR
+ : STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, null, "test", null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js
new file mode 100644
index 000000000..b9f793236
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyDirLockedStageFailure_win.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test applying an update by staging an update and launching an application to
+ * apply it.
+ */
+
+const STATE_AFTER_STAGE = STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ createUpdateInProgressLockFile(getAppBaseDir());
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(false);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ removeUpdateInProgressLockFile(getAppBaseDir());
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(PERFORMING_STAGED_UPDATE);
+ checkUpdateLogContains(ERR_UPDATE_IN_PROGRESS);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js
new file mode 100644
index 000000000..a606720b7
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateAppBinInUseStageSuccess_win.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test applying an update by staging an update and launching an application to
+ * apply it.
+ */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, undefined);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true, false);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ lockDirectory(getAppBaseDir().path);
+ // Switch the application to the staged application that was updated.
+ runUpdateUsingApp(STATE_SUCCEEDED);
+}
+
+/**
+ * Called after the call to runUpdateUsingApp finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
+
+ let updatesDir = getUpdatesPatchDir();
+ Assert.ok(updatesDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(updatesDir.path));
+
+ let log = getUpdateLog(FILE_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js
new file mode 100644
index 000000000..00b38adc7
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageOldVersionFailure.js
@@ -0,0 +1,88 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test a replace request for a staged update with a version file that specifies
+ * an older version failure. The same check is used in nsUpdateDriver.cpp for
+ * all update types which is why there aren't tests for the maintenance service
+ * as well as for other update types.
+ */
+
+const STATE_AFTER_STAGE = STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(false);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Change the active update to an older version to simulate installing a new
+ // version of the application while there is an update that has been staged.
+ let channel = gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL);
+ let patches = getLocalPatchString(null, null, null, null, null, "true",
+ STATE_AFTER_STAGE);
+ let updates = getLocalUpdateString(patches, null, null, null, "1.0", null,
+ null, null, null, null, "true", channel);
+ writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
+ // Change the version file to an older version to simulate installing a new
+ // version of the application while there is an update that has been staged.
+ writeVersionFile("1.0");
+ reloadUpdateManagerData();
+ // Try to switch the application to the staged application that was updated.
+ runUpdateUsingApp(STATE_AFTER_STAGE);
+}
+
+/**
+ * Called after the call to runUpdateUsingApp finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_STAGE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile, IS_MACOSX ? false : true, false);
+
+ let updatesDir = getUpdatesPatchDir();
+ Assert.ok(updatesDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(updatesDir.path));
+
+ let log = getUpdateLog(FILE_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js
new file mode 100644
index 000000000..5b9b08156
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test applying an update by staging an update and launching an application to
+ * apply it.
+ */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, true);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdateUsingApp(STATE_SUCCEEDED);
+}
+
+/**
+ * Called after the call to runUpdateUsingApp finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
+
+ let updatesDir = getUpdatesPatchDir();
+ Assert.ok(updatesDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(updatesDir.path));
+
+ let log = getUpdateLog(FILE_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js
new file mode 100644
index 000000000..e76233fe6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateSuccess.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test applying an update by staging an update and launching an application to
+ * apply it.
+ */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ // The third parameter will test that a full path to the post update binary
+ // doesn't execute.
+ setupUpdaterTest(FILE_COMPLETE_MAR, undefined,
+ getApplyDirFile(null, true).path + "/");
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdateUsingApp(STATE_SUCCEEDED);
+}
+
+/**
+ * Called after the call to runUpdateUsingApp finishes.
+ */
+function runUpdateFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
+
+ let updatesDir = getUpdatesPatchDir();
+ Assert.ok(updatesDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(updatesDir.path));
+
+ let log = getUpdateLog(FILE_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js
new file mode 100644
index 000000000..b1505d58e
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageFailureComplete_win.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Application in use complete MAR file staged patch apply failure test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(DIR_RESOURCES + gCallbackBinFile, false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_MOVE_DESTDIR_7 + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js
new file mode 100644
index 000000000..a1cc7d043
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js
@@ -0,0 +1,113 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Application in use complete MAR file staged patch apply success test */
+
+const START_STATE = STATE_PENDING;
+const STATE_AFTER_STAGE = STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ 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, true);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ setupSymLinks();
+ runHelperFileInUse(DIR_RESOURCES + gCallbackBinFile, false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_SUCCEEDED, true, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ checkSymLinks();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
+ checkCallbackLog();
+}
+
+/**
+ * Setup symlinks for the test.
+ */
+function setupSymLinks() {
+ if (IS_UNIX) {
+ removeSymlink();
+ createSymlink();
+ do_register_cleanup(removeSymlink);
+ gTestFiles.splice(gTestFiles.length - 3, 0,
+ {
+ description: "Readable symlink",
+ fileName: "link",
+ relPathDir: DIR_RESOURCES,
+ originalContents: "test",
+ compareContents: "test",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o666,
+ comparePerms: 0o666
+ });
+ }
+}
+
+/**
+ * Checks the state of the symlinks for the test.
+ */
+function checkSymLinks() {
+ if (IS_UNIX) {
+ checkSymlink();
+ }
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js
new file mode 100644
index 000000000..93333cade
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseSuccessComplete.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Application in use complete MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(DIR_RESOURCES + gCallbackBinFile, false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js
new file mode 100644
index 000000000..79e54c182
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessComplete_win.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Replace app binary complete MAR file staged patch apply success test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ gCallbackBinFile = "exe0.exe";
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_SUCCEEDED, true, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js
new file mode 100644
index 000000000..b1f84715f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppStageSuccessPartial_win.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Patch app binary partial MAR file staged patch apply success test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ gCallbackBinFile = "exe0.exe";
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_SUCCEEDED, true, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js
new file mode 100644
index 000000000..85e92d290
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessComplete_win.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Replace app binary complete MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ gCallbackBinFile = "exe0.exe";
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js
new file mode 100644
index 000000000..1212c9ba2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marCallbackAppSuccessPartial_win.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Patch app binary partial MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ gCallbackBinFile = "exe0.exe";
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js
new file mode 100644
index 000000000..960c96f7b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marFailurePartial.js
@@ -0,0 +1,47 @@
+/* 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/.
+ */
+
+/* General Partial MAR File Patch Apply Failure Test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestFiles[11].originalFile = "partial.png";
+ gTestDirs = gTestDirsPartialSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ // If execv is used the updater process will turn into the callback process
+ // and the updater's return code will be that of the callback process.
+ runUpdate(STATE_FAILED_LOADSOURCE_ERROR_WRONG_SIZE, false, (USE_EXECV ? 0 : 1),
+ true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusFile(), STATE_NONE,
+ "the status file failure code" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContents(LOG_PARTIAL_FAILURE);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js
new file mode 100644
index 000000000..b39595f92
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailureComplete_win.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use complete MAR file staged patch apply failure test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestFiles[13].relPathDir + gTestFiles[13].fileName,
+ false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_MOVE_DESTDIR_7 + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js
new file mode 100644
index 000000000..06d386ad6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseStageFailurePartial_win.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use partial MAR file staged patch apply failure test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestFiles[11].relPathDir + gTestFiles[11].fileName,
+ false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_MOVE_DESTDIR_7 + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js
new file mode 100644
index 000000000..89a2fff5e
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessComplete_win.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use complete MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestFiles[13].relPathDir + gTestFiles[13].fileName,
+ false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContains(ERR_BACKUP_DISCARD);
+ checkUpdateLogContains(STATE_SUCCEEDED + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js
new file mode 100644
index 000000000..ea85ddccc
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileInUseSuccessPartial_win.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use partial MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestFiles[11].relPathDir + gTestFiles[11].fileName,
+ false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContains(ERR_BACKUP_DISCARD);
+ checkUpdateLogContains(STATE_SUCCEEDED + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js
new file mode 100644
index 000000000..c5efaa8c0
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailureComplete_win.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File locked complete MAR file patch apply failure test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperLockFile(gTestFiles[3]);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_FAILED_WRITE_ERROR, false, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusFile(), STATE_PENDING,
+ "the status file failure code" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_BACKUP_CREATE_7);
+ checkUpdateLogContains(STATE_FAILED_WRITE_ERROR + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js
new file mode 100644
index 000000000..4fdbadb5b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedFailurePartial_win.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File locked partial MAR file patch apply failure test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperLockFile(gTestFiles[2]);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_FAILED_READ_ERROR, false, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusFile(), STATE_NONE,
+ "the status file failure code" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_UNABLE_OPEN_DEST);
+ checkUpdateLogContains(STATE_FAILED_READ_ERROR + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js
new file mode 100644
index 000000000..4d12f4e42
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailureComplete_win.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File locked complete MAR file staged patch apply failure test */
+
+const STATE_AFTER_STAGE = STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperLockFile(gTestFiles[3]);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ // Files aren't checked after staging since this test locks a file which
+ // prevents reading the file.
+ checkUpdateLogContains(ERR_ENSURE_COPY);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_FAILED_WRITE_ERROR, false, 1, false);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusFile(), STATE_PENDING,
+ "the status file failure code" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 2,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_BACKUP_CREATE_7);
+ checkUpdateLogContains(STATE_FAILED_WRITE_ERROR + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js
new file mode 100644
index 000000000..5f64df34c
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marFileLockedStageFailurePartial_win.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File locked partial MAR file staged patch apply failure test */
+
+const STATE_AFTER_STAGE = STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperLockFile(gTestFiles[2]);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ // Files aren't checked after staging since this test locks a file which
+ // prevents reading the file.
+ checkUpdateLogContains(ERR_ENSURE_COPY);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_FAILED_READ_ERROR, false, 1, false);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusFile(), STATE_NONE,
+ "the status file failure code" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 2,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_UNABLE_OPEN_DEST);
+ checkUpdateLogContains(STATE_FAILED_READ_ERROR + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js
new file mode 100644
index 000000000..b83bafccc
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailureComplete_win.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use inside removed dir complete MAR file staged patch apply failure
+ test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestDirs[4].relPathDir + gTestDirs[4].subDirs[0] +
+ gTestDirs[4].subDirFiles[0], true);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_MOVE_DESTDIR_7 + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js
new file mode 100644
index 000000000..39ea485cd
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseStageFailurePartial_win.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use inside removed dir partial MAR file staged patch apply failure
+ test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestDirs[2].relPathDir + gTestDirs[2].files[0], true);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_MOVE_DESTDIR_7 + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js
new file mode 100644
index 000000000..a71bb8d49
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessComplete_win.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use inside removed dir complete MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestDirs[4].relPathDir + gTestDirs[4].subDirs[0] +
+ gTestDirs[4].subDirFiles[0], true);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContains(ERR_BACKUP_DISCARD);
+ checkUpdateLogContains(STATE_SUCCEEDED + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js
new file mode 100644
index 000000000..2cbe70ed8
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marRMRFDirFileInUseSuccessPartial_win.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use inside removed dir partial MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestDirs[2].relPathDir + gTestDirs[2].files[0], true);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContains(ERR_BACKUP_DISCARD);
+ checkUpdateLogContains(STATE_SUCCEEDED + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js
new file mode 100644
index 000000000..a9ce23420
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageFailurePartial.js
@@ -0,0 +1,41 @@
+/* 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/.
+ */
+
+/* General Partial MAR File Staged Patch Apply Failure Test */
+
+const STATE_AFTER_STAGE = STATE_FAILED;
+gStagingRemovedUpdate = true;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestFiles[11].originalFile = "partial.png";
+ gTestDirs = gTestDirsPartialSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_LOADSOURCEFILE_FAILED);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
new file mode 100644
index 000000000..f7745f68f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
@@ -0,0 +1,132 @@
+/* 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/.
+ */
+
+/* General Complete MAR File Staged Patch Apply Test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestFiles[gTestFiles.length - 1].originalContents = null;
+ gTestFiles[gTestFiles.length - 1].compareContents = "FromComplete\n";
+ gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupDistributionDir();
+ setupSymLinks();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_SUCCEEDED, true, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ checkSymLinks();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
+ checkDistributionDir();
+ checkCallbackLog();
+}
+
+/**
+ * Setup the state of the distribution directory for the test.
+ */
+function setupDistributionDir() {
+ if (IS_MACOSX) {
+ // Create files in the old distribution directory location to verify that
+ // the directory and its contents are removed when there is a distribution
+ // directory in the new location.
+ let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
+ writeFile(testFile, "test\n");
+ testFile = getApplyDirFile(DIR_MACOS + "distribution/test1/testFile", true);
+ writeFile(testFile, "test\n");
+ }
+}
+
+/**
+ * Checks the state of the distribution directory for the test.
+ */
+function checkDistributionDir() {
+ if (IS_MACOSX) {
+ let distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+ checkUpdateLogContains(REMOVE_OLD_DIST_DIR);
+ }
+}
+
+/**
+ * Setup symlinks for the test.
+ */
+function setupSymLinks() {
+ // Don't test symlinks on Mac OS X in this test since it tends to timeout.
+ // It is tested on Mac OS X in marAppInUseStageSuccessComplete_unix.js
+ if (IS_UNIX && !IS_MACOSX) {
+ removeSymlink();
+ createSymlink();
+ do_register_cleanup(removeSymlink);
+ gTestFiles.splice(gTestFiles.length - 3, 0,
+ {
+ description: "Readable symlink",
+ fileName: "link",
+ relPathDir: DIR_RESOURCES,
+ originalContents: "test",
+ compareContents: "test",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o666,
+ comparePerms: 0o666
+ });
+ }
+}
+
+/**
+ * Checks the state of the symlinks for the test.
+ */
+function checkSymLinks() {
+ // Don't test symlinks on Mac OS X in this test since it tends to timeout.
+ // It is tested on Mac OS X in marAppInUseStageSuccessComplete_unix.js
+ if (IS_UNIX && !IS_MACOSX) {
+ checkSymlink();
+ }
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js
new file mode 100644
index 000000000..ef15326de
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessPartial.js
@@ -0,0 +1,112 @@
+/* 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/.
+ */
+
+/* General Partial MAR File Staged Patch Apply Test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestFiles[gTestFiles.length - 2].originalContents = null;
+ gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
+ gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
+ gTestDirs = gTestDirsPartialSuccess;
+ preventDistributionFiles();
+ setupDistributionDir();
+ setupUpdaterTest(FILE_PARTIAL_MAR, true);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS, true, false, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_SUCCEEDED, true, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true, true);
+ checkDistributionDir();
+ checkCallbackLog();
+}
+
+/**
+ * Setup the state of the distribution directory for the test.
+ */
+function setupDistributionDir() {
+ if (IS_MACOSX) {
+ // Create files in the old distribution directory location to verify that
+ // the directory and its contents are moved to the new location on update.
+ let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
+ writeFile(testFile, "test\n");
+ testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
+ writeFile(testFile, "test\n");
+ }
+}
+
+/**
+ * Checks the state of the distribution directory.
+ */
+function checkDistributionDir() {
+ let distributionDir = getApplyDirFile(DIR_RESOURCES + "distribution", true);
+ if (IS_MACOSX) {
+ Assert.ok(distributionDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(distributionDir.path));
+
+ let testFile = getApplyDirFile(DIR_RESOURCES + "distribution/testFile", true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+
+ testFile = getApplyDirFile(DIR_RESOURCES + "distribution/test/testFile", true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+
+ distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+
+ checkUpdateLogContains(MOVE_OLD_DIST_DIR);
+ } else {
+ debugDump("testing that files aren't added with an add-if instruction " +
+ "when the file's destination directory doesn't exist");
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+ }
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js
new file mode 100644
index 000000000..1008e867f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessComplete.js
@@ -0,0 +1,96 @@
+/* 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/.
+ */
+
+/* General Complete MAR File Patch Apply Test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ preventDistributionFiles();
+ setupDistributionDir();
+ setupUpdaterTest(FILE_COMPLETE_MAR, true);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, false, false, true);
+ checkDistributionDir();
+ checkCallbackLog();
+}
+
+/**
+ * Setup the state of the distribution directory for the test.
+ */
+function setupDistributionDir() {
+ if (IS_MACOSX) {
+ // Create files in the old distribution directory location to verify that
+ // the directory and its contents are moved to the new location on update.
+ let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
+ writeFile(testFile, "test\n");
+ testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
+ writeFile(testFile, "test\n");
+ }
+}
+
+/**
+ * Checks the state of the distribution directory.
+ */
+function checkDistributionDir() {
+ let distributionDir = getApplyDirFile(DIR_RESOURCES + "distribution", true);
+ if (IS_MACOSX) {
+ Assert.ok(distributionDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(distributionDir.path));
+
+ let testFile = getApplyDirFile(DIR_RESOURCES + "distribution/testFile", true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+
+ testFile = getApplyDirFile(DIR_RESOURCES + "distribution/test/testFile", true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+
+ distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+
+ checkUpdateLogContains(MOVE_OLD_DIST_DIR);
+ } else {
+ debugDump("testing that files aren't added with an add-if instruction " +
+ "when the file's destination directory doesn't exist");
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+ }
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js
new file mode 100644
index 000000000..616390f55
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marSuccessPartial.js
@@ -0,0 +1,79 @@
+/* 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/.
+ */
+
+/* General Partial MAR File Patch Apply Test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestFiles[gTestFiles.length - 1].originalContents = null;
+ 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;
+ setupDistributionDir();
+ // The third parameter will test that a relative path that contains a
+ // directory traversal to the post update binary doesn't execute.
+ setupUpdaterTest(FILE_PARTIAL_MAR, false, "test/../");
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
+ checkDistributionDir();
+ checkCallbackLog();
+}
+
+/**
+ * Setup the state of the distribution directory for the test.
+ */
+function setupDistributionDir() {
+ if (IS_MACOSX) {
+ // Create files in the old distribution directory location to verify that
+ // the directory and its contents are removed when there is a distribution
+ // directory in the new location.
+ let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
+ writeFile(testFile, "test\n");
+ testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
+ writeFile(testFile, "test\n");
+ }
+}
+
+/**
+ * Checks the state of the distribution directory.
+ */
+function checkDistributionDir() {
+ if (IS_MACOSX) {
+ let distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+ checkUpdateLogContains(REMOVE_OLD_DIST_DIR);
+ }
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js b/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js
new file mode 100644
index 000000000..86a2eb821
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marVersionDowngrade.js
@@ -0,0 +1,51 @@
+/* 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/.
+ */
+
+/* Test version downgrade MAR security check */
+
+function run_test() {
+ if (!MOZ_VERIFY_MAR_SIGNATURE) {
+ return;
+ }
+
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_OLD_VERSION_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ // If execv is used the updater process will turn into the callback process
+ // and the updater's return code will be that of the callback process.
+ runUpdate(STATE_FAILED_VERSION_DOWNGRADE_ERROR, false, (USE_EXECV ? 0 : 1),
+ false);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, VERSION_DOWNGRADE_ERROR,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(STATE_FAILED_VERSION_DOWNGRADE_ERROR);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js b/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js
new file mode 100644
index 000000000..6db906fbc
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marWrongChannel.js
@@ -0,0 +1,51 @@
+/* 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/.
+ */
+
+/* Test product/channel MAR security check */
+
+function run_test() {
+ if (!MOZ_VERIFY_MAR_SIGNATURE) {
+ return;
+ }
+
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_WRONG_CHANNEL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ // If execv is used the updater process will turn into the callback process
+ // and the updater's return code will be that of the callback process.
+ runUpdate(STATE_FAILED_MAR_CHANNEL_MISMATCH_ERROR, false, (USE_EXECV ? 0 : 1),
+ false);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, MAR_CHANNEL_MISMATCH_ERROR,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(STATE_FAILED_MAR_CHANNEL_MISMATCH_ERROR);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini b/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
new file mode 100644
index 000000000..2b77bee7a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
@@ -0,0 +1,136 @@
+; 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/.
+
+; Tests that require the updater binary. These tests should never run on Android
+; which doesn't use the updater binary as other applications do and are excluded
+; from running the tests in the moz.build file.
+
+[DEFAULT]
+tags = appupdate
+head = head_update.js
+tail =
+
+[invalidArgCallbackFileNotInInstallDirFailure.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[invalidArgCallbackFilePathTooLongFailure.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[invalidArgInstallDirPathTooLongFailure.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[invalidArgInstallDirPathTraversalFailure.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[invalidArgInstallWorkingDirPathNotSameFailure_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[invalidArgPatchDirPathTraversalFailure.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[invalidArgStageDirNotInInstallDirFailure_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[invalidArgWorkingDirPathLocalUNCFailure_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[invalidArgWorkingDirPathRelativeFailure.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[marSuccessComplete.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[marSuccessPartial.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[marFailurePartial.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[marStageSuccessComplete.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[marStageSuccessPartial.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[marVersionDowngrade.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985 and mar signing
+[marWrongChannel.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985 and mar signing
+[marStageFailurePartial.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[marCallbackAppSuccessComplete_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marCallbackAppSuccessPartial_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marCallbackAppStageSuccessComplete_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marCallbackAppStageSuccessPartial_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marAppInUseSuccessComplete.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[marAppInUseStageSuccessComplete_unix.js]
+skip-if = os == 'win'
+reason = not a Windows test
+[marAppInUseStageFailureComplete_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marFileLockedFailureComplete_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marFileLockedFailurePartial_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marFileLockedStageFailureComplete_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marFileLockedStageFailurePartial_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marFileInUseSuccessComplete_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marFileInUseSuccessPartial_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marRMRFDirFileInUseSuccessComplete_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marRMRFDirFileInUseSuccessPartial_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marFileInUseStageFailureComplete_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marFileInUseStageFailurePartial_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marRMRFDirFileInUseStageFailureComplete_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marRMRFDirFileInUseStageFailurePartial_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marAppApplyDirLockedStageFailure_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marAppApplyUpdateAppBinInUseStageSuccess_win.js]
+skip-if = os != 'win' || debug && (os_version == '5.1' || os_version == '5.2')
+reason = Windows only test and bug 1291985
+[marAppApplyUpdateSuccess.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[marAppApplyUpdateStageSuccess.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+[marAppApplyUpdateStageOldVersionFailure.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/.eslintrc.js b/toolkit/mozapps/update/tests/unit_service_updater/.eslintrc.js
new file mode 100644
index 000000000..d35787cd2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/bootstrapSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/bootstrapSvc.js
new file mode 100644
index 000000000..015fbd0cb
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/bootstrapSvc.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Bootstrap the tests using the service by installing our own version of the service */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ // 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_COMPLETE_MAR, null);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdateUsingService finishes.
+ */
+function runUpdateFinished() {
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, false);
+
+ // We need to check the service log even though this is a bootstrap
+ // because the app bin could be in use by this test by the time the next
+ // test runs.
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/checkUpdaterSigSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/checkUpdaterSigSvc.js
new file mode 100644
index 000000000..bf765ee78
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/checkUpdaterSigSvc.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * We skip authenticode cert checks from the service udpates
+ * so that we can use updater-xpcshell with the wrong certs for testing.
+ * This tests that code path. */
+
+function run_test() {
+ if (!IS_AUTHENTICODE_CHECK_ENABLED) {
+ return;
+ }
+
+ let binDir = getGREBinDir();
+ let maintenanceServiceBin = binDir.clone();
+ maintenanceServiceBin.append(FILE_MAINTENANCE_SERVICE_BIN);
+
+ let updaterBin = binDir.clone();
+ updaterBin.append(FILE_UPDATER_BIN);
+
+ debugDump("Launching maintenance service bin: " +
+ maintenanceServiceBin.path + " to check updater: " +
+ updaterBin.path + " signature.");
+
+ // Bypass the manifest and run as invoker
+ gEnv.set("__COMPAT_LAYER", "RunAsInvoker");
+
+ let dummyInstallPath = "---";
+ let maintenanceServiceBinArgs = ["check-cert", dummyInstallPath,
+ updaterBin.path];
+ let maintenanceServiceBinProcess = Cc["@mozilla.org/process/util;1"].
+ createInstance(Ci.nsIProcess);
+ maintenanceServiceBinProcess.init(maintenanceServiceBin);
+ maintenanceServiceBinProcess.run(true, maintenanceServiceBinArgs,
+ maintenanceServiceBinArgs.length);
+ Assert.equal(maintenanceServiceBinProcess.exitValue, 0,
+ "the maintenance service exit value should be 0");
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/head_update.js b/toolkit/mozapps/update/tests/unit_service_updater/head_update.js
new file mode 100644
index 000000000..38be4ee39
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/head_update.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const IS_SERVICE_TEST = true;
+
+/* import-globals-from ../data/xpcshellUtilsAUS.js */
+load("../data/xpcshellUtilsAUS.js");
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js
new file mode 100644
index 000000000..70e03646a
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js
@@ -0,0 +1,47 @@
+/* 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/.
+ */
+
+/* Too long install directory path failure test */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_INSTALL_DIR_PATH_ERROR
+ : STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = "123456789";
+ if (IS_WIN) {
+ path = "\\" + path;
+ path = path.repeat(30); // 300 characters
+ path = "C:" + path;
+ } else {
+ path = "/" + path;
+ path = path.repeat(1000); // 10000 characters
+ }
+
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, path, null, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js
new file mode 100644
index 000000000..330578de6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js
@@ -0,0 +1,44 @@
+/* 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/.
+ */
+
+/* Install directory path traversal failure test */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_INSTALL_DIR_PATH_ERROR
+ : STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = "123456789";
+ if (IS_WIN) {
+ path = "C:\\" + path + "\\..\\" + path;
+ } else {
+ path = "/" + path + "/../" + path;
+ }
+
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, path, null, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js
new file mode 100644
index 000000000..8ddb34af0
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js
@@ -0,0 +1,38 @@
+/* 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/.
+ */
+
+/* Different install and working directories for a regular update failure */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_ERROR
+ : STATE_FAILED_INVALID_APPLYTO_DIR_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = getApplyDirFile("..", false).path;
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, null, path, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js
new file mode 100644
index 000000000..c8ae3f0c6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgPatchDirPathTraversalFailureSvc.js
@@ -0,0 +1,43 @@
+/* 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/.
+ */
+
+/* Patch directory path traversal failure test */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = getUpdatesPatchDir();
+ if (IS_WIN) {
+ path = path + "\\..\\";
+ } else {
+ path = path + "/../";
+ }
+
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, path, null, null, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js
new file mode 100644
index 000000000..e9b227657
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js
@@ -0,0 +1,38 @@
+/* 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/.
+ */
+
+/* Different install and working directories for a regular update failure */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR
+ : STATE_FAILED_INVALID_APPLYTO_DIR_STAGED_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = getApplyDirFile("..", false).path;
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true, null, null, path, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js
new file mode 100644
index 000000000..87bbad4aa
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js
@@ -0,0 +1,38 @@
+/* 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/.
+ */
+
+/* Working directory path local UNC failure test */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_WORKING_DIR_PATH_ERROR
+ : STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ let path = "\\\\.\\" + getApplyDirFile(null, false).path;
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, null, path, null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js
new file mode 100644
index 000000000..a550909b2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js
@@ -0,0 +1,37 @@
+/* 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/.
+ */
+
+/* Relative working directory path failure test */
+
+const STATE_AFTER_RUNUPDATE =
+ IS_SERVICE_TEST ? STATE_FAILED_SERVICE_INVALID_WORKING_DIR_PATH_ERROR
+ : STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_AFTER_RUNUPDATE, false, 1, true, null, null, "test", null);
+}
+
+/**
+ * Called after the call to runUpdateUsingUpdater finishes.
+ */
+function runUpdateFinished() {
+ standardInit();
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js
new file mode 100644
index 000000000..b9f793236
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyDirLockedStageFailureSvc_win.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test applying an update by staging an update and launching an application to
+ * apply it.
+ */
+
+const STATE_AFTER_STAGE = STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ createUpdateInProgressLockFile(getAppBaseDir());
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(false);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ removeUpdateInProgressLockFile(getAppBaseDir());
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(PERFORMING_STAGED_UPDATE);
+ checkUpdateLogContains(ERR_UPDATE_IN_PROGRESS);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js
new file mode 100644
index 000000000..a606720b7
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test applying an update by staging an update and launching an application to
+ * apply it.
+ */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, undefined);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true, false);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ lockDirectory(getAppBaseDir().path);
+ // Switch the application to the staged application that was updated.
+ runUpdateUsingApp(STATE_SUCCEEDED);
+}
+
+/**
+ * Called after the call to runUpdateUsingApp finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
+
+ let updatesDir = getUpdatesPatchDir();
+ Assert.ok(updatesDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(updatesDir.path));
+
+ let log = getUpdateLog(FILE_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js
new file mode 100644
index 000000000..5b9b08156
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test applying an update by staging an update and launching an application to
+ * apply it.
+ */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, true);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdateUsingApp(STATE_SUCCEEDED);
+}
+
+/**
+ * Called after the call to runUpdateUsingApp finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
+
+ let updatesDir = getUpdatesPatchDir();
+ Assert.ok(updatesDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(updatesDir.path));
+
+ let log = getUpdateLog(FILE_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js
new file mode 100644
index 000000000..e76233fe6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test applying an update by staging an update and launching an application to
+ * apply it.
+ */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ // The third parameter will test that a full path to the post update binary
+ // doesn't execute.
+ setupUpdaterTest(FILE_COMPLETE_MAR, undefined,
+ getApplyDirFile(null, true).path + "/");
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdateUsingApp(STATE_SUCCEEDED);
+}
+
+/**
+ * Called after the call to runUpdateUsingApp finishes.
+ */
+function runUpdateFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
+
+ let updatesDir = getUpdatesPatchDir();
+ Assert.ok(updatesDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(updatesDir.path));
+
+ let log = getUpdateLog(FILE_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_LAST_UPDATE_LOG);
+ Assert.ok(log.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(log.path));
+
+ log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
+ Assert.ok(!log.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(log.path));
+
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js
new file mode 100644
index 000000000..b1505d58e
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseStageFailureCompleteSvc_win.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Application in use complete MAR file staged patch apply failure test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(DIR_RESOURCES + gCallbackBinFile, false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_MOVE_DESTDIR_7 + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js
new file mode 100644
index 000000000..93333cade
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppInUseSuccessCompleteSvc.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Application in use complete MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(DIR_RESOURCES + gCallbackBinFile, false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js
new file mode 100644
index 000000000..79e54c182
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessCompleteSvc_win.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Replace app binary complete MAR file staged patch apply success test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ gCallbackBinFile = "exe0.exe";
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_SUCCEEDED, true, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js
new file mode 100644
index 000000000..b1f84715f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppStageSuccessPartialSvc_win.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Patch app binary partial MAR file staged patch apply success test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ gCallbackBinFile = "exe0.exe";
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_SUCCEEDED, true, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js
new file mode 100644
index 000000000..85e92d290
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessCompleteSvc_win.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Replace app binary complete MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ gCallbackBinFile = "exe0.exe";
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js
new file mode 100644
index 000000000..1212c9ba2
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marCallbackAppSuccessPartialSvc_win.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Patch app binary partial MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ gCallbackBinFile = "exe0.exe";
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js
new file mode 100644
index 000000000..960c96f7b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marFailurePartialSvc.js
@@ -0,0 +1,47 @@
+/* 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/.
+ */
+
+/* General Partial MAR File Patch Apply Failure Test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestFiles[11].originalFile = "partial.png";
+ gTestDirs = gTestDirsPartialSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ // If execv is used the updater process will turn into the callback process
+ // and the updater's return code will be that of the callback process.
+ runUpdate(STATE_FAILED_LOADSOURCE_ERROR_WRONG_SIZE, false, (USE_EXECV ? 0 : 1),
+ true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusFile(), STATE_NONE,
+ "the status file failure code" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContents(LOG_PARTIAL_FAILURE);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js
new file mode 100644
index 000000000..b39595f92
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailureCompleteSvc_win.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use complete MAR file staged patch apply failure test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestFiles[13].relPathDir + gTestFiles[13].fileName,
+ false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_MOVE_DESTDIR_7 + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js
new file mode 100644
index 000000000..06d386ad6
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseStageFailurePartialSvc_win.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use partial MAR file staged patch apply failure test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestFiles[11].relPathDir + gTestFiles[11].fileName,
+ false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_MOVE_DESTDIR_7 + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js
new file mode 100644
index 000000000..89a2fff5e
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessCompleteSvc_win.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use complete MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestFiles[13].relPathDir + gTestFiles[13].fileName,
+ false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContains(ERR_BACKUP_DISCARD);
+ checkUpdateLogContains(STATE_SUCCEEDED + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js
new file mode 100644
index 000000000..ea85ddccc
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileInUseSuccessPartialSvc_win.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use partial MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestFiles[11].relPathDir + gTestFiles[11].fileName,
+ false);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContains(ERR_BACKUP_DISCARD);
+ checkUpdateLogContains(STATE_SUCCEEDED + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js
new file mode 100644
index 000000000..c5efaa8c0
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailureCompleteSvc_win.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File locked complete MAR file patch apply failure test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperLockFile(gTestFiles[3]);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_FAILED_WRITE_ERROR, false, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusFile(), STATE_PENDING,
+ "the status file failure code" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_BACKUP_CREATE_7);
+ checkUpdateLogContains(STATE_FAILED_WRITE_ERROR + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js
new file mode 100644
index 000000000..4fdbadb5b
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedFailurePartialSvc_win.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File locked partial MAR file patch apply failure test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperLockFile(gTestFiles[2]);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_FAILED_READ_ERROR, false, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusFile(), STATE_NONE,
+ "the status file failure code" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_UNABLE_OPEN_DEST);
+ checkUpdateLogContains(STATE_FAILED_READ_ERROR + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js
new file mode 100644
index 000000000..4d12f4e42
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailureCompleteSvc_win.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File locked complete MAR file staged patch apply failure test */
+
+const STATE_AFTER_STAGE = STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperLockFile(gTestFiles[3]);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ // Files aren't checked after staging since this test locks a file which
+ // prevents reading the file.
+ checkUpdateLogContains(ERR_ENSURE_COPY);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_FAILED_WRITE_ERROR, false, 1, false);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusFile(), STATE_PENDING,
+ "the status file failure code" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 2,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_PENDING,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, WRITE_ERROR,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_BACKUP_CREATE_7);
+ checkUpdateLogContains(STATE_FAILED_WRITE_ERROR + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js
new file mode 100644
index 000000000..5f64df34c
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marFileLockedStageFailurePartialSvc_win.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File locked partial MAR file staged patch apply failure test */
+
+const STATE_AFTER_STAGE = STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperLockFile(gTestFiles[2]);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ // Files aren't checked after staging since this test locks a file which
+ // prevents reading the file.
+ checkUpdateLogContains(ERR_ENSURE_COPY);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_FAILED_READ_ERROR, false, 1, false);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusFile(), STATE_NONE,
+ "the status file failure code" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.updateCount, 2,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_FAILED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, READ_ERROR,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_UNABLE_OPEN_DEST);
+ checkUpdateLogContains(STATE_FAILED_READ_ERROR + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js
new file mode 100644
index 000000000..b83bafccc
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailureCompleteSvc_win.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use inside removed dir complete MAR file staged patch apply failure
+ test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestDirs[4].relPathDir + gTestDirs[4].subDirs[0] +
+ gTestDirs[4].subDirFiles[0], true);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_MOVE_DESTDIR_7 + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js
new file mode 100644
index 000000000..39ea485cd
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseStageFailurePartialSvc_win.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use inside removed dir partial MAR file staged patch apply failure
+ test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_PENDING_SVC : STATE_PENDING;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestDirs[2].relPathDir + gTestDirs[2].files[0], true);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_AFTER_RUNUPDATE, true, 1, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_AFTER_RUNUPDATE,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ setTestFilesAndDirsForFailure();
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_RENAME_FILE);
+ checkUpdateLogContains(ERR_MOVE_DESTDIR_7 + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js
new file mode 100644
index 000000000..a71bb8d49
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessCompleteSvc_win.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use inside removed dir complete MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestDirs[4].relPathDir + gTestDirs[4].subDirs[0] +
+ gTestDirs[4].subDirFiles[0], true);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContains(ERR_BACKUP_DISCARD);
+ checkUpdateLogContains(STATE_SUCCEEDED + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js
new file mode 100644
index 000000000..2cbe70ed8
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marRMRFDirFileInUseSuccessPartialSvc_win.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* File in use inside removed dir partial MAR file patch apply success test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestDirs = gTestDirsPartialSuccess;
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runHelperFileInUse(gTestDirs[2].relPathDir + gTestDirs[2].files[0], true);
+}
+
+/**
+ * Called after the call to waitForHelperSleep finishes.
+ */
+function waitForHelperSleepFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ waitForHelperExit();
+}
+
+/**
+ * Called after the call to waitForHelperExit finishes.
+ */
+function waitForHelperExitFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContains(ERR_BACKUP_DISCARD);
+ checkUpdateLogContains(STATE_SUCCEEDED + "\n" + CALL_QUIT);
+ checkCallbackLog();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js
new file mode 100644
index 000000000..a9ce23420
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageFailurePartialSvc.js
@@ -0,0 +1,41 @@
+/* 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/.
+ */
+
+/* General Partial MAR File Staged Patch Apply Failure Test */
+
+const STATE_AFTER_STAGE = STATE_FAILED;
+gStagingRemovedUpdate = true;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestFiles[11].originalFile = "partial.png";
+ gTestDirs = gTestDirsPartialSuccess;
+ setTestFilesAndDirsForFailure();
+ setupUpdaterTest(FILE_PARTIAL_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).errorCode, LOADSOURCE_ERROR_WRONG_SIZE,
+ "the update errorCode" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateFailure(getApplyDirFile);
+ checkUpdateLogContains(ERR_LOADSOURCEFILE_FAILED);
+ waitForFilesInUse();
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js
new file mode 100644
index 000000000..f7745f68f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js
@@ -0,0 +1,132 @@
+/* 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/.
+ */
+
+/* General Complete MAR File Staged Patch Apply Test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestFiles[gTestFiles.length - 1].originalContents = null;
+ gTestFiles[gTestFiles.length - 1].compareContents = "FromComplete\n";
+ gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
+ gTestDirs = gTestDirsCompleteSuccess;
+ setupDistributionDir();
+ setupSymLinks();
+ setupUpdaterTest(FILE_COMPLETE_MAR, false);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_SUCCEEDED, true, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ checkSymLinks();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true);
+ checkDistributionDir();
+ checkCallbackLog();
+}
+
+/**
+ * Setup the state of the distribution directory for the test.
+ */
+function setupDistributionDir() {
+ if (IS_MACOSX) {
+ // Create files in the old distribution directory location to verify that
+ // the directory and its contents are removed when there is a distribution
+ // directory in the new location.
+ let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
+ writeFile(testFile, "test\n");
+ testFile = getApplyDirFile(DIR_MACOS + "distribution/test1/testFile", true);
+ writeFile(testFile, "test\n");
+ }
+}
+
+/**
+ * Checks the state of the distribution directory for the test.
+ */
+function checkDistributionDir() {
+ if (IS_MACOSX) {
+ let distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+ checkUpdateLogContains(REMOVE_OLD_DIST_DIR);
+ }
+}
+
+/**
+ * Setup symlinks for the test.
+ */
+function setupSymLinks() {
+ // Don't test symlinks on Mac OS X in this test since it tends to timeout.
+ // It is tested on Mac OS X in marAppInUseStageSuccessComplete_unix.js
+ if (IS_UNIX && !IS_MACOSX) {
+ removeSymlink();
+ createSymlink();
+ do_register_cleanup(removeSymlink);
+ gTestFiles.splice(gTestFiles.length - 3, 0,
+ {
+ description: "Readable symlink",
+ fileName: "link",
+ relPathDir: DIR_RESOURCES,
+ originalContents: "test",
+ compareContents: "test",
+ originalFile: null,
+ compareFile: null,
+ originalPerms: 0o666,
+ comparePerms: 0o666
+ });
+ }
+}
+
+/**
+ * Checks the state of the symlinks for the test.
+ */
+function checkSymLinks() {
+ // Don't test symlinks on Mac OS X in this test since it tends to timeout.
+ // It is tested on Mac OS X in marAppInUseStageSuccessComplete_unix.js
+ if (IS_UNIX && !IS_MACOSX) {
+ checkSymlink();
+ }
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js
new file mode 100644
index 000000000..ef15326de
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js
@@ -0,0 +1,112 @@
+/* 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/.
+ */
+
+/* General Partial MAR File Staged Patch Apply Test */
+
+const STATE_AFTER_STAGE = IS_SERVICE_TEST ? STATE_APPLIED_SVC : STATE_APPLIED;
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestFiles[gTestFiles.length - 2].originalContents = null;
+ gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
+ gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
+ gTestDirs = gTestDirsPartialSuccess;
+ preventDistributionFiles();
+ setupDistributionDir();
+ setupUpdaterTest(FILE_PARTIAL_MAR, true);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ stageUpdate(true);
+}
+
+/**
+ * Called after the call to stageUpdate finishes.
+ */
+function stageUpdateFinished() {
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getStageDirFile, true);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS, true, false, true);
+ // Switch the application to the staged application that was updated.
+ runUpdate(STATE_SUCCEEDED, true, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
+ checkUpdateLogContents(LOG_REPLACE_SUCCESS, false, true, true);
+ checkDistributionDir();
+ checkCallbackLog();
+}
+
+/**
+ * Setup the state of the distribution directory for the test.
+ */
+function setupDistributionDir() {
+ if (IS_MACOSX) {
+ // Create files in the old distribution directory location to verify that
+ // the directory and its contents are moved to the new location on update.
+ let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
+ writeFile(testFile, "test\n");
+ testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
+ writeFile(testFile, "test\n");
+ }
+}
+
+/**
+ * Checks the state of the distribution directory.
+ */
+function checkDistributionDir() {
+ let distributionDir = getApplyDirFile(DIR_RESOURCES + "distribution", true);
+ if (IS_MACOSX) {
+ Assert.ok(distributionDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(distributionDir.path));
+
+ let testFile = getApplyDirFile(DIR_RESOURCES + "distribution/testFile", true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+
+ testFile = getApplyDirFile(DIR_RESOURCES + "distribution/test/testFile", true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+
+ distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+
+ checkUpdateLogContains(MOVE_OLD_DIST_DIR);
+ } else {
+ debugDump("testing that files aren't added with an add-if instruction " +
+ "when the file's destination directory doesn't exist");
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+ }
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js
new file mode 100644
index 000000000..1008e867f
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js
@@ -0,0 +1,96 @@
+/* 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/.
+ */
+
+/* General Complete MAR File Patch Apply Test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesCompleteSuccess;
+ gTestDirs = gTestDirsCompleteSuccess;
+ preventDistributionFiles();
+ setupDistributionDir();
+ setupUpdaterTest(FILE_COMPLETE_MAR, true);
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkPostUpdateAppLog();
+}
+
+/**
+ * Called after the call to checkPostUpdateAppLog finishes.
+ */
+function checkPostUpdateAppLogFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(true);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_COMPLETE_SUCCESS, false, false, true);
+ checkDistributionDir();
+ checkCallbackLog();
+}
+
+/**
+ * Setup the state of the distribution directory for the test.
+ */
+function setupDistributionDir() {
+ if (IS_MACOSX) {
+ // Create files in the old distribution directory location to verify that
+ // the directory and its contents are moved to the new location on update.
+ let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
+ writeFile(testFile, "test\n");
+ testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
+ writeFile(testFile, "test\n");
+ }
+}
+
+/**
+ * Checks the state of the distribution directory.
+ */
+function checkDistributionDir() {
+ let distributionDir = getApplyDirFile(DIR_RESOURCES + "distribution", true);
+ if (IS_MACOSX) {
+ Assert.ok(distributionDir.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(distributionDir.path));
+
+ let testFile = getApplyDirFile(DIR_RESOURCES + "distribution/testFile", true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+
+ testFile = getApplyDirFile(DIR_RESOURCES + "distribution/test/testFile", true);
+ Assert.ok(testFile.exists(),
+ MSG_SHOULD_EXIST + getMsgPath(testFile.path));
+
+ distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+
+ checkUpdateLogContains(MOVE_OLD_DIST_DIR);
+ } else {
+ debugDump("testing that files aren't added with an add-if instruction " +
+ "when the file's destination directory doesn't exist");
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+ }
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js
new file mode 100644
index 000000000..616390f55
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js
@@ -0,0 +1,79 @@
+/* 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/.
+ */
+
+/* General Partial MAR File Patch Apply Test */
+
+function run_test() {
+ if (!setupTestCommon()) {
+ return;
+ }
+ gTestFiles = gTestFilesPartialSuccess;
+ gTestFiles[gTestFiles.length - 1].originalContents = null;
+ 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;
+ setupDistributionDir();
+ // The third parameter will test that a relative path that contains a
+ // directory traversal to the post update binary doesn't execute.
+ setupUpdaterTest(FILE_PARTIAL_MAR, false, "test/../");
+}
+
+/**
+ * Called after the call to setupUpdaterTest finishes.
+ */
+function setupUpdaterTestFinished() {
+ runUpdate(STATE_SUCCEEDED, false, 0, true);
+}
+
+/**
+ * Called after the call to runUpdate finishes.
+ */
+function runUpdateFinished() {
+ checkAppBundleModTime();
+ standardInit();
+ Assert.equal(readStatusState(), STATE_NONE,
+ "the status file state" + MSG_SHOULD_EQUAL);
+ Assert.ok(!gUpdateManager.activeUpdate,
+ "the active update should not be defined");
+ Assert.equal(gUpdateManager.updateCount, 1,
+ "the update manager updateCount attribute" + MSG_SHOULD_EQUAL);
+ Assert.equal(gUpdateManager.getUpdateAt(0).state, STATE_SUCCEEDED,
+ "the update state" + MSG_SHOULD_EQUAL);
+ checkPostUpdateRunningFile(false);
+ checkFilesAfterUpdateSuccess(getApplyDirFile);
+ checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
+ checkDistributionDir();
+ checkCallbackLog();
+}
+
+/**
+ * Setup the state of the distribution directory for the test.
+ */
+function setupDistributionDir() {
+ if (IS_MACOSX) {
+ // Create files in the old distribution directory location to verify that
+ // the directory and its contents are removed when there is a distribution
+ // directory in the new location.
+ let testFile = getApplyDirFile(DIR_MACOS + "distribution/testFile", true);
+ writeFile(testFile, "test\n");
+ testFile = getApplyDirFile(DIR_MACOS + "distribution/test/testFile", true);
+ writeFile(testFile, "test\n");
+ }
+}
+
+/**
+ * Checks the state of the distribution directory.
+ */
+function checkDistributionDir() {
+ if (IS_MACOSX) {
+ let distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
+ Assert.ok(!distributionDir.exists(),
+ MSG_SHOULD_NOT_EXIST + getMsgPath(distributionDir.path));
+ checkUpdateLogContains(REMOVE_OLD_DIST_DIR);
+ }
+}
diff --git a/toolkit/mozapps/update/tests/unit_service_updater/xpcshell.ini b/toolkit/mozapps/update/tests/unit_service_updater/xpcshell.ini
new file mode 100644
index 000000000..1d63f8583
--- /dev/null
+++ b/toolkit/mozapps/update/tests/unit_service_updater/xpcshell.ini
@@ -0,0 +1,156 @@
+; 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/.
+
+; Tests that require the updater binary and the maintenance service.
+
+[DEFAULT]
+tags = appupdate
+head = head_update.js
+tail =
+
+[bootstrapSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[invalidArgInstallDirPathTooLongFailureSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[invalidArgInstallDirPathTraversalFailureSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[invalidArgPatchDirPathTraversalFailureSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[invalidArgStageDirNotInInstallDirFailureSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[invalidArgWorkingDirPathLocalUNCFailureSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[invalidArgWorkingDirPathRelativeFailureSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marSuccessCompleteSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marSuccessPartialSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marFailurePartialSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marStageSuccessCompleteSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marStageSuccessPartialSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marStageFailurePartialSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marCallbackAppSuccessCompleteSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marCallbackAppSuccessPartialSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marCallbackAppStageSuccessCompleteSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marCallbackAppStageSuccessPartialSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marAppInUseSuccessCompleteSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marAppInUseStageFailureCompleteSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marFileLockedFailureCompleteSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marFileLockedFailurePartialSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marFileLockedStageFailureCompleteSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marFileLockedStageFailurePartialSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marFileInUseSuccessCompleteSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marFileInUseSuccessPartialSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marRMRFDirFileInUseSuccessCompleteSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marRMRFDirFileInUseSuccessPartialSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marFileInUseStageFailureCompleteSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marFileInUseStageFailurePartialSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marRMRFDirFileInUseStageFailureCompleteSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marRMRFDirFileInUseStageFailurePartialSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marAppApplyDirLockedStageFailureSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marAppApplyUpdateSuccessSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[marAppApplyUpdateStageSuccessSvc.js]
+skip-if = os == 'win' && debug && (os_version == '5.1' || os_version == '5.2')
+reason = bug 1291985
+run-sequentially = Uses the Mozilla Maintenance Service.
+[checkUpdaterSigSvc.js]