michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const INSTALL_LOCALE = "@AB_CD@"; michael@0: const MOZ_APP_NAME = "@MOZ_APP_NAME@"; michael@0: const BIN_SUFFIX = "@BIN_SUFFIX@"; michael@0: michael@0: #ifdef XP_WIN michael@0: // MOZ_APP_VENDOR is optional. michael@0: // On Windows, if MOZ_APP_VENDOR is not defined the updates directory will be michael@0: // located under %LOCALAPPDATA%\@MOZ_APP_BASENAME@\updates\TaskBarID michael@0: #ifdef MOZ_APP_VENDOR michael@0: const MOZ_APP_VENDOR = "@MOZ_APP_VENDOR@"; michael@0: #else michael@0: const MOZ_APP_VENDOR = ""; michael@0: #endif michael@0: michael@0: // MOZ_APP_BASENAME is not optional for tests. michael@0: const MOZ_APP_BASENAME = "@MOZ_APP_BASENAME@"; michael@0: #endif // XP_WIN michael@0: michael@0: const APP_INFO_NAME = "XPCShell"; michael@0: const APP_INFO_VENDOR = "Mozilla"; michael@0: michael@0: #ifdef XP_UNIX michael@0: const APP_BIN_SUFFIX = "-bin"; michael@0: #else michael@0: const APP_BIN_SUFFIX = "@BIN_SUFFIX@"; michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: const IS_WIN = true; michael@0: #else michael@0: const IS_WIN = false; michael@0: #endif michael@0: michael@0: #ifdef XP_MACOSX michael@0: const IS_MACOSX = true; michael@0: #ifdef MOZ_SHARK michael@0: const IS_SHARK = true; michael@0: #else michael@0: const IS_SHARK = false; michael@0: #endif michael@0: #else michael@0: const IS_MACOSX = false; michael@0: #endif michael@0: michael@0: #ifdef XP_UNIX michael@0: const IS_UNIX = true; michael@0: #else michael@0: const IS_UNIX = false; michael@0: #endif michael@0: michael@0: #ifdef ANDROID michael@0: const IS_ANDROID = true; michael@0: #else michael@0: const IS_ANDROID = false; michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: const IS_TOOLKIT_GONK = true; michael@0: #else michael@0: const IS_TOOLKIT_GONK = false; michael@0: #endif michael@0: michael@0: const USE_EXECV = IS_UNIX && !IS_MACOSX; michael@0: michael@0: #ifdef MOZ_VERIFY_MAR_SIGNATURE michael@0: const IS_MAR_CHECKS_ENABLED = true; michael@0: #else michael@0: const IS_MAR_CHECKS_ENABLED = false; michael@0: #endif michael@0: michael@0: const URL_HOST = "http://localhost"; michael@0: michael@0: const FILE_APP_BIN = MOZ_APP_NAME + APP_BIN_SUFFIX; michael@0: const FILE_COMPLETE_EXE = "complete.exe"; michael@0: const FILE_COMPLETE_MAR = "complete.mar"; michael@0: const FILE_HELPER_BIN = "TestAUSHelper" + BIN_SUFFIX; michael@0: const FILE_MAINTENANCE_SERVICE_BIN = "maintenanceservice.exe"; michael@0: const FILE_MAINTENANCE_SERVICE_INSTALLER_BIN = "maintenanceservice_installer.exe"; michael@0: const FILE_OLD_VERSION_MAR = "old_version.mar"; michael@0: const FILE_PARTIAL_EXE = "partial.exe"; michael@0: const FILE_PARTIAL_MAR = "partial.mar"; michael@0: const FILE_UPDATER_BIN = "updater" + BIN_SUFFIX; michael@0: const FILE_UPDATER_INI_BAK = "updater.ini.bak"; michael@0: const FILE_WRONG_CHANNEL_MAR = "wrong_product_channel.mar"; michael@0: michael@0: const LOG_COMPLETE_SUCCESS = "complete_log_success"; michael@0: const LOG_PARTIAL_SUCCESS = "partial_log_success"; michael@0: const LOG_PARTIAL_FAILURE = "partial_log_failure"; michael@0: michael@0: const LOG_SWITCH_SUCCESS = "rename_file: proceeding to rename the directory\n" + michael@0: "rename_file: proceeding to rename the directory\n" + michael@0: "Now, remove the tmpDir\n" + michael@0: "succeeded\n" + michael@0: "calling QuitProgressUI"; michael@0: michael@0: const ERR_RENAME_FILE = "rename_file: failed to rename file"; michael@0: const ERR_UNABLE_OPEN_DEST = "unable to open destination file"; michael@0: const ERR_BACKUP_DISCARD = "backup_discard: unable to remove"; michael@0: michael@0: const LOG_SVC_SUCCESSFUL_LAUNCH = "Process was started... waiting on result."; michael@0: michael@0: // All we care about is that the last modified time has changed so that Mac OS michael@0: // X Launch Services invalidates its cache so the test allows up to one minute michael@0: // difference in the last modified time. michael@0: const MAC_MAX_TIME_DIFFERENCE = 60000; michael@0: michael@0: // Time to wait for the test helper process before continuing the test michael@0: const TEST_HELPER_TIMEOUT = 100; michael@0: michael@0: // Time to wait for a check in the test before continuing the test michael@0: const TEST_CHECK_TIMEOUT = 100; michael@0: michael@0: // How many of TEST_CHECK_TIMEOUT to wait before we abort the test. michael@0: const MAX_TIMEOUT_RUNS = 2000; michael@0: michael@0: // Time in seconds the helper application should sleep.the helper's input and output files michael@0: const HELPER_SLEEP_TIMEOUT = 180; michael@0: michael@0: // Maximum number of milliseconds the process that is launched can run before michael@0: // the test will try to kill it. michael@0: const APP_TIMER_TIMEOUT = 120000; michael@0: michael@0: #ifdef XP_WIN michael@0: const PIPE_TO_NULL = ">nul"; michael@0: #else michael@0: const PIPE_TO_NULL = "> /dev/null 2>&1"; michael@0: #endif michael@0: michael@0: // This default value will be overridden when using the http server. michael@0: var gURLData = URL_HOST + "/"; michael@0: michael@0: var gTestID; michael@0: michael@0: var gTestserver; michael@0: michael@0: var gRegisteredServiceCleanup; michael@0: michael@0: var gXHR; michael@0: var gXHRCallback; michael@0: michael@0: var gUpdatePrompt; michael@0: var gUpdatePromptCallback; michael@0: michael@0: var gCheckFunc; michael@0: var gResponseBody; michael@0: var gResponseStatusCode = 200; michael@0: var gRequestURL; michael@0: var gUpdateCount; michael@0: var gUpdates; michael@0: var gStatusCode; michael@0: var gStatusText; michael@0: var gStatusResult; michael@0: michael@0: var gProcess; michael@0: var gAppTimer; michael@0: var gHandle; michael@0: michael@0: var gGREDirOrig; michael@0: var gAppDirOrig; michael@0: michael@0: var gServiceLaunchedCallbackLog = null; michael@0: var gServiceLaunchedCallbackArgs = null; michael@0: michael@0: // Variables are used instead of contants so tests can override these values if michael@0: // necessary. michael@0: var gCallbackBinFile = "callback_app" + BIN_SUFFIX; michael@0: var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"]; michael@0: var gStageUpdate = false; michael@0: var gSwitchApp = false; michael@0: var gDisableReplaceFallback = false; michael@0: var gUseTestAppDir = true; michael@0: michael@0: var gTimeoutRuns = 0; michael@0: michael@0: // Environment related globals michael@0: var gShouldResetEnv = undefined; michael@0: var gAddedEnvXRENoWindowsCrashDialog = false; michael@0: var gEnvXPCOMDebugBreak; michael@0: var gEnvXPCOMMemLeakLog; michael@0: var gEnvDyldLibraryPath; michael@0: var gEnvLdLibraryPath; michael@0: michael@0: // Set to true to log additional information for debugging. To log additional michael@0: // information for an individual test set DEBUG_AUS_TEST to true in the test's michael@0: // run_test function. michael@0: var DEBUG_AUS_TEST = true; michael@0: // Never set DEBUG_TEST_LOG to true except when running tests locally or on the michael@0: // try server since this will force a test that failed a parallel run to fail michael@0: // when the same test runs non-parallel so the log from parallel test run can michael@0: // be displayed in the log. michael@0: var DEBUG_TEST_LOG = false; michael@0: // Set to false to keep the log file from the failed parallel test run. michael@0: var gDeleteLogFile = true; michael@0: var gRealDump; michael@0: var gTestLogText = ""; michael@0: var gPassed; michael@0: michael@0: #include ../shared.js michael@0: michael@0: var gTestFiles = []; michael@0: var gTestDirs = []; michael@0: michael@0: // Common files for both successful and failed updates. michael@0: var gTestFilesCommon = [ michael@0: { michael@0: description : "Should never change", michael@0: fileName : FILE_UPDATE_SETTINGS_INI, michael@0: relPathDir : "a/b/", michael@0: originalContents : "ShouldNotBeReplaced\n", michael@0: compareContents : "ShouldNotBeReplaced\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : 0o767, michael@0: comparePerms : 0o767 michael@0: }, { michael@0: description : "Should never change", michael@0: fileName : "channel-prefs.js", michael@0: relPathDir : "a/b/defaults/pref/", michael@0: originalContents : "ShouldNotBeReplaced\n", michael@0: compareContents : "ShouldNotBeReplaced\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : 0o767, michael@0: comparePerms : 0o767 michael@0: }]; michael@0: michael@0: // Files for a complete successful update. This can be used for a complete michael@0: // failed update by calling setTestFilesAndDirsForFailure. michael@0: var gTestFilesCompleteSuccess = [ michael@0: { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "precomplete", michael@0: relPathDir : "", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "partial_precomplete", michael@0: compareFile : "complete_precomplete", michael@0: originalPerms : 0o666, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "searchpluginstext0", michael@0: relPathDir : "a/b/searchplugins/", michael@0: originalContents : "ToBeReplacedWithFromComplete\n", michael@0: compareContents : "FromComplete\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : 0o775, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "searchpluginspng1.png", michael@0: relPathDir : "a/b/searchplugins/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : null, michael@0: compareFile : "complete.png", michael@0: originalPerms : null, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "searchpluginspng0.png", michael@0: relPathDir : "a/b/searchplugins/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "partial.png", michael@0: compareFile : "complete.png", michael@0: originalPerms : 0o666, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "removed-files", michael@0: relPathDir : "a/b/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "partial_removed-files", michael@0: compareFile : "complete_removed-files", michael@0: originalPerms : 0o666, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest if the parent directory " + michael@0: "exists (add-if)", michael@0: fileName : "extensions1text0", michael@0: relPathDir : "a/b/distribution/extensions/extensions1/", michael@0: originalContents : null, michael@0: compareContents : "FromComplete\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : null, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest if the parent directory " + michael@0: "exists (add-if)", michael@0: fileName : "extensions1png1.png", michael@0: relPathDir : "a/b/distribution/extensions/extensions1/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "partial.png", michael@0: compareFile : "complete.png", michael@0: originalPerms : 0o666, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest if the parent directory " + michael@0: "exists (add-if)", michael@0: fileName : "extensions1png0.png", michael@0: relPathDir : "a/b/distribution/extensions/extensions1/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : null, michael@0: compareFile : "complete.png", michael@0: originalPerms : null, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest if the parent directory " + michael@0: "exists (add-if)", michael@0: fileName : "extensions0text0", michael@0: relPathDir : "a/b/distribution/extensions/extensions0/", michael@0: originalContents : "ToBeReplacedWithFromComplete\n", michael@0: compareContents : "FromComplete\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : null, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest if the parent directory " + michael@0: "exists (add-if)", michael@0: fileName : "extensions0png1.png", michael@0: relPathDir : "a/b/distribution/extensions/extensions0/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : null, michael@0: compareFile : "complete.png", michael@0: originalPerms : null, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest if the parent directory " + michael@0: "exists (add-if)", michael@0: fileName : "extensions0png0.png", michael@0: relPathDir : "a/b/distribution/extensions/extensions0/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : null, michael@0: compareFile : "complete.png", michael@0: originalPerms : null, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "exe0.exe", michael@0: relPathDir : "a/b/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : FILE_HELPER_BIN, michael@0: compareFile : FILE_COMPLETE_EXE, michael@0: originalPerms : 0o777, michael@0: comparePerms : 0o755 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "10text0", michael@0: relPathDir : "a/b/1/10/", michael@0: originalContents : "ToBeReplacedWithFromComplete\n", michael@0: compareContents : "FromComplete\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : 0o767, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "0exe0.exe", michael@0: relPathDir : "a/b/0/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : FILE_HELPER_BIN, michael@0: compareFile : FILE_COMPLETE_EXE, michael@0: originalPerms : 0o777, michael@0: comparePerms : 0o755 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "00text1", michael@0: relPathDir : "a/b/0/00/", michael@0: originalContents : "ToBeReplacedWithFromComplete\n", michael@0: compareContents : "FromComplete\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : 0o677, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "00text0", michael@0: relPathDir : "a/b/0/00/", michael@0: originalContents : "ToBeReplacedWithFromComplete\n", michael@0: compareContents : "FromComplete\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : 0o775, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "00png0.png", michael@0: relPathDir : "a/b/0/00/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : null, michael@0: compareFile : "complete.png", michael@0: originalPerms : 0o776, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Removed by precomplete (remove)", michael@0: fileName : "20text0", michael@0: relPathDir : "a/b/2/20/", michael@0: originalContents : "ToBeDeleted\n", michael@0: compareContents : null, michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : null, michael@0: comparePerms : null michael@0: }, { michael@0: description : "Removed by precomplete (remove)", michael@0: fileName : "20png0.png", michael@0: relPathDir : "a/b/2/20/", michael@0: originalContents : "ToBeDeleted\n", michael@0: compareContents : null, michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : null, michael@0: comparePerms : null michael@0: }]; michael@0: michael@0: // Concatenate the common files to the end of the array. michael@0: gTestFilesCompleteSuccess = gTestFilesCompleteSuccess.concat(gTestFilesCommon); michael@0: michael@0: // Files for a partial successful update. This can be used for a partial failed michael@0: // update by calling setTestFilesAndDirsForFailure. michael@0: var gTestFilesPartialSuccess = [ michael@0: { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "precomplete", michael@0: relPathDir : "", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "complete_precomplete", michael@0: compareFile : "partial_precomplete", michael@0: originalPerms : 0o666, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "searchpluginstext0", michael@0: relPathDir : "a/b/searchplugins/", michael@0: originalContents : "ToBeReplacedWithFromPartial\n", michael@0: compareContents : "FromPartial\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : 0o775, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Patched by update.manifest if the file exists " + michael@0: "(patch-if)", michael@0: fileName : "searchpluginspng1.png", michael@0: relPathDir : "a/b/searchplugins/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "complete.png", michael@0: compareFile : "partial.png", michael@0: originalPerms : 0o666, michael@0: comparePerms : 0o666 michael@0: }, { michael@0: description : "Patched by update.manifest if the file exists " + michael@0: "(patch-if)", michael@0: fileName : "searchpluginspng0.png", michael@0: relPathDir : "a/b/searchplugins/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "complete.png", michael@0: compareFile : "partial.png", michael@0: originalPerms : 0o666, michael@0: comparePerms : 0o666 michael@0: }, { michael@0: description : "Added by update.manifest if the parent directory " + michael@0: "exists (add-if)", michael@0: fileName : "extensions1text0", michael@0: relPathDir : "a/b/distribution/extensions/extensions1/", michael@0: originalContents : null, michael@0: compareContents : "FromPartial\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : null, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Patched by update.manifest if the parent directory " + michael@0: "exists (patch-if)", michael@0: fileName : "extensions1png1.png", michael@0: relPathDir : "a/b/distribution/extensions/extensions1/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "complete.png", michael@0: compareFile : "partial.png", michael@0: originalPerms : 0o666, michael@0: comparePerms : 0o666 michael@0: }, { michael@0: description : "Patched by update.manifest if the parent directory " + michael@0: "exists (patch-if)", michael@0: fileName : "extensions1png0.png", michael@0: relPathDir : "a/b/distribution/extensions/extensions1/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "complete.png", michael@0: compareFile : "partial.png", michael@0: originalPerms : 0o666, michael@0: comparePerms : 0o666 michael@0: }, { michael@0: description : "Added by update.manifest if the parent directory " + michael@0: "exists (add-if)", michael@0: fileName : "extensions0text0", michael@0: relPathDir : "a/b/distribution/extensions/extensions0/", michael@0: originalContents : "ToBeReplacedWithFromPartial\n", michael@0: compareContents : "FromPartial\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : 0o644, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Patched by update.manifest if the parent directory " + michael@0: "exists (patch-if)", michael@0: fileName : "extensions0png1.png", michael@0: relPathDir : "a/b/distribution/extensions/extensions0/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "complete.png", michael@0: compareFile : "partial.png", michael@0: originalPerms : 0o644, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Patched by update.manifest if the parent directory " + michael@0: "exists (patch-if)", michael@0: fileName : "extensions0png0.png", michael@0: relPathDir : "a/b/distribution/extensions/extensions0/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "complete.png", michael@0: compareFile : "partial.png", michael@0: originalPerms : 0o644, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Patched by update.manifest (patch)", michael@0: fileName : "exe0.exe", michael@0: relPathDir : "a/b/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : FILE_COMPLETE_EXE, michael@0: compareFile : FILE_PARTIAL_EXE, michael@0: originalPerms : 0o755, michael@0: comparePerms : 0o755 michael@0: }, { michael@0: description : "Patched by update.manifest (patch)", michael@0: fileName : "0exe0.exe", michael@0: relPathDir : "a/b/0/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : FILE_COMPLETE_EXE, michael@0: compareFile : FILE_PARTIAL_EXE, michael@0: originalPerms : 0o755, michael@0: comparePerms : 0o755 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "00text0", michael@0: relPathDir : "a/b/0/00/", michael@0: originalContents : "ToBeReplacedWithFromPartial\n", michael@0: compareContents : "FromPartial\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : 0o644, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Patched by update.manifest (patch)", michael@0: fileName : "00png0.png", michael@0: relPathDir : "a/b/0/00/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : "complete.png", michael@0: compareFile : "partial.png", michael@0: originalPerms : 0o666, michael@0: comparePerms : 0o666 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "20text0", michael@0: relPathDir : "a/b/2/20/", michael@0: originalContents : null, michael@0: compareContents : "FromPartial\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : null, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "20png0.png", michael@0: relPathDir : "a/b/2/20/", michael@0: originalContents : null, michael@0: compareContents : null, michael@0: originalFile : null, michael@0: compareFile : "partial.png", michael@0: originalPerms : null, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Added by update.manifest (add)", michael@0: fileName : "00text2", michael@0: relPathDir : "a/b/0/00/", michael@0: originalContents : null, michael@0: compareContents : "FromPartial\n", michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : null, michael@0: comparePerms : 0o644 michael@0: }, { michael@0: description : "Removed by update.manifest (remove)", michael@0: fileName : "10text0", michael@0: relPathDir : "a/b/1/10/", michael@0: originalContents : "ToBeDeleted\n", michael@0: compareContents : null, michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : null, michael@0: comparePerms : null michael@0: }, { michael@0: description : "Removed by update.manifest (remove)", michael@0: fileName : "00text1", michael@0: relPathDir : "a/b/0/00/", michael@0: originalContents : "ToBeDeleted\n", michael@0: compareContents : null, michael@0: originalFile : null, michael@0: compareFile : null, michael@0: originalPerms : null, michael@0: comparePerms : null michael@0: }]; michael@0: michael@0: // Concatenate the common files to the end of the array. michael@0: gTestFilesPartialSuccess = gTestFilesPartialSuccess.concat(gTestFilesCommon); michael@0: michael@0: /** michael@0: * The mar files used for the updater tests contain the following remove michael@0: * operations. michael@0: * michael@0: * partial and complete test mar remove operations michael@0: * ----------------------------------------------- michael@0: * remove "text1" michael@0: * remove "text0" michael@0: * rmrfdir "9/99/" michael@0: * rmdir "9/99/" michael@0: * rmrfdir "9/98/" michael@0: * rmrfdir "9/97/" michael@0: * rmrfdir "9/96/" michael@0: * rmrfdir "9/95/" michael@0: * rmrfdir "9/95/" michael@0: * rmrfdir "9/94/" michael@0: * rmdir "9/94/" michael@0: * rmdir "9/93/" michael@0: * rmdir "9/92/" michael@0: * rmdir "9/91/" michael@0: * rmdir "9/90/" michael@0: * rmdir "9/90/" michael@0: * rmrfdir "8/89/" michael@0: * rmdir "8/89/" michael@0: * rmrfdir "8/88/" michael@0: * rmrfdir "8/87/" michael@0: * rmrfdir "8/86/" michael@0: * rmrfdir "8/85/" michael@0: * rmrfdir "8/85/" michael@0: * rmrfdir "8/84/" michael@0: * rmdir "8/84/" michael@0: * rmdir "8/83/" michael@0: * rmdir "8/82/" michael@0: * rmdir "8/81/" michael@0: * rmdir "8/80/" michael@0: * rmdir "8/80/" michael@0: * rmrfdir "7/" michael@0: * rmdir "6/" michael@0: * remove "5/text1" michael@0: * remove "5/text0" michael@0: * rmrfdir "5/" michael@0: * remove "4/text1" michael@0: * remove "4/text0" michael@0: * remove "4/exe0.exe" michael@0: * rmdir "4/" michael@0: * remove "3/text1" michael@0: * remove "3/text0" michael@0: * michael@0: * partial test mar additional remove operations michael@0: * --------------------------------------------- michael@0: * remove "0/00/00text1" michael@0: * remove "1/10/10text0" michael@0: * rmdir "1/10/" michael@0: * rmdir "1/" michael@0: */ michael@0: var gTestDirsCommon = [ michael@0: { michael@0: relPathDir : "a/b/3/", michael@0: dirRemoved : false, michael@0: files : ["3text0", "3text1"], michael@0: filesRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/4/", michael@0: dirRemoved : true, michael@0: files : ["4text0", "4text1"], michael@0: filesRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/5/", michael@0: dirRemoved : true, michael@0: files : ["5test.exe", "5text0", "5text1"], michael@0: filesRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/6/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/7/", michael@0: dirRemoved : true, michael@0: files : ["7text0", "7text1"], michael@0: subDirs : ["70/", "71/"], michael@0: subDirFiles : ["7xtest.exe", "7xtext0", "7xtext1"] michael@0: }, { michael@0: relPathDir : "a/b/8/", michael@0: dirRemoved : false michael@0: }, { michael@0: relPathDir : "a/b/8/80/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/8/81/", michael@0: dirRemoved : false, michael@0: files : ["81text0", "81text1"] michael@0: }, { michael@0: relPathDir : "a/b/8/82/", michael@0: dirRemoved : false, michael@0: subDirs : ["820/", "821/"] michael@0: }, { michael@0: relPathDir : "a/b/8/83/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/8/84/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/8/85/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/8/86/", michael@0: dirRemoved : true, michael@0: files : ["86text0", "86text1"] michael@0: }, { michael@0: relPathDir : "a/b/8/87/", michael@0: dirRemoved : true, michael@0: subDirs : ["870/", "871/"], michael@0: subDirFiles : ["87xtext0", "87xtext1"] michael@0: }, { michael@0: relPathDir : "a/b/8/88/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/8/89/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/9/90/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/9/91/", michael@0: dirRemoved : false, michael@0: files : ["91text0", "91text1"] michael@0: }, { michael@0: relPathDir : "a/b/9/92/", michael@0: dirRemoved : false, michael@0: subDirs : ["920/", "921/"] michael@0: }, { michael@0: relPathDir : "a/b/9/93/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/9/94/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/9/95/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/9/96/", michael@0: dirRemoved : true, michael@0: files : ["96text0", "96text1"] michael@0: }, { michael@0: relPathDir : "a/b/9/97/", michael@0: dirRemoved : true, michael@0: subDirs : ["970/", "971/"], michael@0: subDirFiles : ["97xtext0", "97xtext1"] michael@0: }, { michael@0: relPathDir : "a/b/9/98/", michael@0: dirRemoved : true michael@0: }, { michael@0: relPathDir : "a/b/9/99/", michael@0: dirRemoved : true michael@0: }]; michael@0: michael@0: // Directories for a complete successful update. This array can be used for a michael@0: // complete failed update by calling setTestFilesAndDirsForFailure. michael@0: var gTestDirsCompleteSuccess = [ michael@0: { michael@0: description : "Removed by precomplete (rmdir)", michael@0: relPathDir : "a/b/2/20/", michael@0: dirRemoved : true michael@0: }, { michael@0: description : "Removed by precomplete (rmdir)", michael@0: relPathDir : "a/b/2/", michael@0: dirRemoved : true michael@0: }]; michael@0: michael@0: // Concatenate the common files to the beginning of the array. michael@0: gTestDirsCompleteSuccess = gTestDirsCommon.concat(gTestDirsCompleteSuccess); michael@0: michael@0: // Directories for a partial successful update. This array can be used for a michael@0: // partial failed update by calling setTestFilesAndDirsForFailure. michael@0: var gTestDirsPartialSuccess = [ michael@0: { michael@0: description : "Removed by update.manifest (rmdir)", michael@0: relPathDir : "a/b/1/10/", michael@0: dirRemoved : true michael@0: }, { michael@0: description : "Removed by update.manifest (rmdir)", michael@0: relPathDir : "a/b/1/", michael@0: dirRemoved : true michael@0: }]; michael@0: michael@0: // Concatenate the common files to the beginning of the array. michael@0: gTestDirsPartialSuccess = gTestDirsCommon.concat(gTestDirsPartialSuccess); michael@0: michael@0: // Extra directories to check for existence for both complete and partial michael@0: // updates. Whether they exist or not is set when calling setupUpdaterTest. michael@0: var gTestExtraDirs = [ michael@0: { michael@0: relPathDir : DIR_UPDATED, michael@0: dirExists : false michael@0: }, { michael@0: relPathDir : DIR_TOBEDELETED, michael@0: dirExists : false michael@0: }]; michael@0: michael@0: // This makes it possible to run most tests on xulrunner where the update michael@0: // channel default preference is not set. michael@0: if (MOZ_APP_NAME == "xulrunner") { michael@0: try { michael@0: gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL); michael@0: } catch (e) { michael@0: setUpdateChannel("test_channel"); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Helper function for setting up the test environment. michael@0: */ michael@0: function setupTestCommon() { michael@0: logTestInfo("start - general test setup"); michael@0: michael@0: do_test_pending(); michael@0: michael@0: if (gTestID) { michael@0: do_throw("setupTestCommon should only be called once!"); michael@0: } michael@0: michael@0: let caller = Components.stack.caller; michael@0: gTestID = caller.filename.toString().split("/").pop().split(".")[0]; michael@0: michael@0: if (DEBUG_TEST_LOG) { michael@0: let logFile = do_get_file(gTestID + ".log", true); michael@0: if (logFile.exists()) { michael@0: gPassed = false; michael@0: logTestInfo("start - dumping previous test run log"); michael@0: logTestInfo("\n" + readFile(logFile) + "\n"); michael@0: logTestInfo("finish - dumping previous test run log"); michael@0: if (gDeleteLogFile) { michael@0: logFile.remove(false); michael@0: } michael@0: do_throw("The parallel run of this test failed. Failing non-parallel " + michael@0: "test so the log from the parallel run can be displayed in " + michael@0: "non-parallel log.") michael@0: } else { michael@0: gRealDump = dump; michael@0: dump = dumpOverride; michael@0: } michael@0: } michael@0: michael@0: // Don't attempt to show a prompt when an update finishes. michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, true); michael@0: michael@0: gGREDirOrig = getGREDir(); michael@0: gAppDirOrig = getAppBaseDir(); michael@0: michael@0: let applyDir = getApplyDirFile(null, true).parent; michael@0: michael@0: // Try to remove the directory used to apply updates and the updates directory michael@0: // on platforms other than Windows. Since the test hasn't ran yet and the michael@0: // directory shouldn't exist finished this is non-fatal for the test. michael@0: if (applyDir.exists()) { michael@0: logTestInfo("attempting to remove directory. Path: " + applyDir.path); michael@0: try { michael@0: removeDirRecursive(applyDir); michael@0: } catch (e) { michael@0: logTestInfo("non-fatal error removing directory. Path: " + michael@0: applyDir.path + ", Exception: " + e); michael@0: } michael@0: } michael@0: michael@0: // adjustGeneralPaths registers a cleanup function that calls end_test when michael@0: // it is defined as a function. michael@0: adjustGeneralPaths(); michael@0: michael@0: // Remove the updates directory on Windows which is located outside of the michael@0: // application directory after the call to adjustGeneralPaths has set it up. michael@0: // Since the test hasn't ran yet and the directory shouldn't exist finished michael@0: // this is non-fatal for the test. michael@0: if (IS_WIN) { michael@0: let updatesDir = getMockUpdRootD(); michael@0: if (updatesDir.exists()) { michael@0: logTestInfo("attempting to remove directory. Path: " + updatesDir.path); michael@0: try { michael@0: removeDirRecursive(updatesDir); michael@0: } catch (e) { michael@0: logTestInfo("non-fatal error removing directory. Path: " + michael@0: updatesDir.path + ", Exception: " + e); michael@0: } michael@0: } michael@0: } michael@0: michael@0: logTestInfo("finish - general test setup"); michael@0: } michael@0: michael@0: /** michael@0: * Nulls out the most commonly used global vars used by tests to prevent leaks michael@0: * as needed and attempts to restore the system to its original state. michael@0: */ michael@0: function cleanupTestCommon() { michael@0: logTestInfo("start - general test cleanup"); michael@0: michael@0: // Force the update manager to reload the update data to prevent it from michael@0: // writing the old data to the files that have just been removed. michael@0: reloadUpdateManagerData(); michael@0: michael@0: if (gChannel) { michael@0: gPrefRoot.removeObserver(PREF_APP_UPDATE_CHANNEL, observer); michael@0: } michael@0: michael@0: // Call app update's observe method passing xpcom-shutdown to test that the michael@0: // shutdown of app update runs without throwing or leaking. The observer michael@0: // method is used directly instead of calling notifyObservers so components michael@0: // outside of the scope of this test don't assert and thereby cause app update michael@0: // tests to fail. michael@0: gAUS.observe(null, "xpcom-shutdown", ""); michael@0: michael@0: if (gXHR) { michael@0: gXHRCallback = null; michael@0: michael@0: gXHR.responseXML = null; michael@0: // null out the event handlers to prevent a mFreeCount leak of 1 michael@0: gXHR.onerror = null; michael@0: gXHR.onload = null; michael@0: gXHR.onprogress = null; michael@0: michael@0: gXHR = null; michael@0: } michael@0: michael@0: gTestserver = null; michael@0: michael@0: if (IS_UNIX) { michael@0: // This will delete the launch script if it exists. michael@0: getLaunchScript(); michael@0: } michael@0: michael@0: if (IS_WIN && MOZ_APP_BASENAME) { michael@0: let appDir = getApplyDirFile(null, true); michael@0: let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla"; michael@0: const REG_PATH = "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME + michael@0: "\\TaskBarIDs"; michael@0: let key = AUS_Cc["@mozilla.org/windows-registry-key;1"]. michael@0: createInstance(AUS_Ci.nsIWindowsRegKey); michael@0: try { michael@0: key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH, michael@0: AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); michael@0: if (key.hasValue(appDir.path)) { michael@0: key.removeValue(appDir.path); michael@0: } michael@0: } catch (e) { michael@0: } michael@0: try { michael@0: key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, REG_PATH, michael@0: AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); michael@0: if (key.hasValue(appDir.path)) { michael@0: key.removeValue(appDir.path); michael@0: } michael@0: } catch (e) { michael@0: } michael@0: } michael@0: michael@0: // The updates directory is located outside of the application directory on michael@0: // Windows so it also needs to be removed. michael@0: if (IS_WIN) { michael@0: let updatesDir = getMockUpdRootD(); michael@0: // Try to remove the directory used to apply updates. Since the test has michael@0: // already finished this is non-fatal for the test. michael@0: if (updatesDir.exists()) { michael@0: logTestInfo("attempting to remove directory. Path: " + updatesDir.path); michael@0: try { michael@0: removeDirRecursive(updatesDir); michael@0: } catch (e) { michael@0: logTestInfo("non-fatal error removing directory. Path: " + michael@0: updatesDir.path + ", Exception: " + e); michael@0: } michael@0: } michael@0: } michael@0: michael@0: let applyDir = getApplyDirFile(null, true).parent; michael@0: michael@0: // Try to remove the directory used to apply updates. Since the test has michael@0: // already finished this is non-fatal for the test. michael@0: if (applyDir.exists()) { michael@0: logTestInfo("attempting to remove directory. Path: " + applyDir.path); michael@0: try { michael@0: removeDirRecursive(applyDir); michael@0: } catch (e) { michael@0: logTestInfo("non-fatal error removing directory. Path: " + michael@0: applyDir.path + ", Exception: " + e); michael@0: } michael@0: } michael@0: michael@0: resetEnvironment(); michael@0: michael@0: logTestInfo("finish - general test cleanup"); michael@0: michael@0: if (gRealDump) { michael@0: dump = gRealDump; michael@0: gRealDump = null; michael@0: } michael@0: michael@0: if (DEBUG_TEST_LOG && !gPassed) { michael@0: let fos = AUS_Cc["@mozilla.org/network/file-output-stream;1"]. michael@0: createInstance(AUS_Ci.nsIFileOutputStream); michael@0: let logFile = do_get_file(gTestID + ".log", true); michael@0: if (!logFile.exists()) { michael@0: logFile.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE); michael@0: } michael@0: fos.init(logFile, MODE_WRONLY | MODE_CREATE | MODE_APPEND, PERMS_FILE, 0); michael@0: fos.write(gTestLogText, gTestLogText.length); michael@0: fos.close(); michael@0: } michael@0: michael@0: if (DEBUG_TEST_LOG) { michael@0: gTestLogText = null; michael@0: } else { michael@0: let logFile = do_get_file(gTestID + ".log", true); michael@0: if (logFile.exists()) { michael@0: logFile.remove(false); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Helper function to store the log output of calls to dump in a variable so the michael@0: * values can be written to a file for a parallel run of a test and printed to michael@0: * the log file when the test runs synchronously. michael@0: */ michael@0: function dumpOverride(aText) { michael@0: gTestLogText += aText; michael@0: gRealDump(aText); michael@0: } michael@0: michael@0: /** michael@0: * Helper function that calls do_test_finished that tracks whether a parallel michael@0: * run of a test passed when it runs synchronously so the log output can be michael@0: * inspected. michael@0: */ michael@0: function doTestFinish() { michael@0: if (gPassed === undefined) { michael@0: gPassed = true; michael@0: } michael@0: do_test_finished(); michael@0: } michael@0: michael@0: /** michael@0: * Sets the most commonly used preferences used by tests michael@0: */ michael@0: function setDefaultPrefs() { michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true); michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_METRO_ENABLED, true); michael@0: // Don't display UI for a successful installation. Some apps may not set this michael@0: // pref to false like Firefox does. michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_SHOW_INSTALLED_UI, false); michael@0: // Enable Update logging michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater binary tests that sets the appropriate values michael@0: * to check for update failures. michael@0: */ michael@0: function setTestFilesAndDirsForFailure() { michael@0: gTestFiles.forEach(function STFADFF_Files(aTestFile) { michael@0: aTestFile.compareContents = aTestFile.originalContents; michael@0: aTestFile.compareFile = aTestFile.originalFile; michael@0: aTestFile.comparePerms = aTestFile.originalPerms; michael@0: }); michael@0: michael@0: gTestDirs.forEach(function STFADFF_Dirs(aTestDir) { michael@0: aTestDir.dirRemoved = false; michael@0: if (aTestDir.filesRemoved) { michael@0: aTestDir.filesRemoved = false; michael@0: } michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Initializes the most commonly used settings and creates an instance of the michael@0: * update service stub. michael@0: */ michael@0: function standardInit() { michael@0: createAppInfo("xpcshell@tests.mozilla.org", APP_INFO_NAME, "1.0", "2.0"); michael@0: setDefaultPrefs(); michael@0: // Initialize the update service stub component michael@0: initUpdateServiceStub(); michael@0: } michael@0: michael@0: /** michael@0: * Custom path handler for the http server michael@0: * michael@0: * @param aMetadata michael@0: * The http metadata for the request. michael@0: * @param aResponse michael@0: * The http response for the request. michael@0: */ michael@0: function pathHandler(aMetadata, aResponse) { michael@0: aResponse.setHeader("Content-Type", "text/xml", false); michael@0: aResponse.setStatusLine(aMetadata.httpVersion, gResponseStatusCode, "OK"); michael@0: aResponse.bodyOutputStream.write(gResponseBody, gResponseBody.length); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for getting the application version from the application.ini michael@0: * file. This will look in both the GRE and the application directories for the michael@0: * application.ini file. michael@0: * michael@0: * @return The version string from the application.ini file. michael@0: * @throws If the application.ini file is not found. michael@0: */ michael@0: function getAppVersion() { michael@0: // Read the application.ini and use its application version. michael@0: let iniFile = gGREDirOrig.clone(); michael@0: iniFile.append("application.ini"); michael@0: if (!iniFile.exists()) { michael@0: iniFile = gAppDirOrig.clone(); michael@0: iniFile.append("application.ini"); michael@0: } michael@0: if (!iniFile.exists()) { michael@0: do_throw("Unable to find application.ini!"); michael@0: } michael@0: let iniParser = AUS_Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. michael@0: getService(AUS_Ci.nsIINIParserFactory). michael@0: createINIParser(iniFile); michael@0: return iniParser.getString("App", "Version"); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for getting the relative path to the directory where the michael@0: * application binary is located (e.g. /dir.app/). michael@0: * michael@0: * Note: The dir.app subdirectory under is needed for michael@0: * platforms other than Mac OS X so the tests can run in parallel due to michael@0: * update staging creating a lock file named moz_update_in_progress.lock in michael@0: * the parent directory of the installation directory. michael@0: * michael@0: * @return The relative path to the directory where application binary is michael@0: * located. michael@0: */ michael@0: function getApplyDirPath() { michael@0: return gTestID + "/dir.app/"; michael@0: } michael@0: michael@0: /** michael@0: * Helper function for getting the nsIFile for a file in the directory where the michael@0: * update will be applied. michael@0: * michael@0: * The files for the update are located two directories below the apply to michael@0: * directory since Mac OS X sets the last modified time for the root directory michael@0: * to the current time and if the update changes any files in the root directory michael@0: * then it wouldn't be possible to test (bug 600098). michael@0: * michael@0: * @param aRelPath (optional) michael@0: * The relative path to the file or directory to get from the root of michael@0: * the test's directory. If not specified the test's directory will be michael@0: * returned. michael@0: * @param aAllowNonexistent (optional) michael@0: * Whether the file must exist. If false or not specified the file must michael@0: * exist or the function will throw. michael@0: * @return The nsIFile for the file in the directory where the update will be michael@0: * applied. michael@0: */ michael@0: function getApplyDirFile(aRelPath, aAllowNonexistent) { michael@0: let relpath = getApplyDirPath() + (aRelPath ? aRelPath : ""); michael@0: return do_get_file(relpath, aAllowNonexistent); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for getting the relative path to the directory where the michael@0: * test data files are located. michael@0: * michael@0: * @return The relative path to the directory where the test data files are michael@0: * located. michael@0: */ michael@0: function getTestDirPath() { michael@0: return "../data/"; michael@0: } michael@0: michael@0: /** michael@0: * Helper function for getting the nsIFile for a file in the test data michael@0: * directory. michael@0: * michael@0: * @param aRelPath (optional) michael@0: * The relative path to the file or directory to get from the root of michael@0: * the test's data directory. If not specified the test's data michael@0: * directory will be returned. michael@0: * @return The nsIFile for the file in the test data directory. michael@0: * @throws If the file or directory does not exist. michael@0: */ michael@0: function getTestDirFile(aRelPath) { michael@0: let relpath = getTestDirPath() + (aRelPath ? aRelPath : ""); michael@0: return do_get_file(relpath, false); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for getting the directory that was updated. This can either michael@0: * be the directory where the application binary is located or the directory michael@0: * that contains the staged update. michael@0: */ michael@0: function getUpdatedDirPath() { michael@0: return getApplyDirPath() + (gStageUpdate ? DIR_UPDATED + "/" : ""); michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: XPCOMUtils.defineLazyGetter(this, "gInstallDirPathHash", michael@0: function test_gInstallDirPathHash() { michael@0: // Figure out where we should check for a cached hash value michael@0: if (!MOZ_APP_BASENAME) michael@0: return null; michael@0: michael@0: let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla"; michael@0: let appDir = getApplyDirFile(null, true); michael@0: michael@0: const REG_PATH = "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME + michael@0: "\\TaskBarIDs"; michael@0: let regKey = AUS_Cc["@mozilla.org/windows-registry-key;1"]. michael@0: createInstance(AUS_Ci.nsIWindowsRegKey); michael@0: try { michael@0: regKey.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH, michael@0: AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); michael@0: regKey.writeStringValue(appDir.path, gTestID); michael@0: return gTestID; michael@0: } catch (e) { michael@0: } michael@0: michael@0: try { michael@0: regKey.create(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, REG_PATH, michael@0: AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); michael@0: regKey.writeStringValue(appDir.path, gTestID); michael@0: return gTestID; michael@0: } catch (e) { michael@0: logTestInfo("failed to create registry key. Registry Path: " + REG_PATH + michael@0: ", Key Name: " + appDir.path + ", Key Value: " + gTestID + michael@0: ", Exception " + e); michael@0: } michael@0: return null; michael@0: }); michael@0: michael@0: XPCOMUtils.defineLazyGetter(this, "gLocalAppDataDir", michael@0: function test_gLocalAppDataDir() { michael@0: const CSIDL_LOCAL_APPDATA = 0x1c; michael@0: michael@0: AUS_Cu.import("resource://gre/modules/ctypes.jsm"); michael@0: let lib = ctypes.open("shell32"); michael@0: let SHGetSpecialFolderPath = lib.declare("SHGetSpecialFolderPathW", michael@0: ctypes.winapi_abi, michael@0: ctypes.bool, /* bool(return) */ michael@0: ctypes.int32_t, /* HWND hwndOwner */ michael@0: ctypes.jschar.ptr, /* LPTSTR lpszPath */ michael@0: ctypes.int32_t, /* int csidl */ michael@0: ctypes.bool /* BOOL fCreate */); michael@0: michael@0: let aryPathLocalAppData = ctypes.jschar.array()(260); michael@0: let rv = SHGetSpecialFolderPath(0, aryPathLocalAppData, CSIDL_LOCAL_APPDATA, false); michael@0: lib.close(); michael@0: michael@0: let pathLocalAppData = aryPathLocalAppData.readString(); // Convert the c-string to js-string michael@0: let updatesDir = AUS_Cc["@mozilla.org/file/local;1"]. michael@0: createInstance(AUS_Ci.nsILocalFile); michael@0: updatesDir.initWithPath(pathLocalAppData); michael@0: return updatesDir; michael@0: }); michael@0: michael@0: XPCOMUtils.defineLazyGetter(this, "gProgFilesDir", michael@0: function test_gProgFilesDir() { michael@0: const CSIDL_PROGRAM_FILES = 0x26; michael@0: michael@0: AUS_Cu.import("resource://gre/modules/ctypes.jsm"); michael@0: let lib = ctypes.open("shell32"); michael@0: let SHGetSpecialFolderPath = lib.declare("SHGetSpecialFolderPathW", michael@0: ctypes.winapi_abi, michael@0: ctypes.bool, /* bool(return) */ michael@0: ctypes.int32_t, /* HWND hwndOwner */ michael@0: ctypes.jschar.ptr, /* LPTSTR lpszPath */ michael@0: ctypes.int32_t, /* int csidl */ michael@0: ctypes.bool /* BOOL fCreate */); michael@0: michael@0: let aryPathProgFiles = ctypes.jschar.array()(260); michael@0: let rv = SHGetSpecialFolderPath(0, aryPathProgFiles, CSIDL_PROGRAM_FILES, false); michael@0: lib.close(); michael@0: michael@0: let pathProgFiles = aryPathProgFiles.readString(); // Convert the c-string to js-string michael@0: let progFilesDir = AUS_Cc["@mozilla.org/file/local;1"]. michael@0: createInstance(AUS_Ci.nsILocalFile); michael@0: progFilesDir.initWithPath(pathProgFiles); michael@0: return progFilesDir; michael@0: }); michael@0: michael@0: /** michael@0: * Helper function for getting the update root directory used by the tests. This michael@0: * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir michael@0: * in nsXREDirProvider.cpp so an application will be able to find the update michael@0: * when running a test that launches the application. michael@0: */ michael@0: function getMockUpdRootD() { michael@0: let localAppDataDir = gLocalAppDataDir.clone(); michael@0: let progFilesDir = gProgFilesDir.clone(); michael@0: let appDir = Services.dirsvc.get(XRE_EXECUTABLE_FILE, AUS_Ci.nsIFile).parent; michael@0: michael@0: let appDirPath = appDir.path; michael@0: var relPathUpdates = ""; michael@0: if (gInstallDirPathHash && (MOZ_APP_VENDOR || MOZ_APP_BASENAME)) { michael@0: relPathUpdates += (MOZ_APP_VENDOR ? MOZ_APP_VENDOR : MOZ_APP_BASENAME) + michael@0: "\\" + DIR_UPDATES + "\\" + gInstallDirPathHash; michael@0: } michael@0: michael@0: if (!relPathUpdates) { michael@0: if (appDirPath.length > progFilesDir.path.length) { michael@0: if (appDirPath.substr(0, progFilesDir.path.length) == progFilesDir.path) { michael@0: if (MOZ_APP_VENDOR && MOZ_APP_BASENAME) { michael@0: relPathUpdates += MOZ_APP_VENDOR + "\\" + MOZ_APP_BASENAME; michael@0: } else { michael@0: relPathUpdates += MOZ_APP_BASENAME; michael@0: } michael@0: relPathUpdates += appDirPath.substr(progFilesDir.path.length); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!relPathUpdates) { michael@0: if (MOZ_APP_VENDOR && MOZ_APP_BASENAME) { michael@0: relPathUpdates += MOZ_APP_VENDOR + "\\" + MOZ_APP_BASENAME; michael@0: } else { michael@0: relPathUpdates += MOZ_APP_BASENAME; michael@0: } michael@0: relPathUpdates += "\\" + MOZ_APP_NAME; michael@0: } michael@0: michael@0: var updatesDir = AUS_Cc["@mozilla.org/file/local;1"]. michael@0: createInstance(AUS_Ci.nsILocalFile); michael@0: updatesDir.initWithPath(localAppDataDir.path + "\\" + relPathUpdates); michael@0: logTestInfo("returning UpdRootD Path: " + updatesDir.path); michael@0: return updatesDir; michael@0: } michael@0: #else michael@0: /** michael@0: * Helper function for getting the update root directory used by the tests. This michael@0: * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir michael@0: * in nsXREDirProvider.cpp so an application will be able to find the update michael@0: * when running a test that launches the application. michael@0: */ michael@0: function getMockUpdRootD() { michael@0: return getApplyDirFile(DIR_BIN_REL_PATH, true); michael@0: } michael@0: #endif michael@0: michael@0: /** michael@0: * Helper function for getting the nsIFile for the directory where the update michael@0: * has been applied. michael@0: * michael@0: * This will be the same as getApplyDirFile for foreground updates, but will michael@0: * point to a different file for the case of staged updates. michael@0: * michael@0: * Functions which attempt to access the files in the updated directory should michael@0: * be using this instead of getApplyDirFile. michael@0: * michael@0: * @param aRelPath (optional) michael@0: * The relative path to the file or directory to get from the root of michael@0: * the test's directory. If not specified the test's directory will be michael@0: * returned. michael@0: * @param aAllowNonexistent (optional) michael@0: * Whether the file must exist. If false or not specified the file must michael@0: * exist or the function will throw. michael@0: * @return The nsIFile for the directory where the update has been applied. michael@0: */ michael@0: function getTargetDirFile(aRelPath, aAllowNonexistent) { michael@0: let relpath = getUpdatedDirPath() + (aRelPath ? aRelPath : ""); michael@0: return do_get_file(relpath, aAllowNonexistent); michael@0: } michael@0: michael@0: if (IS_WIN) { michael@0: const kLockFileName = "updated.update_in_progress.lock"; michael@0: /** michael@0: * Helper function for locking a directory on Windows. michael@0: * michael@0: * @param aDir michael@0: * The nsIFile for the directory to lock. michael@0: */ michael@0: function lockDirectory(aDir) { michael@0: var file = aDir.clone(); michael@0: file.append(kLockFileName); michael@0: file.create(file.NORMAL_FILE_TYPE, 0o444); michael@0: file.QueryInterface(AUS_Ci.nsILocalFileWin); michael@0: file.fileAttributesWin |= file.WFA_READONLY; michael@0: file.fileAttributesWin &= ~file.WFA_READWRITE; michael@0: logTestInfo("testing the successful creation of the lock file"); michael@0: do_check_true(file.exists()); michael@0: do_check_false(file.isWritable()); michael@0: } michael@0: /** michael@0: * Helper function for unlocking a directory on Windows. michael@0: * michael@0: * @param aDir michael@0: * The nsIFile for the directory to unlock. michael@0: */ michael@0: function unlockDirectory(aDir) { michael@0: var file = aDir.clone(); michael@0: file.append(kLockFileName); michael@0: file.QueryInterface(AUS_Ci.nsILocalFileWin); michael@0: file.fileAttributesWin |= file.WFA_READWRITE; michael@0: file.fileAttributesWin &= ~file.WFA_READONLY; michael@0: logTestInfo("removing and testing the successful removal of the lock file"); michael@0: file.remove(false); michael@0: do_check_false(file.exists()); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater tests for launching the updater binary to apply michael@0: * a mar file. michael@0: * michael@0: * @param aExpectedExitValue michael@0: * The expected exit value from the updater binary. michael@0: * @param aExpectedStatus michael@0: * The expected value of update.status when the test finishes. michael@0: * @param aCallback (optional) michael@0: * A callback function that will be called when this function finishes. michael@0: * If null no function will be called when this function finishes. michael@0: * If not specified the checkUpdateApplied function will be called when michael@0: * this function finishes. michael@0: */ michael@0: function runUpdate(aExpectedExitValue, aExpectedStatus, aCallback) { michael@0: // Copy the updater binary to the updates directory. michael@0: let binDir = gGREDirOrig.clone(); michael@0: let updater = binDir.clone(); michael@0: updater.append("updater.app"); michael@0: if (!updater.exists()) { michael@0: updater = binDir.clone(); michael@0: updater.append(FILE_UPDATER_BIN); michael@0: if (!updater.exists()) { michael@0: do_throw("Unable to find updater binary!"); michael@0: } michael@0: } michael@0: michael@0: let updatesDir = getUpdatesPatchDir(); michael@0: updater.copyToFollowingLinks(updatesDir, updater.leafName); michael@0: let updateBin = updatesDir.clone(); michael@0: updateBin.append(updater.leafName); michael@0: if (updateBin.leafName == "updater.app") { michael@0: updateBin.append("Contents"); michael@0: updateBin.append("MacOS"); michael@0: updateBin.append("updater"); michael@0: if (!updateBin.exists()) { michael@0: do_throw("Unable to find the updater executable!"); michael@0: } michael@0: } michael@0: michael@0: let applyToDir = getApplyDirFile(null, true); michael@0: let applyToDirPath = applyToDir.path; michael@0: if (gStageUpdate || gSwitchApp) { michael@0: applyToDirPath += "/" + DIR_UPDATED + "/"; michael@0: } michael@0: michael@0: if (IS_WIN) { michael@0: // Convert to native path michael@0: applyToDirPath = applyToDirPath.replace(/\//g, "\\"); michael@0: } michael@0: michael@0: let callbackApp = getApplyDirFile("a/b/" + gCallbackBinFile); michael@0: callbackApp.permissions = PERMS_DIRECTORY; michael@0: michael@0: let args = [updatesDir.path, applyToDirPath, 0]; michael@0: if (gStageUpdate) { michael@0: args[2] = -1; michael@0: } else { michael@0: if (gSwitchApp) { michael@0: args[2] = "0/replace"; michael@0: } michael@0: args = args.concat([callbackApp.parent.path, callbackApp.path]); michael@0: args = args.concat(gCallbackArgs); michael@0: } michael@0: logTestInfo("running the updater: " + updateBin.path + " " + args.join(" ")); michael@0: michael@0: let env = AUS_Cc["@mozilla.org/process/environment;1"]. michael@0: getService(AUS_Ci.nsIEnvironment); michael@0: if (gDisableReplaceFallback) { michael@0: env.set("MOZ_NO_REPLACE_FALLBACK", "1"); michael@0: } michael@0: michael@0: let process = AUS_Cc["@mozilla.org/process/util;1"]. michael@0: createInstance(AUS_Ci.nsIProcess); michael@0: process.init(updateBin); michael@0: process.run(true, args, args.length); michael@0: michael@0: if (gDisableReplaceFallback) { michael@0: env.set("MOZ_NO_REPLACE_FALLBACK", ""); michael@0: } michael@0: michael@0: let status = readStatusFile(); michael@0: if (process.exitValue != aExpectedExitValue || status != aExpectedStatus) { michael@0: if (process.exitValue != aExpectedExitValue) { michael@0: logTestInfo("updater exited with unexpected value! Got: " + michael@0: process.exitValue + ", Expected: " + aExpectedExitValue); michael@0: } michael@0: if (status != aExpectedStatus) { michael@0: logTestInfo("update status is not the expected status! Got: " + status + michael@0: ", Expected: " + aExpectedStatus); michael@0: } michael@0: let updateLog = getUpdatesPatchDir(); michael@0: updateLog.append(FILE_UPDATE_LOG); michael@0: logTestInfo("contents of " + updateLog.path + ":\n" + michael@0: readFileBytes(updateLog).replace(/\r\n/g, "\n")); michael@0: } michael@0: logTestInfo("testing updater binary process exitValue against expected " + michael@0: "exit value"); michael@0: do_check_eq(process.exitValue, aExpectedExitValue); michael@0: logTestInfo("testing update status against expected status"); michael@0: do_check_eq(status, aExpectedStatus); michael@0: michael@0: if (aCallback !== null) { michael@0: if (typeof(aCallback) == typeof(Function)) { michael@0: aCallback(); michael@0: } else { michael@0: checkUpdateApplied(); michael@0: } michael@0: } michael@0: } michael@0: /** michael@0: * Helper function for updater tests to stage an update. michael@0: */ michael@0: function stageUpdate() { michael@0: logTestInfo("start - staging update"); michael@0: Services.obs.addObserver(gUpdateStagedObserver, "update-staged", false); michael@0: michael@0: setEnvironment(); michael@0: // Stage the update. michael@0: AUS_Cc["@mozilla.org/updates/update-processor;1"]. michael@0: createInstance(AUS_Ci.nsIUpdateProcessor). michael@0: processUpdate(gUpdateManager.activeUpdate); michael@0: resetEnvironment(); michael@0: michael@0: logTestInfo("finish - staging update"); michael@0: } michael@0: michael@0: /** michael@0: * Helper function to check whether the maintenance service updater tests should michael@0: * run. See bug 711660 for more details. michael@0: * michael@0: * @param aFirstTest michael@0: * Whether this is the first test within the test. michael@0: * @return true if the test should run and false if it shouldn't. michael@0: */ michael@0: function shouldRunServiceTest(aFirstTest) { michael@0: // In case the machine is running an old maintenance service or if it michael@0: // is not installed, and permissions exist to install it. Then install michael@0: // the newer bin that we have. michael@0: attemptServiceInstall(); michael@0: michael@0: let binDir = getGREDir(); michael@0: let updaterBin = binDir.clone(); michael@0: updaterBin.append(FILE_UPDATER_BIN); michael@0: if (!updaterBin.exists()) { michael@0: do_throw("Unable to find updater binary!"); michael@0: } michael@0: michael@0: let updaterBinPath = updaterBin.path; michael@0: if (/ /.test(updaterBinPath)) { michael@0: updaterBinPath = '"' + updaterBinPath + '"'; michael@0: } michael@0: michael@0: const REG_PATH = "SOFTWARE\\Mozilla\\MaintenanceService\\" + michael@0: "3932ecacee736d366d6436db0f55bce4"; michael@0: michael@0: let key = AUS_Cc["@mozilla.org/windows-registry-key;1"]. michael@0: createInstance(AUS_Ci.nsIWindowsRegKey); michael@0: try { michael@0: key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH, michael@0: AUS_Ci.nsIWindowsRegKey.ACCESS_READ | key.WOW64_64); michael@0: } catch (e) { michael@0: #ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK michael@0: // The build system could sign the files and not have the test registry key michael@0: // in which case we should fail the test by throwing so it can be fixed. michael@0: if (isBinarySigned(updaterBinPath)) { michael@0: do_throw("binary is signed but the test registry key does not exists!"); michael@0: } michael@0: #endif michael@0: michael@0: logTestInfo("this test can only run on the buildbot build system at this " + michael@0: "time."); michael@0: return false; michael@0: } michael@0: michael@0: // Check to make sure the service is installed michael@0: let helperBin = getTestDirFile(FILE_HELPER_BIN); michael@0: let args = ["wait-for-service-stop", "MozillaMaintenance", "10"]; michael@0: let process = AUS_Cc["@mozilla.org/process/util;1"]. michael@0: createInstance(AUS_Ci.nsIProcess); michael@0: process.init(helperBin); michael@0: logTestInfo("checking if the service exists on this machine."); michael@0: process.run(true, args, args.length); michael@0: if (process.exitValue == 0xEE) { michael@0: do_throw("test registry key exists but this test can only run on systems " + michael@0: "with the maintenance service installed."); michael@0: } else { michael@0: logTestInfo("service exists, return value: " + process.exitValue); michael@0: } michael@0: michael@0: // If this is the first test in the series, then there is no reason the michael@0: // service should be anything but stopped, so be strict here and throw michael@0: // an error. michael@0: if (aFirstTest && process.exitValue != 0) { michael@0: do_throw("First test, check for service stopped state returned error " + michael@0: process.exitValue); michael@0: } michael@0: michael@0: #ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK michael@0: if (!isBinarySigned(updaterBinPath)) { michael@0: logTestInfo("test registry key exists but this test can only run on " + michael@0: "builds with signed binaries when " + michael@0: "DISABLE_UPDATER_AUTHENTICODE_CHECK is not defined"); michael@0: do_throw("this test can only run on builds with signed binaries."); michael@0: } michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Helper function to check whether the a binary is signed. michael@0: * michael@0: * @param aBinPath The path to the file to check if it is signed. michael@0: * @return true if the file is signed and false if it isn't. michael@0: */ michael@0: function isBinarySigned(aBinPath) { michael@0: let helperBin = getTestDirFile(FILE_HELPER_BIN); michael@0: let args = ["check-signature", aBinPath]; michael@0: let process = AUS_Cc["@mozilla.org/process/util;1"]. michael@0: createInstance(AUS_Ci.nsIProcess); michael@0: process.init(helperBin); michael@0: process.run(true, args, args.length); michael@0: if (process.exitValue != 0) { michael@0: logTestInfo("binary is not signed. " + FILE_HELPER_BIN + " returned " + michael@0: process.exitValue + " for file " + aBinPath); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Helper function for asynchronously setting up the application files required michael@0: * to launch the application for the updater tests by either copying or creating michael@0: * symlinks for the files. This is needed for Windows debug builds which can michael@0: * lock a file that is being copied so that the tests can run in parallel. After michael@0: * the files have been copied the setupAppFilesFinished function will be called. michael@0: */ michael@0: function setupAppFilesAsync() { michael@0: gTimeoutRuns++; michael@0: try { michael@0: setupAppFiles(); michael@0: } catch (e) { michael@0: if (gTimeoutRuns > MAX_TIMEOUT_RUNS) { michael@0: do_throw("Exceeded MAX_TIMEOUT_RUNS while trying to setup application " + michael@0: "files. Exception: " + e); michael@0: } michael@0: do_timeout(TEST_CHECK_TIMEOUT, setupAppFilesAsync); michael@0: return; michael@0: } michael@0: michael@0: setupAppFilesFinished(); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for setting up the application files required to launch the michael@0: * application for the updater tests by either copying or creating symlinks for michael@0: * the files. michael@0: */ michael@0: function setupAppFiles() { michael@0: logTestInfo("start - copying or creating symlinks for application files " + michael@0: "for the test"); michael@0: michael@0: let srcDir = getCurrentProcessDir(); michael@0: let destDir = getApplyDirFile(null, true); michael@0: if (!destDir.exists()) { michael@0: try { michael@0: destDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); michael@0: } catch (e) { michael@0: logTestInfo("unable to create directory, Path: " + destDir.path + michael@0: ", Exception: " + e); michael@0: do_throw(e); michael@0: } michael@0: } michael@0: michael@0: // Required files for the application or the test that aren't listed in the michael@0: // dependentlibs.list file. michael@0: let fileRelPaths = [FILE_APP_BIN, FILE_UPDATER_BIN, michael@0: "application.ini", "dependentlibs.list"]; michael@0: michael@0: // On Linux the updater.png must also be copied michael@0: if (IS_UNIX && !IS_MACOSX) { michael@0: fileRelPaths.push("icons/updater.png"); michael@0: } michael@0: michael@0: // Read the dependent libs file leafnames from the dependentlibs.list file michael@0: // into the array. michael@0: let deplibsFile = srcDir.clone(); michael@0: deplibsFile.append("dependentlibs.list"); michael@0: let istream = AUS_Cc["@mozilla.org/network/file-input-stream;1"]. michael@0: createInstance(AUS_Ci.nsIFileInputStream); michael@0: istream.init(deplibsFile, 0x01, 0o444, 0); michael@0: istream.QueryInterface(AUS_Ci.nsILineInputStream); michael@0: michael@0: let hasMore; michael@0: let line = {}; michael@0: do { michael@0: hasMore = istream.readLine(line); michael@0: fileRelPaths.push(line.value); michael@0: } while(hasMore); michael@0: michael@0: istream.close(); michael@0: michael@0: fileRelPaths.forEach(function CMAF_FLN_FE(aFileRelPath) { michael@0: copyFileToTestAppDir(aFileRelPath); michael@0: }); michael@0: michael@0: logTestInfo("finish - copying or creating symlinks for application files " + michael@0: "for the test"); michael@0: } michael@0: michael@0: /** michael@0: * Copies the specified files from the dist/bin directory into the test's michael@0: * application directory. michael@0: * michael@0: * @param aFileRelPath michael@0: * The relative path of the file to copy. michael@0: */ michael@0: function copyFileToTestAppDir(aFileRelPath) { michael@0: let fileRelPath = aFileRelPath; michael@0: let srcFile = gGREDirOrig.clone(); michael@0: let pathParts = fileRelPath.split("/"); michael@0: for (let i = 0; i < pathParts.length; i++) { michael@0: if (pathParts[i]) { michael@0: srcFile.append(pathParts[i]); michael@0: } michael@0: } michael@0: michael@0: if (IS_MACOSX && !srcFile.exists()) { michael@0: logTestInfo("unable to copy file since it doesn't exist! Checking if " + michael@0: fileRelPath + ".app exists. Path: " + michael@0: srcFile.path); michael@0: srcFile = gGREDirOrig.clone(); michael@0: for (let i = 0; i < pathParts.length; i++) { michael@0: if (pathParts[i]) { michael@0: srcFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : "")); michael@0: } michael@0: } michael@0: fileRelPath = fileRelPath + ".app"; michael@0: } michael@0: michael@0: if (!srcFile.exists()) { michael@0: do_throw("Unable to copy file since it doesn't exist! Path: " + michael@0: srcFile.path); michael@0: } michael@0: michael@0: // Symlink libraries. Note that the XUL library on Mac OS X doesn't have a michael@0: // file extension and this will always be false on Windows. michael@0: let shouldSymlink = (pathParts[pathParts.length - 1] == "XUL" || michael@0: fileRelPath.substr(fileRelPath.length - 3) == ".so" || michael@0: fileRelPath.substr(fileRelPath.length - 6) == ".dylib"); michael@0: let destFile = getApplyDirFile(DIR_BIN_REL_PATH + fileRelPath, true); michael@0: if (!shouldSymlink) { michael@0: if (!destFile.exists()) { michael@0: try { michael@0: srcFile.copyToFollowingLinks(destFile.parent, destFile.leafName); michael@0: } catch (e) { michael@0: // Just in case it is partially copied michael@0: if (destFile.exists()) { michael@0: try { michael@0: destFile.remove(true); michael@0: } catch (e) { michael@0: logTestInfo("unable to remove file that failed to copy! Path: " + michael@0: destFile.path); michael@0: } michael@0: } michael@0: do_throw("Unable to copy file! Path: " + srcFile.path + michael@0: ", Exception: " + e); michael@0: } michael@0: } michael@0: } else { michael@0: try { michael@0: if (destFile.exists()) { michael@0: destFile.remove(false); michael@0: } michael@0: let ln = AUS_Cc["@mozilla.org/file/local;1"].createInstance(AUS_Ci.nsILocalFile); michael@0: ln.initWithPath("/bin/ln"); michael@0: let process = AUS_Cc["@mozilla.org/process/util;1"].createInstance(AUS_Ci.nsIProcess); michael@0: process.init(ln); michael@0: let args = ["-s", srcFile.path, destFile.path]; michael@0: process.run(true, args, args.length); michael@0: logTestInfo("verifying symlink. Path: " + destFile.path); michael@0: do_check_true(destFile.isSymlink()); michael@0: } catch (e) { michael@0: do_throw("Unable to create symlink for file! Path: " + srcFile.path + michael@0: ", Exception: " + e); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Attempts to upgrade the maintenance service if permissions are allowed. michael@0: * This is useful for XP where we have permission to upgrade in case an michael@0: * older service installer exists. Also if the user manually installed into michael@0: * a unprivileged location. michael@0: */ michael@0: function attemptServiceInstall() { michael@0: var version = AUS_Cc["@mozilla.org/system-info;1"] michael@0: .getService(AUS_Ci.nsIPropertyBag2) michael@0: .getProperty("version"); michael@0: var isVistaOrHigher = (parseFloat(version) >= 6.0); michael@0: if (isVistaOrHigher) { michael@0: return; michael@0: } michael@0: michael@0: let binDir = getGREDir(); michael@0: let installerFile = binDir.clone(); michael@0: installerFile.append(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN); michael@0: if (!installerFile.exists()) { michael@0: do_throw(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN + " not found."); michael@0: } michael@0: let installerProcess = AUS_Cc["@mozilla.org/process/util;1"]. michael@0: createInstance(AUS_Ci.nsIProcess); michael@0: installerProcess.init(installerFile); michael@0: logTestInfo("starting installer process..."); michael@0: installerProcess.run(true, [], 0); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater tests for launching the updater using the michael@0: * maintenance service to apply a mar file. michael@0: * michael@0: * @param aInitialStatus michael@0: * The initial value of update.status. michael@0: * @param aExpectedStatus michael@0: * The expected value of update.status when the test finishes. michael@0: * @param aCheckSvcLog michael@0: * Whether the service log should be checked (optional). michael@0: */ michael@0: function runUpdateUsingService(aInitialStatus, aExpectedStatus, aCheckSvcLog) { michael@0: // Check the service logs for a successful update michael@0: function checkServiceLogs(aOriginalContents) { michael@0: let contents = readServiceLogFile(); michael@0: logTestInfo("the contents of maintenanceservice.log:\n" + contents + "\n"); michael@0: do_check_neq(contents, aOriginalContents); michael@0: do_check_neq(contents.indexOf(LOG_SVC_SUCCESSFUL_LAUNCH), -1); michael@0: } michael@0: function readServiceLogFile() { michael@0: let file = AUS_Cc["@mozilla.org/file/directory_service;1"]. michael@0: getService(AUS_Ci.nsIProperties). michael@0: get("CmAppData", AUS_Ci.nsIFile); michael@0: file.append("Mozilla"); michael@0: file.append("logs"); michael@0: file.append("maintenanceservice.log"); michael@0: return readFile(file); michael@0: } michael@0: function waitServiceApps() { michael@0: // maintenanceservice_installer.exe is started async during updates. michael@0: waitForApplicationStop("maintenanceservice_installer.exe"); michael@0: // maintenanceservice_tmp.exe is started async from the service installer. michael@0: waitForApplicationStop("maintenanceservice_tmp.exe"); michael@0: // In case the SCM thinks the service is stopped, but process still exists. michael@0: waitForApplicationStop("maintenanceservice.exe"); michael@0: } michael@0: function waitForServiceStop(aFailTest) { michael@0: waitServiceApps(); michael@0: logTestInfo("waiting for service to stop if necessary..."); michael@0: // Use the helper bin to ensure the service is stopped. If not michael@0: // stopped then wait for the service to be stopped (at most 120 seconds) michael@0: let helperBin = getTestDirFile(FILE_HELPER_BIN); michael@0: let helperBinArgs = ["wait-for-service-stop", michael@0: "MozillaMaintenance", michael@0: "120"]; michael@0: let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"]. michael@0: createInstance(AUS_Ci.nsIProcess); michael@0: helperBinProcess.init(helperBin); michael@0: logTestInfo("stopping service..."); michael@0: helperBinProcess.run(true, helperBinArgs, helperBinArgs.length); michael@0: if (helperBinProcess.exitValue == 0xEE) { michael@0: do_throw("The service does not exist on this machine. Return value: " + michael@0: helperBinProcess.exitValue); michael@0: } else if (helperBinProcess.exitValue != 0) { michael@0: if (aFailTest) { michael@0: do_throw("maintenance service did not stop, last state: " + michael@0: helperBinProcess.exitValue + ". Forcing test failure."); michael@0: } else { michael@0: logTestInfo("maintenance service did not stop, last state: " + michael@0: helperBinProcess.exitValue + ". May cause failures."); michael@0: } michael@0: } else { michael@0: logTestInfo("service stopped."); michael@0: } michael@0: waitServiceApps(); michael@0: } michael@0: function waitForApplicationStop(aApplication) { michael@0: logTestInfo("waiting for " + aApplication + " to stop if " + michael@0: "necessary..."); michael@0: // Use the helper bin to ensure the application is stopped. michael@0: // If not, then wait for it to be stopped (at most 120 seconds) michael@0: let helperBin = getTestDirFile(FILE_HELPER_BIN); michael@0: let helperBinArgs = ["wait-for-application-exit", michael@0: aApplication, michael@0: "120"]; michael@0: let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"]. michael@0: createInstance(AUS_Ci.nsIProcess); michael@0: helperBinProcess.init(helperBin); michael@0: helperBinProcess.run(true, helperBinArgs, helperBinArgs.length); michael@0: if (helperBinProcess.exitValue != 0) { michael@0: do_throw(aApplication + " did not stop, last state: " + michael@0: helperBinProcess.exitValue + ". Forcing test failure."); michael@0: } michael@0: } michael@0: michael@0: // Make sure the service from the previous test is already stopped. michael@0: waitForServiceStop(true); michael@0: michael@0: // Prevent the cleanup function from begin run more than once michael@0: if (gRegisteredServiceCleanup === undefined) { michael@0: gRegisteredServiceCleanup = true; michael@0: michael@0: do_register_cleanup(function RUUS_cleanup() { michael@0: resetEnvironment(); michael@0: michael@0: // This will delete the app arguments log file if it exists. michael@0: try { michael@0: getAppArgsLogPath(); michael@0: } catch (e) { michael@0: logTestInfo("unable to remove file during cleanup. Exception: " + e); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: let svcOriginalLog; michael@0: // Default to checking the service log if the parameter is not specified. michael@0: if (aCheckSvcLog === undefined || aCheckSvcLog) { michael@0: svcOriginalLog = readServiceLogFile(); michael@0: } michael@0: michael@0: let appArgsLogPath = getAppArgsLogPath(); michael@0: gServiceLaunchedCallbackLog = appArgsLogPath.replace(/^"|"$/g, ""); michael@0: michael@0: let updatesDir = getUpdatesPatchDir(); michael@0: let file = updatesDir.clone(); michael@0: writeStatusFile(aInitialStatus); michael@0: michael@0: // sanity check michael@0: do_check_eq(readStatusState(), aInitialStatus); michael@0: michael@0: writeVersionFile(DEFAULT_UPDATE_VERSION); michael@0: michael@0: gServiceLaunchedCallbackArgs = [ michael@0: "-no-remote", michael@0: "-process-updates", michael@0: "-dump-args", michael@0: appArgsLogPath michael@0: ]; michael@0: michael@0: if (gSwitchApp) { michael@0: // We want to set the env vars again michael@0: gShouldResetEnv = undefined; michael@0: } michael@0: michael@0: setEnvironment(); michael@0: michael@0: // There is a security check done by the service to make sure the updater michael@0: // we are executing is the same as the one in the apply-to dir. michael@0: // To make sure they match from tests we copy updater.exe to the apply-to dir. michael@0: copyFileToTestAppDir(FILE_UPDATER_BIN); michael@0: michael@0: // The service will execute maintenanceservice_installer.exe and michael@0: // will copy maintenanceservice.exe out of the same directory from michael@0: // the installation directory. So we need to make sure both of those michael@0: // bins always exist in the installation directory. michael@0: copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_BIN); michael@0: copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN); michael@0: michael@0: let launchBin = getLaunchBin(); michael@0: let args = getProcessArgs(["-dump-args", appArgsLogPath]); michael@0: michael@0: let process = AUS_Cc["@mozilla.org/process/util;1"]. michael@0: createInstance(AUS_Ci.nsIProcess); michael@0: process.init(launchBin); michael@0: logTestInfo("launching " + launchBin.path + " " + args.join(" ")); michael@0: // Firefox does not wait for the service command to finish, but michael@0: // we still launch the process sync to avoid intermittent failures with michael@0: // the log file not being written out yet. michael@0: // We will rely on watching the update.status file and waiting for the service michael@0: // to stop to know the service command is done. michael@0: process.run(true, args, args.length); michael@0: michael@0: resetEnvironment(); michael@0: michael@0: function timerCallback(aTimer) { michael@0: // Wait for the expected status michael@0: let status = readStatusState(); michael@0: // status will probably always be equal to STATE_APPLYING but there is a michael@0: // race condition where it would be possible on slower machines where status michael@0: // could be equal to STATE_PENDING_SVC. michael@0: if (status == STATE_APPLYING || michael@0: status == STATE_PENDING_SVC) { michael@0: logTestInfo("still waiting to see the " + aExpectedStatus + michael@0: " status, got " + status + " for now..."); michael@0: return; michael@0: } michael@0: michael@0: // Make sure all of the logs are written out. michael@0: waitForServiceStop(false); michael@0: michael@0: aTimer.cancel(); michael@0: aTimer = null; michael@0: michael@0: if (status != aExpectedStatus) { michael@0: logTestInfo("update status is not the expected status! Got: " + status + michael@0: ", Expected: " + aExpectedStatus); michael@0: logTestInfo("update.status contents: " + readStatusFile()); michael@0: let updateLog = getUpdatesPatchDir(); michael@0: updateLog.append(FILE_UPDATE_LOG); michael@0: logTestInfo("contents of " + updateLog.path + ":\n" + michael@0: readFileBytes(updateLog).replace(/\r\n/g, "\n")); michael@0: } michael@0: logTestInfo("testing update status against expected status"); michael@0: do_check_eq(status, aExpectedStatus); michael@0: michael@0: if (aCheckSvcLog) { michael@0: checkServiceLogs(svcOriginalLog); michael@0: } michael@0: michael@0: checkUpdateFinished(); michael@0: } michael@0: michael@0: let timer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer); michael@0: timer.initWithCallback(timerCallback, 1000, timer.TYPE_REPEATING_SLACK); michael@0: } michael@0: michael@0: /** michael@0: * Gets the platform specific shell binary that is launched using nsIProcess and michael@0: * in turn launches a binary used for the test (e.g. application, updater, michael@0: * etc.). A shell is used so debug console output can be redirected to a file so michael@0: * it doesn't end up in the test log. michael@0: * michael@0: * @return nsIFile for the shell binary to launch using nsIProcess. michael@0: * @throws if the shell binary doesn't exist. michael@0: */ michael@0: function getLaunchBin() { michael@0: let launchBin; michael@0: if (IS_WIN) { michael@0: launchBin = Services.dirsvc.get("WinD", AUS_Ci.nsIFile); michael@0: launchBin.append("System32"); michael@0: launchBin.append("cmd.exe"); michael@0: } else { michael@0: launchBin = AUS_Cc["@mozilla.org/file/local;1"]. michael@0: createInstance(AUS_Ci.nsILocalFile); michael@0: launchBin.initWithPath("/bin/sh"); michael@0: } michael@0: michael@0: if (!launchBin.exists()) michael@0: do_throw(launchBin.path + " must exist to run this test!"); michael@0: michael@0: return launchBin; michael@0: } michael@0: michael@0: /** michael@0: * Helper function that waits until the helper has completed its operations and michael@0: * is in a sleep state before performing an update by calling doUpdate. michael@0: */ michael@0: function waitForHelperSleep() { michael@0: gTimeoutRuns++; michael@0: // Give the lock file process time to lock the file before updating otherwise michael@0: // this test can fail intermittently on Windows debug builds. michael@0: let output = getApplyDirFile("a/b/output", true); michael@0: if (readFile(output) != "sleeping\n") { michael@0: if (gTimeoutRuns > MAX_TIMEOUT_RUNS) { michael@0: do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the helper to " + michael@0: "finish its operation. Path: " + output.path); michael@0: } michael@0: do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep); michael@0: return; michael@0: } michael@0: try { michael@0: output.remove(false); michael@0: } michael@0: catch (e) { michael@0: if (gTimeoutRuns > MAX_TIMEOUT_RUNS) { michael@0: do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the helper " + michael@0: "message file to no longer be in use. Path: " + output.path); michael@0: } michael@0: logTestInfo("failed to remove file. Path: " + output.path); michael@0: do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep); michael@0: return; michael@0: } michael@0: doUpdate(); michael@0: } michael@0: michael@0: /** michael@0: * Helper function that waits until the helper has finished its operations michael@0: * before calling waitForHelperFinishFileUnlock to verify that the helper's michael@0: * input and output directories are no longer in use. michael@0: */ michael@0: function waitForHelperFinished() { michael@0: // Give the lock file process time to lock the file before updating otherwise michael@0: // this test can fail intermittently on Windows debug builds. michael@0: let output = getApplyDirFile("a/b/output", true); michael@0: if (readFile(output) != "finished\n") { michael@0: do_timeout(TEST_HELPER_TIMEOUT, waitForHelperFinished); michael@0: return; michael@0: } michael@0: // Give the lock file process time to unlock the file before deleting the michael@0: // input and output files. michael@0: waitForHelperFinishFileUnlock(); michael@0: } michael@0: michael@0: /** michael@0: * Helper function that waits until the helper's input and output files are no michael@0: * longer in use before calling checkUpdate. michael@0: */ michael@0: function waitForHelperFinishFileUnlock() { michael@0: try { michael@0: let output = getApplyDirFile("a/b/output", true); michael@0: if (output.exists()) { michael@0: output.remove(false); michael@0: } michael@0: let input = getApplyDirFile("a/b/input", true); michael@0: if (input.exists()) { michael@0: input.remove(false); michael@0: } michael@0: } catch (e) { michael@0: // Give the lock file process time to unlock the file before deleting the michael@0: // input and output files. michael@0: do_timeout(TEST_HELPER_TIMEOUT, waitForHelperFinishFileUnlock); michael@0: return; michael@0: } michael@0: checkUpdate(); michael@0: } michael@0: michael@0: /** michael@0: * Helper function to tell the helper to finish and exit its sleep state. michael@0: */ michael@0: function setupHelperFinish() { michael@0: let input = getApplyDirFile("a/b/input", true); michael@0: writeFile(input, "finish\n"); michael@0: waitForHelperFinished(); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater binary tests that creates the files and michael@0: * directories used by the test. michael@0: * michael@0: * @param aMarFile michael@0: * The mar file for the update test. michael@0: */ michael@0: function setupUpdaterTest(aMarFile, aUpdatedDirExists, aToBeDeletedDirExists) { michael@0: let updatesPatchDir = getUpdatesPatchDir(); michael@0: if (!updatesPatchDir.exists()) { michael@0: updatesPatchDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); michael@0: } michael@0: // Copy the mar that will be applied michael@0: let mar = getTestDirFile(aMarFile); michael@0: mar.copyToFollowingLinks(updatesPatchDir, FILE_UPDATE_ARCHIVE); michael@0: michael@0: createUpdateSettingsINI(); michael@0: michael@0: let applyToDir = getApplyDirFile(null, true); michael@0: gTestFiles.forEach(function SUT_TF_FE(aTestFile) { michael@0: if (aTestFile.originalFile || aTestFile.originalContents) { michael@0: let testDir = getApplyDirFile(aTestFile.relPathDir, true); michael@0: if (!testDir.exists()) michael@0: testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); michael@0: michael@0: let testFile; michael@0: if (aTestFile.originalFile) { michael@0: testFile = getTestDirFile(aTestFile.originalFile); michael@0: testFile.copyToFollowingLinks(testDir, aTestFile.fileName); michael@0: testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName); michael@0: } else { michael@0: testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName, michael@0: true); michael@0: writeFile(testFile, aTestFile.originalContents); michael@0: } michael@0: michael@0: // Skip these tests on Windows and OS/2 since their michael@0: // implementaions of chmod doesn't really set permissions. michael@0: if (!IS_WIN && aTestFile.originalPerms) { michael@0: testFile.permissions = aTestFile.originalPerms; michael@0: // Store the actual permissions on the file for reference later after michael@0: // setting the permissions. michael@0: if (!aTestFile.comparePerms) { michael@0: aTestFile.comparePerms = testFile.permissions; michael@0: } michael@0: } michael@0: } michael@0: }); michael@0: michael@0: let helperBin = getTestDirFile(FILE_HELPER_BIN); michael@0: let afterApplyBinDir = getApplyDirFile("a/b/", true); michael@0: helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile); michael@0: michael@0: // Add the test directory that will be updated for a successful update or left michael@0: // in the initial state for a failed update. michael@0: gTestDirs.forEach(function SUT_TD_FE(aTestDir) { michael@0: let testDir = getApplyDirFile(aTestDir.relPathDir, true); michael@0: if (!testDir.exists()) { michael@0: testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); michael@0: } michael@0: michael@0: if (aTestDir.files) { michael@0: aTestDir.files.forEach(function SUT_TD_F_FE(aTestFile) { michael@0: let testFile = getApplyDirFile(aTestDir.relPathDir + aTestFile, true); michael@0: if (!testFile.exists()) { michael@0: testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: if (aTestDir.subDirs) { michael@0: aTestDir.subDirs.forEach(function SUT_TD_SD_FE(aSubDir) { michael@0: let testSubDir = getApplyDirFile(aTestDir.relPathDir + aSubDir, true); michael@0: if (!testSubDir.exists()) { michael@0: testSubDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); michael@0: } michael@0: michael@0: if (aTestDir.subDirFiles) { michael@0: aTestDir.subDirFiles.forEach(function SUT_TD_SDF_FE(aTestFile) { michael@0: let testFile = getApplyDirFile(aTestDir.relPathDir + aSubDir + aTestFile, true); michael@0: if (!testFile.exists()) { michael@0: testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); michael@0: } michael@0: }); michael@0: } michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: gTestExtraDirs[0].dirExists = aUpdatedDirExists; michael@0: gTestExtraDirs[1].dirExists = IS_WIN ? aToBeDeletedDirExists : false; michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater binary tests that creates the update-settings.ini michael@0: * file. michael@0: */ michael@0: function createUpdateSettingsINI() { michael@0: updateSettingsIni = getApplyDirFile(null, true); michael@0: if (IS_MACOSX) { michael@0: updateSettingsIni.append("Contents"); michael@0: updateSettingsIni.append("MacOS"); michael@0: } michael@0: updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI); michael@0: writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater binary tests for verifying the contents of the michael@0: * update log after a successful update. michael@0: * michael@0: * @param aCompareLogFile michael@0: * The log file to compare the update log with. michael@0: */ michael@0: function checkUpdateLogContents(aCompareLogFile) { michael@0: let updateLog = getUpdatesPatchDir(); michael@0: updateLog.append(FILE_UPDATE_LOG); michael@0: let updateLogContents = readFileBytes(updateLog); michael@0: michael@0: // The channel-prefs.js is defined in gTestFilesCommon which will always be michael@0: // located to the end of gTestFiles. michael@0: if (gTestFiles.length > 1 && michael@0: gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" && michael@0: !gTestFiles[gTestFiles.length - 1].originalContents) { michael@0: updateLogContents = updateLogContents.replace(/.* a\/b\/defaults\/.*/g, ""); michael@0: } michael@0: if (gTestFiles.length > 2 && michael@0: gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI && michael@0: !gTestFiles[gTestFiles.length - 2].originalContents) { michael@0: updateLogContents = updateLogContents.replace(/.* a\/b\/update-settings.ini.*/g, ""); michael@0: } michael@0: if (gStageUpdate) { michael@0: // Skip the staged update messages michael@0: updateLogContents = updateLogContents.replace(/Performing a staged update/, ""); michael@0: } else if (gSwitchApp) { michael@0: // Skip the switch app request messages michael@0: updateLogContents = updateLogContents.replace(/Performing a staged update/, ""); michael@0: updateLogContents = updateLogContents.replace(/Performing a replace request/, ""); michael@0: } michael@0: // Skip the source/destination lines since they contain absolute paths. michael@0: updateLogContents = updateLogContents.replace(/SOURCE DIRECTORY.*/g, ""); michael@0: updateLogContents = updateLogContents.replace(/DESTINATION DIRECTORY.*/g, ""); michael@0: // Skip lines that log failed attempts to open the callback executable. michael@0: updateLogContents = updateLogContents.replace(/NS_main: callback app file .*/g, ""); michael@0: if (gSwitchApp) { michael@0: // Remove the lines which contain absolute paths michael@0: updateLogContents = updateLogContents.replace(/^Begin moving.*$/mg, ""); michael@0: if (IS_MACOSX) { michael@0: // Remove the entire section about moving the precomplete file as it contains michael@0: // absolute paths. michael@0: updateLogContents = updateLogContents.replace(/\n/g, "%%%EOL%%%"); michael@0: updateLogContents = updateLogContents.replace(/Moving the precomplete file.*Finished moving the precomplete file/, ""); michael@0: updateLogContents = updateLogContents.replace(/%%%EOL%%%/g, "\n"); michael@0: } michael@0: } michael@0: updateLogContents = updateLogContents.replace(/\r/g, ""); michael@0: // Replace error codes since they are different on each platform. michael@0: updateLogContents = updateLogContents.replace(/, err:.*\n/g, "\n"); michael@0: // Replace to make the log parsing happy. michael@0: updateLogContents = updateLogContents.replace(/non-fatal error /g, ""); michael@0: // The FindFile results when enumerating the filesystem on Windows is not michael@0: // determistic so the results matching the following need to be ignored. michael@0: updateLogContents = updateLogContents.replace(/.* a\/b\/7\/7text.*\n/g, ""); michael@0: // Remove consecutive newlines michael@0: updateLogContents = updateLogContents.replace(/\n+/g, "\n"); michael@0: // Remove leading and trailing newlines michael@0: updateLogContents = updateLogContents.replace(/^\n|\n$/g, ""); michael@0: // The update log when running the service tests sometimes starts with data michael@0: // from the previous launch of the updater. michael@0: updateLogContents = updateLogContents.replace(/^calling QuitProgressUI\n[^\n]*\nUPDATE TYPE/g, "UPDATE TYPE"); michael@0: michael@0: let compareLogContents = ""; michael@0: if (aCompareLogFile) { michael@0: compareLogContents = readFileBytes(getTestDirFile(aCompareLogFile)); michael@0: } michael@0: if (gSwitchApp) { michael@0: compareLogContents += LOG_SWITCH_SUCCESS; michael@0: } michael@0: // The channel-prefs.js is defined in gTestFilesCommon which will always be michael@0: // located to the end of gTestFiles. michael@0: if (gTestFiles.length > 1 && michael@0: gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" && michael@0: !gTestFiles[gTestFiles.length - 1].originalContents) { michael@0: compareLogContents = compareLogContents.replace(/.* a\/b\/defaults\/.*/g, ""); michael@0: } michael@0: if (gTestFiles.length > 2 && michael@0: gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI && michael@0: !gTestFiles[gTestFiles.length - 2].originalContents) { michael@0: compareLogContents = compareLogContents.replace(/.* a\/b\/update-settings.ini.*/g, ""); michael@0: } michael@0: // Remove leading and trailing newlines michael@0: compareLogContents = compareLogContents.replace(/\n+/g, "\n"); michael@0: // Remove leading and trailing newlines michael@0: compareLogContents = compareLogContents.replace(/^\n|\n$/g, ""); michael@0: michael@0: // Don't write the contents of the file to the log to reduce log spam michael@0: // unless there is a failure. michael@0: if (compareLogContents == updateLogContents) { michael@0: logTestInfo("log contents are correct"); michael@0: do_check_true(true); michael@0: } else { michael@0: logTestInfo("log contents are not correct"); michael@0: do_check_eq(compareLogContents, updateLogContents); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Helper function to check if the update log contains a string. michael@0: * michael@0: * @param aCheckString michael@0: * The string to check if the update log contains. michael@0: */ michael@0: function checkUpdateLogContains(aCheckString) { michael@0: let updateLog = getUpdatesPatchDir(); michael@0: updateLog.append(FILE_UPDATE_LOG); michael@0: let updateLogContents = readFileBytes(updateLog); michael@0: if (updateLogContents.indexOf(aCheckString) != -1) { michael@0: logTestInfo("log file does contain: " + aCheckString); michael@0: do_check_true(true); michael@0: } else { michael@0: logTestInfo("log file does not contain: " + aCheckString); michael@0: logTestInfo("log file contents:\n" + updateLogContents); michael@0: do_check_true(false); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater binary tests for verifying the state of files and michael@0: * directories after a successful update. michael@0: */ michael@0: function checkFilesAfterUpdateSuccess() { michael@0: logTestInfo("testing contents of files after a successful update"); michael@0: gTestFiles.forEach(function CFAUS_TF_FE(aTestFile) { michael@0: let testFile = getTargetDirFile(aTestFile.relPathDir + aTestFile.fileName, michael@0: true); michael@0: logTestInfo("testing file: " + testFile.path); michael@0: if (aTestFile.compareFile || aTestFile.compareContents) { michael@0: do_check_true(testFile.exists()); michael@0: michael@0: // Skip these tests on Windows and OS/2 since their michael@0: // implementaions of chmod doesn't really set permissions. michael@0: if (!IS_WIN && aTestFile.comparePerms) { michael@0: // Check if the permssions as set in the complete mar file are correct. michael@0: let logPerms = "testing file permissions - "; michael@0: if (aTestFile.originalPerms) { michael@0: logPerms += "original permissions: " + michael@0: aTestFile.originalPerms.toString(8) + ", "; michael@0: } michael@0: logPerms += "compare permissions : " + michael@0: aTestFile.comparePerms.toString(8) + ", "; michael@0: logPerms += "updated permissions : " + testFile.permissions.toString(8); michael@0: logTestInfo(logPerms); michael@0: do_check_eq(testFile.permissions & 0xfff, aTestFile.comparePerms & 0xfff); michael@0: } michael@0: michael@0: let fileContents1 = readFileBytes(testFile); michael@0: let fileContents2 = aTestFile.compareFile ? michael@0: readFileBytes(getTestDirFile(aTestFile.compareFile)) : michael@0: aTestFile.compareContents; michael@0: // Don't write the contents of the file to the log to reduce log spam michael@0: // unless there is a failure. michael@0: if (fileContents1 == fileContents2) { michael@0: logTestInfo("file contents are correct"); michael@0: do_check_true(true); michael@0: } else { michael@0: logTestInfo("file contents are not correct"); michael@0: do_check_eq(fileContents1, fileContents2); michael@0: } michael@0: } else { michael@0: do_check_false(testFile.exists()); michael@0: } michael@0: }); michael@0: michael@0: logTestInfo("testing operations specified in removed-files were performed " + michael@0: "after a successful update"); michael@0: gTestDirs.forEach(function CFAUS_TD_FE(aTestDir) { michael@0: let testDir = getTargetDirFile(aTestDir.relPathDir, true); michael@0: logTestInfo("testing directory: " + testDir.path); michael@0: if (aTestDir.dirRemoved) { michael@0: do_check_false(testDir.exists()); michael@0: } else { michael@0: do_check_true(testDir.exists()); michael@0: michael@0: if (aTestDir.files) { michael@0: aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) { michael@0: let testFile = getTargetDirFile(aTestDir.relPathDir + aTestFile, true); michael@0: logTestInfo("testing directory file: " + testFile.path); michael@0: if (aTestDir.filesRemoved) { michael@0: do_check_false(testFile.exists()); michael@0: } else { michael@0: do_check_true(testFile.exists()); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: if (aTestDir.subDirs) { michael@0: aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) { michael@0: let testSubDir = getTargetDirFile(aTestDir.relPathDir + aSubDir, true); michael@0: logTestInfo("testing sub-directory: " + testSubDir.path); michael@0: do_check_true(testSubDir.exists()); michael@0: if (aTestDir.subDirFiles) { michael@0: aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) { michael@0: let testFile = getTargetDirFile(aTestDir.relPathDir + aSubDir + aTestFile, true); michael@0: logTestInfo("testing sub-directory file: " + testFile.path); michael@0: do_check_true(testFile.exists()); michael@0: }); michael@0: } michael@0: }); michael@0: } michael@0: } michael@0: }); michael@0: michael@0: checkFilesAfterUpdateCommon(); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater binary tests for verifying the state of files and michael@0: * directories after a failed update. michael@0: * michael@0: * @param aGetDirectory: the function used to get the files in the target directory. michael@0: * Pass getApplyDirFile if you want to test the case of a failed switch request. michael@0: */ michael@0: function checkFilesAfterUpdateFailure(aGetDirectory) { michael@0: let getdir = aGetDirectory || getTargetDirFile; michael@0: logTestInfo("testing contents of files after a failed update"); michael@0: gTestFiles.forEach(function CFAUF_TF_FE(aTestFile) { michael@0: let testFile = getdir(aTestFile.relPathDir + aTestFile.fileName, true); michael@0: logTestInfo("testing file: " + testFile.path); michael@0: if (aTestFile.compareFile || aTestFile.compareContents) { michael@0: do_check_true(testFile.exists()); michael@0: michael@0: // Skip these tests on Windows and OS/2 since their michael@0: // implementaions of chmod doesn't really set permissions. michael@0: if (!IS_WIN && aTestFile.comparePerms) { michael@0: // Check the original permssions are retained on the file. michael@0: let logPerms = "testing file permissions - "; michael@0: if (aTestFile.originalPerms) { michael@0: logPerms += "original permissions: " + michael@0: aTestFile.originalPerms.toString(8) + ", "; michael@0: } michael@0: logPerms += "compare permissions : " + michael@0: aTestFile.comparePerms.toString(8) + ", "; michael@0: logPerms += "updated permissions : " + testFile.permissions.toString(8); michael@0: logTestInfo(logPerms); michael@0: do_check_eq(testFile.permissions & 0xfff, aTestFile.comparePerms & 0xfff); michael@0: } michael@0: michael@0: let fileContents1 = readFileBytes(testFile); michael@0: let fileContents2 = aTestFile.compareFile ? michael@0: readFileBytes(getTestDirFile(aTestFile.compareFile)) : michael@0: aTestFile.compareContents; michael@0: // Don't write the contents of the file to the log to reduce log spam michael@0: // unless there is a failure. michael@0: if (fileContents1 == fileContents2) { michael@0: logTestInfo("file contents are correct"); michael@0: do_check_true(true); michael@0: } else { michael@0: logTestInfo("file contents are not correct"); michael@0: do_check_eq(fileContents1, fileContents2); michael@0: } michael@0: } else { michael@0: do_check_false(testFile.exists()); michael@0: } michael@0: }); michael@0: michael@0: logTestInfo("testing operations specified in removed-files were not " + michael@0: "performed after a failed update"); michael@0: gTestDirs.forEach(function CFAUF_TD_FE(aTestDir) { michael@0: let testDir = getdir(aTestDir.relPathDir, true); michael@0: logTestInfo("testing directory: " + testDir.path); michael@0: do_check_true(testDir.exists()); michael@0: michael@0: if (aTestDir.files) { michael@0: aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) { michael@0: let testFile = getdir(aTestDir.relPathDir + aTestFile, true); michael@0: logTestInfo("testing directory file: " + testFile.path); michael@0: do_check_true(testFile.exists()); michael@0: }); michael@0: } michael@0: michael@0: if (aTestDir.subDirs) { michael@0: aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) { michael@0: let testSubDir = getdir(aTestDir.relPathDir + aSubDir, true); michael@0: logTestInfo("testing sub-directory: " + testSubDir.path); michael@0: do_check_true(testSubDir.exists()); michael@0: if (aTestDir.subDirFiles) { michael@0: aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) { michael@0: let testFile = getdir(aTestDir.relPathDir + aSubDir + aTestFile, michael@0: true); michael@0: logTestInfo("testing sub-directory file: " + testFile.path); michael@0: do_check_true(testFile.exists()); michael@0: }); michael@0: } michael@0: }); michael@0: } michael@0: }); michael@0: michael@0: checkFilesAfterUpdateCommon(); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater binary tests for verifying the state of common michael@0: * files and directories after a successful or failed update. michael@0: */ michael@0: function checkFilesAfterUpdateCommon() { michael@0: logTestInfo("testing extra directories"); michael@0: gTestExtraDirs.forEach(function CFAUC_TED_FE(aTestExtraDir) { michael@0: let testDir = getTargetDirFile(aTestExtraDir.relPathDir, true); michael@0: logTestInfo("testing directory: " + testDir.path); michael@0: if (aTestExtraDir.dirExists) { michael@0: do_check_true(testDir.exists()); michael@0: } else { michael@0: do_check_false(testDir.exists()); michael@0: } michael@0: }); michael@0: michael@0: logTestInfo("testing updating directory doesn't exist in the application " + michael@0: "directory"); michael@0: let updatingDir = getTargetDirFile("updating", true); michael@0: do_check_false(updatingDir.exists()); michael@0: michael@0: if (gStageUpdate) { michael@0: logTestInfo("testing updating directory doesn't exist in the updated " + michael@0: "directory"); michael@0: updatingDir = getApplyDirFile("updating", true); michael@0: do_check_false(updatingDir.exists()); michael@0: michael@0: // This should never exist since the update was applied to the updated michael@0: // directory and the files should never be in use. michael@0: logTestInfo("testing tobedeleted directory doesn't exist in the updated " + michael@0: "directory"); michael@0: let toBeDeletedDir = getApplyDirFile(DIR_TOBEDELETED, true); michael@0: do_check_false(toBeDeletedDir.exists()); michael@0: } michael@0: michael@0: logTestInfo("testing patch files should not be left behind"); michael@0: let updatesDir = getUpdatesPatchDir(); michael@0: let entries = updatesDir.QueryInterface(AUS_Ci.nsIFile).directoryEntries; michael@0: while (entries.hasMoreElements()) { michael@0: let entry = entries.getNext().QueryInterface(AUS_Ci.nsIFile); michael@0: do_check_neq(getFileExtension(entry), "patch"); michael@0: } michael@0: michael@0: logTestInfo("testing backup files should not be left behind"); michael@0: let applyToDir = getTargetDirFile(null, true); michael@0: checkFilesInDirRecursive(applyToDir, checkForBackupFiles); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater binary tests for verifying the contents of the michael@0: * updater callback application log which should contain the arguments passed to michael@0: * the callback application. michael@0: */ michael@0: function checkCallbackAppLog() { michael@0: let appLaunchLog = getApplyDirFile("a/b/" + gCallbackArgs[1], true); michael@0: if (!appLaunchLog.exists()) { michael@0: do_timeout(TEST_HELPER_TIMEOUT, checkCallbackAppLog); michael@0: return; michael@0: } michael@0: michael@0: let expectedLogContents = gCallbackArgs.join("\n") + "\n"; michael@0: let logContents = readFile(appLaunchLog); michael@0: // It is possible for the log file contents check to occur before the log file michael@0: // contents are completely written so wait until the contents are the expected michael@0: // value. If the contents are never the expected value then the test will michael@0: // fail by timing out. michael@0: if (logContents != expectedLogContents) { michael@0: do_timeout(TEST_HELPER_TIMEOUT, checkCallbackAppLog); michael@0: return; michael@0: } michael@0: michael@0: if (logContents == expectedLogContents) { michael@0: logTestInfo("callback log file contents are correct"); michael@0: do_check_true(true); michael@0: } else { michael@0: logTestInfo("callback log file contents are not correct"); michael@0: do_check_eq(logContents, expectedLogContents); michael@0: } michael@0: michael@0: waitForFilesInUse(); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater service tests for verifying the contents of the michael@0: * updater callback application log which should contain the arguments passed to michael@0: * the callback application. michael@0: */ michael@0: function checkCallbackServiceLog() { michael@0: do_check_neq(gServiceLaunchedCallbackLog, null); michael@0: michael@0: let expectedLogContents = gServiceLaunchedCallbackArgs.join("\n") + "\n"; michael@0: let logFile = AUS_Cc["@mozilla.org/file/local;1"].createInstance(AUS_Ci.nsILocalFile); michael@0: logFile.initWithPath(gServiceLaunchedCallbackLog); michael@0: let logContents = readFile(logFile); michael@0: // It is possible for the log file contents check to occur before the log file michael@0: // contents are completely written so wait until the contents are the expected michael@0: // value. If the contents are never the expected value then the test will michael@0: // fail by timing out. michael@0: if (logContents != expectedLogContents) { michael@0: logTestInfo("callback service log not expected value, waiting longer"); michael@0: do_timeout(TEST_HELPER_TIMEOUT, checkCallbackServiceLog); michael@0: return; michael@0: } michael@0: michael@0: logTestInfo("testing that the callback application successfully launched " + michael@0: "and the expected command line arguments were passed to it"); michael@0: do_check_eq(logContents, expectedLogContents); michael@0: michael@0: waitForFilesInUse(); michael@0: } michael@0: michael@0: // Waits until files that are in use that break tests are no longer in use and michael@0: // then calls do_test_finished. michael@0: function waitForFilesInUse() { michael@0: if (IS_WIN) { michael@0: let appBin = getApplyDirFile(FILE_APP_BIN, true); michael@0: let maintSvcInstaller = getApplyDirFile(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN, true); michael@0: let helper = getApplyDirFile("uninstall/helper.exe", true); michael@0: let updater = getUpdatesPatchDir(); michael@0: updater.append(FILE_UPDATER_BIN); michael@0: michael@0: let files = [appBin, updater, maintSvcInstaller, helper]; michael@0: michael@0: for (var i = 0; i < files.length; ++i) { michael@0: let file = files[i]; michael@0: let fileBak = file.parent.clone(); michael@0: if (file.exists()) { michael@0: fileBak.append(file.leafName + ".bak"); michael@0: try { michael@0: if (fileBak.exists()) { michael@0: fileBak.remove(false); michael@0: } michael@0: file.copyTo(fileBak.parent, fileBak.leafName); michael@0: file.remove(false); michael@0: fileBak.moveTo(file.parent, file.leafName); michael@0: logTestInfo("file is not in use. Path: " + file.path); michael@0: } catch (e) { michael@0: logTestInfo("file in use, will try again after " + TEST_CHECK_TIMEOUT + michael@0: " ms, Path: " + file.path + ", Exception: " + e); michael@0: try { michael@0: if (fileBak.exists()) { michael@0: fileBak.remove(false); michael@0: } michael@0: } catch (e) { michael@0: logTestInfo("unable to remove file, this should never happen! " + michael@0: "Path: " + fileBak.path + ", Exception: " + e); michael@0: } michael@0: do_timeout(TEST_CHECK_TIMEOUT, waitForFilesInUse); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: logTestInfo("calling doTestFinish"); michael@0: doTestFinish(); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater binary tests for verifying there are no update michael@0: * backup files left behind after an update. michael@0: * michael@0: * @param aFile michael@0: * An nsIFile to check if it has moz-backup for its extension. michael@0: */ michael@0: function checkForBackupFiles(aFile) { michael@0: do_check_neq(getFileExtension(aFile), "moz-backup"); michael@0: } michael@0: michael@0: /** michael@0: * Helper function for updater binary tests for recursively enumerating a michael@0: * directory and calling a callback function with the file as a parameter for michael@0: * each file found. michael@0: * michael@0: * @param aDir michael@0: * A nsIFile for the directory to be deleted michael@0: * @param aCallback michael@0: * A callback function that will be called with the file as a michael@0: * parameter for each file found. michael@0: */ michael@0: function checkFilesInDirRecursive(aDir, aCallback) { michael@0: if (!aDir.exists()) michael@0: do_throw("Directory must exist!"); michael@0: michael@0: let dirEntries = aDir.directoryEntries; michael@0: while (dirEntries.hasMoreElements()) { michael@0: let entry = dirEntries.getNext().QueryInterface(AUS_Ci.nsIFile); michael@0: michael@0: if (entry.isDirectory()) { michael@0: checkFilesInDirRecursive(entry, aCallback); michael@0: } else { michael@0: aCallback(entry); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Sets up the bare bones XMLHttpRequest implementation below. michael@0: * michael@0: * @param aCallback michael@0: * The callback function that will call the nsIDomEventListener's michael@0: * handleEvent method. michael@0: * michael@0: * Example of the callback function michael@0: * michael@0: * function callHandleEvent() { michael@0: * gXHR.status = gExpectedStatus; michael@0: * var e = { target: gXHR }; michael@0: * gXHR.onload.handleEvent(e); michael@0: * } michael@0: */ michael@0: function overrideXHR(aCallback) { michael@0: gXHRCallback = aCallback; michael@0: gXHR = new xhr(); michael@0: var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); michael@0: registrar.registerFactory(gXHR.classID, gXHR.classDescription, michael@0: gXHR.contractID, gXHR); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Bare bones XMLHttpRequest implementation for testing onprogress, onerror, michael@0: * and onload nsIDomEventListener handleEvent. michael@0: */ michael@0: function makeHandler(aVal) { michael@0: if (typeof aVal == "function") michael@0: return { handleEvent: aVal }; michael@0: return aVal; michael@0: } michael@0: function xhr() { michael@0: } michael@0: xhr.prototype = { michael@0: overrideMimeType: function(aMimetype) { }, michael@0: setRequestHeader: function(aHeader, aValue) { }, michael@0: status: null, michael@0: channel: { set notificationCallbacks(aVal) { } }, michael@0: _url: null, michael@0: _method: null, michael@0: open: function(aMethod, aUrl) { michael@0: gXHR.channel.originalURI = Services.io.newURI(aUrl, null, null); michael@0: gXHR._method = aMethod; gXHR._url = aUrl; michael@0: }, michael@0: responseXML: null, michael@0: responseText: null, michael@0: send: function(aBody) { michael@0: do_execute_soon(gXHRCallback); // Use a timeout so the XHR completes michael@0: }, michael@0: _onprogress: null, michael@0: set onprogress(aValue) { gXHR._onprogress = makeHandler(aValue); }, michael@0: get onprogress() { return gXHR._onprogress; }, michael@0: _onerror: null, michael@0: set onerror(aValue) { gXHR._onerror = makeHandler(aValue); }, michael@0: get onerror() { return gXHR._onerror; }, michael@0: _onload: null, michael@0: set onload(aValue) { gXHR._onload = makeHandler(aValue); }, michael@0: get onload() { return gXHR._onload; }, michael@0: addEventListener: function(aEvent, aValue, aCapturing) { michael@0: eval("gXHR._on" + aEvent + " = aValue"); michael@0: }, michael@0: flags: AUS_Ci.nsIClassInfo.SINGLETON, michael@0: implementationLanguage: AUS_Ci.nsIProgrammingLanguage.JAVASCRIPT, michael@0: getHelperForLanguage: function(aLanguage) null, michael@0: getInterfaces: function(aCount) { michael@0: var interfaces = [AUS_Ci.nsISupports]; michael@0: aCount.value = interfaces.length; michael@0: return interfaces; michael@0: }, michael@0: classDescription: "XMLHttpRequest", michael@0: contractID: "@mozilla.org/xmlextras/xmlhttprequest;1", michael@0: classID: Components.ID("{c9b37f43-4278-4304-a5e0-600991ab08cb}"), michael@0: createInstance: function(aOuter, aIID) { michael@0: if (aOuter == null) michael@0: return gXHR.QueryInterface(aIID); michael@0: throw AUS_Cr.NS_ERROR_NO_AGGREGATION; michael@0: }, michael@0: QueryInterface: function(aIID) { michael@0: if (aIID.equals(AUS_Ci.nsIClassInfo) || michael@0: aIID.equals(AUS_Ci.nsISupports)) michael@0: return gXHR; michael@0: throw AUS_Cr.NS_ERROR_NO_INTERFACE; michael@0: }, michael@0: get wrappedJSObject() { return this; } michael@0: }; michael@0: michael@0: /** michael@0: * Helper function to override the update prompt component to verify whether it michael@0: * is called or not. michael@0: * michael@0: * @param aCallback michael@0: * The callback to call if the update prompt component is called. michael@0: */ michael@0: function overrideUpdatePrompt(aCallback) { michael@0: var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); michael@0: gUpdatePrompt = new UpdatePrompt(); michael@0: gUpdatePromptCallback = aCallback; michael@0: registrar.registerFactory(gUpdatePrompt.classID, gUpdatePrompt.classDescription, michael@0: gUpdatePrompt.contractID, gUpdatePrompt); michael@0: } michael@0: michael@0: function UpdatePrompt() { michael@0: var fns = ["checkForUpdates", "showUpdateAvailable", "showUpdateDownloaded", michael@0: "showUpdateError", "showUpdateHistory", "showUpdateInstalled"]; michael@0: michael@0: fns.forEach(function(aPromptFn) { michael@0: UpdatePrompt.prototype[aPromptFn] = function() { michael@0: if (!gUpdatePromptCallback) { michael@0: return; michael@0: } michael@0: michael@0: var callback = gUpdatePromptCallback[aPromptFn]; michael@0: if (!callback) { michael@0: return; michael@0: } michael@0: michael@0: callback.apply(gUpdatePromptCallback, michael@0: Array.prototype.slice.call(arguments)); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: UpdatePrompt.prototype = { michael@0: flags: AUS_Ci.nsIClassInfo.SINGLETON, michael@0: implementationLanguage: AUS_Ci.nsIProgrammingLanguage.JAVASCRIPT, michael@0: getHelperForLanguage: function(aLanguage) null, michael@0: getInterfaces: function(aCount) { michael@0: var interfaces = [AUS_Ci.nsISupports, AUS_Ci.nsIUpdatePrompt]; michael@0: aCount.value = interfaces.length; michael@0: return interfaces; michael@0: }, michael@0: classDescription: "UpdatePrompt", michael@0: contractID: "@mozilla.org/updates/update-prompt;1", michael@0: classID: Components.ID("{8c350a15-9b90-4622-93a1-4d320308664b}"), michael@0: createInstance: function(aOuter, aIID) { michael@0: if (aOuter == null) michael@0: return gUpdatePrompt.QueryInterface(aIID); michael@0: throw AUS_Cr.NS_ERROR_NO_AGGREGATION; michael@0: }, michael@0: QueryInterface: function(aIID) { michael@0: if (aIID.equals(AUS_Ci.nsIClassInfo) || michael@0: aIID.equals(AUS_Ci.nsISupports) || michael@0: aIID.equals(AUS_Ci.nsIUpdatePrompt)) michael@0: return gUpdatePrompt; michael@0: throw AUS_Cr.NS_ERROR_NO_INTERFACE; michael@0: }, michael@0: }; michael@0: michael@0: /* Update check listener */ michael@0: const updateCheckListener = { michael@0: onProgress: function UCL_onProgress(aRequest, aPosition, aTotalSize) { michael@0: }, michael@0: michael@0: onCheckComplete: function UCL_onCheckComplete(aRequest, aUpdates, aUpdateCount) { michael@0: gRequestURL = aRequest.channel.originalURI.spec; michael@0: gUpdateCount = aUpdateCount; michael@0: gUpdates = aUpdates; michael@0: logTestInfo("url = " + gRequestURL + ", " + michael@0: "request.status = " + aRequest.status + ", " + michael@0: "update.statusText = " + aRequest.statusText + ", " + michael@0: "updateCount = " + aUpdateCount); michael@0: // Use a timeout to allow the XHR to complete michael@0: do_execute_soon(gCheckFunc); michael@0: }, michael@0: michael@0: onError: function UCL_onError(aRequest, aUpdate) { michael@0: gRequestURL = aRequest.channel.originalURI.spec; michael@0: gStatusCode = aRequest.status; michael@0: michael@0: gStatusText = aUpdate.statusText; michael@0: logTestInfo("url = " + gRequestURL + ", " + michael@0: "request.status = " + gStatusCode + ", " + michael@0: "update.statusText = " + gStatusText); michael@0: // Use a timeout to allow the XHR to complete michael@0: do_execute_soon(gCheckFunc.bind(null, aRequest, aUpdate)); michael@0: }, michael@0: michael@0: QueryInterface: function(aIID) { michael@0: if (!aIID.equals(AUS_Ci.nsIUpdateCheckListener) && michael@0: !aIID.equals(AUS_Ci.nsISupports)) michael@0: throw AUS_Cr.NS_ERROR_NO_INTERFACE; michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: /* Update download listener - nsIRequestObserver */ michael@0: const downloadListener = { michael@0: onStartRequest: function DL_onStartRequest(aRequest, aContext) { michael@0: }, michael@0: michael@0: onProgress: function DL_onProgress(aRequest, aContext, aProgress, aMaxProgress) { michael@0: }, michael@0: michael@0: onStatus: function DL_onStatus(aRequest, aContext, aStatus, aStatusText) { michael@0: }, michael@0: michael@0: onStopRequest: function DL_onStopRequest(aRequest, aContext, aStatus) { michael@0: gStatusResult = aStatus; michael@0: // Use a timeout to allow the request to complete michael@0: do_execute_soon(gCheckFunc); michael@0: }, michael@0: michael@0: QueryInterface: function DL_QueryInterface(aIID) { michael@0: if (!aIID.equals(AUS_Ci.nsIRequestObserver) && michael@0: !aIID.equals(AUS_Ci.nsIProgressEventSink) && michael@0: !aIID.equals(AUS_Ci.nsISupports)) michael@0: throw AUS_Cr.NS_ERROR_NO_INTERFACE; michael@0: return this; michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * Helper for starting the http server used by the tests michael@0: */ michael@0: function start_httpserver() { michael@0: let dir = getTestDirFile(); michael@0: logTestInfo("http server directory path: " + dir.path); michael@0: michael@0: if (!dir.isDirectory()) { michael@0: do_throw("A file instead of a directory was specified for HttpServer " + michael@0: "registerDirectory! Path: " + dir.path + "\n"); michael@0: } michael@0: michael@0: AUS_Cu.import("resource://testing-common/httpd.js"); michael@0: gTestserver = new HttpServer(); michael@0: gTestserver.registerDirectory("/", dir); michael@0: gTestserver.start(-1); michael@0: let testserverPort = gTestserver.identity.primaryPort; michael@0: gURLData = URL_HOST + ":" + testserverPort + "/"; michael@0: logTestInfo("http server port = " + testserverPort); michael@0: } michael@0: michael@0: /** michael@0: * Helper for stopping the http server used by the tests michael@0: * michael@0: * @param aCallback michael@0: * The callback to call after stopping the http server. michael@0: */ michael@0: function stop_httpserver(aCallback) { michael@0: do_check_true(!!aCallback); michael@0: gTestserver.stop(aCallback); michael@0: } michael@0: michael@0: /** michael@0: * Creates an nsIXULAppInfo michael@0: * michael@0: * @param aID michael@0: * The ID of the test application michael@0: * @param aName michael@0: * A name for the test application michael@0: * @param aVersion michael@0: * The version of the application michael@0: * @param aPlatformVersion michael@0: * The gecko version of the application michael@0: */ michael@0: function createAppInfo(aID, aName, aVersion, aPlatformVersion) { michael@0: const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; michael@0: const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}"); michael@0: var XULAppInfo = { michael@0: vendor: APP_INFO_VENDOR, michael@0: name: aName, michael@0: ID: aID, michael@0: version: aVersion, michael@0: appBuildID: "2007010101", michael@0: platformVersion: aPlatformVersion, michael@0: platformBuildID: "2007010101", michael@0: inSafeMode: false, michael@0: logConsoleErrors: true, michael@0: OS: "XPCShell", michael@0: XPCOMABI: "noarch-spidermonkey", michael@0: michael@0: QueryInterface: function QueryInterface(aIID) { michael@0: if (aIID.equals(AUS_Ci.nsIXULAppInfo) || michael@0: aIID.equals(AUS_Ci.nsIXULRuntime) || michael@0: #ifdef XP_WIN michael@0: aIID.equals(AUS_Ci.nsIWinAppHelper) || michael@0: #endif michael@0: aIID.equals(AUS_Ci.nsISupports)) michael@0: return this; michael@0: throw AUS_Cr.NS_ERROR_NO_INTERFACE; michael@0: } michael@0: }; michael@0: michael@0: var XULAppInfoFactory = { michael@0: createInstance: function (aOuter, aIID) { michael@0: if (aOuter == null) michael@0: return XULAppInfo.QueryInterface(aIID); michael@0: throw AUS_Cr.NS_ERROR_NO_AGGREGATION; michael@0: } michael@0: }; michael@0: michael@0: var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); michael@0: registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo", michael@0: XULAPPINFO_CONTRACTID, XULAppInfoFactory); michael@0: } michael@0: michael@0: /** michael@0: * Returns the platform specific arguments used by nsIProcess when launching michael@0: * the application. michael@0: * michael@0: * @param aExtraArgs (optional) michael@0: * An array of extra arguments to append to the default arguments. michael@0: * @return an array of arguments to be passed to nsIProcess. michael@0: * michael@0: * Note: a shell is necessary to pipe the application's console output which michael@0: * would otherwise pollute the xpcshell log. michael@0: * michael@0: * Command line arguments used when launching the application: michael@0: * -no-remote prevents shell integration from being affected by an existing michael@0: * application process. michael@0: * -process-updates makes the application exits after being relaunched by the michael@0: * updater. michael@0: * the platform specific string defined by PIPE_TO_NULL to output both stdout michael@0: * and stderr to null. This is needed to prevent output from the application michael@0: * from ending up in the xpchsell log. michael@0: */ michael@0: function getProcessArgs(aExtraArgs) { michael@0: if (!aExtraArgs) { michael@0: aExtraArgs = []; michael@0: } michael@0: michael@0: let appBinPath = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false).path; michael@0: if (/ /.test(appBinPath)) { michael@0: appBinPath = '"' + appBinPath + '"'; michael@0: } michael@0: michael@0: let args; michael@0: if (IS_UNIX) { michael@0: let launchScript = getLaunchScript(); michael@0: // Precreate the script with executable permissions michael@0: launchScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY); michael@0: michael@0: let scriptContents = "#! /bin/sh\n"; michael@0: scriptContents += appBinPath + " -no-remote -process-updates " + michael@0: aExtraArgs.join(" ") + " " + PIPE_TO_NULL; michael@0: writeFile(launchScript, scriptContents); michael@0: logTestInfo("created " + launchScript.path + " containing:\n" + michael@0: scriptContents); michael@0: args = [launchScript.path]; michael@0: } else { michael@0: args = ["/D", "/Q", "/C", appBinPath, "-no-remote", "-process-updates"]. michael@0: concat(aExtraArgs).concat([PIPE_TO_NULL]); michael@0: } michael@0: return args; michael@0: } michael@0: michael@0: /** michael@0: * Gets a file path for the application to dump its arguments into. This is used michael@0: * to verify that a callback application is launched. michael@0: * michael@0: * @return the file for the application to dump its arguments into. michael@0: */ michael@0: function getAppArgsLogPath() { michael@0: let appArgsLog = do_get_file("/", true); michael@0: appArgsLog.append(gTestID + "_app_args_log"); michael@0: if (appArgsLog.exists()) { michael@0: appArgsLog.remove(false); michael@0: } michael@0: let appArgsLogPath = appArgsLog.path; michael@0: if (/ /.test(appArgsLogPath)) { michael@0: appArgsLogPath = '"' + appArgsLogPath + '"'; michael@0: } michael@0: return appArgsLogPath; michael@0: } michael@0: michael@0: /** michael@0: * Gets the nsIFile reference for the shell script to launch the application. If michael@0: * the file exists it will be removed by this function. michael@0: * michael@0: * @return the nsIFile for the shell script to launch the application. michael@0: */ michael@0: function getLaunchScript() { michael@0: let launchScript = do_get_file("/", true); michael@0: launchScript.append(gTestID + "_launch.sh"); michael@0: if (launchScript.exists()) { michael@0: launchScript.remove(false); michael@0: } michael@0: return launchScript; michael@0: } michael@0: michael@0: /** michael@0: * Makes GreD, XREExeF, and UpdRootD point to unique file system locations so michael@0: * xpcshell tests can run in parallel and to keep the environment clean. michael@0: */ michael@0: function adjustGeneralPaths() { michael@0: let dirProvider = { michael@0: getFile: function AGP_DP_getFile(aProp, aPersistent) { michael@0: aPersistent.value = true; michael@0: switch (aProp) { michael@0: case NS_GRE_DIR: michael@0: if (gUseTestAppDir) { michael@0: return getApplyDirFile(DIR_BIN_REL_PATH, true); michael@0: } michael@0: break; michael@0: case XRE_EXECUTABLE_FILE: michael@0: if (gUseTestAppDir) { michael@0: return getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, true); michael@0: } michael@0: break; michael@0: case XRE_UPDATE_ROOT_DIR: michael@0: return getMockUpdRootD(); michael@0: } michael@0: return null; michael@0: }, michael@0: QueryInterface: function(aIID) { michael@0: if (aIID.equals(AUS_Ci.nsIDirectoryServiceProvider) || michael@0: aIID.equals(AUS_Ci.nsISupports)) michael@0: return this; michael@0: throw AUS_Cr.NS_ERROR_NO_INTERFACE; michael@0: } michael@0: }; michael@0: let ds = Services.dirsvc.QueryInterface(AUS_Ci.nsIDirectoryService); michael@0: ds.QueryInterface(AUS_Ci.nsIProperties).undefine(NS_GRE_DIR); michael@0: ds.QueryInterface(AUS_Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE); michael@0: ds.registerProvider(dirProvider); michael@0: do_register_cleanup(function AGP_cleanup() { michael@0: logTestInfo("start - unregistering directory provider"); michael@0: michael@0: if (gAppTimer) { michael@0: logTestInfo("start - cancel app timer"); michael@0: gAppTimer.cancel(); michael@0: gAppTimer = null; michael@0: logTestInfo("finish - cancel app timer"); michael@0: } michael@0: michael@0: if (gProcess && gProcess.isRunning) { michael@0: logTestInfo("start - kill process"); michael@0: try { michael@0: gProcess.kill(); michael@0: } catch (e) { michael@0: logTestInfo("kill process failed. Exception: " + e); michael@0: } michael@0: gProcess = null; michael@0: logTestInfo("finish - kill process"); michael@0: } michael@0: michael@0: if (gHandle) { michael@0: try { michael@0: logTestInfo("start - closing handle"); michael@0: let kernel32 = ctypes.open("kernel32"); michael@0: let CloseHandle = kernel32.declare("CloseHandle", ctypes.default_abi, michael@0: ctypes.bool, /*return*/ michael@0: ctypes.voidptr_t /*handle*/); michael@0: if (!CloseHandle(gHandle)) { michael@0: logTestInfo("call to CloseHandle failed"); michael@0: } michael@0: kernel32.close(); michael@0: gHandle = null; michael@0: logTestInfo("finish - closing handle"); michael@0: } catch (e) { michael@0: logTestInfo("call to CloseHandle failed. Exception: " + e); michael@0: } michael@0: } michael@0: michael@0: // Call end_test first before the directory provider is unregistered michael@0: if (typeof(end_test) == typeof(Function)) { michael@0: logTestInfo("calling end_test"); michael@0: end_test(); michael@0: } michael@0: michael@0: ds.unregisterProvider(dirProvider); michael@0: cleanupTestCommon(); michael@0: michael@0: logTestInfo("finish - unregistering directory provider"); michael@0: }); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Helper function for launching the application to apply an update. michael@0: */ michael@0: function launchAppToApplyUpdate() { michael@0: logTestInfo("start - launching application to apply update"); michael@0: michael@0: let appBin = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false); michael@0: michael@0: if (typeof(customLaunchAppToApplyUpdate) == typeof(Function)) { michael@0: customLaunchAppToApplyUpdate(); michael@0: } michael@0: michael@0: let launchBin = getLaunchBin(); michael@0: let args = getProcessArgs(); michael@0: logTestInfo("launching " + launchBin.path + " " + args.join(" ")); michael@0: michael@0: gProcess = AUS_Cc["@mozilla.org/process/util;1"]. michael@0: createInstance(AUS_Ci.nsIProcess); michael@0: gProcess.init(launchBin); michael@0: michael@0: gAppTimer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer); michael@0: gAppTimer.initWithCallback(gTimerCallback, APP_TIMER_TIMEOUT, michael@0: AUS_Ci.nsITimer.TYPE_ONE_SHOT); michael@0: michael@0: setEnvironment(); michael@0: logTestInfo("launching application"); michael@0: gProcess.runAsync(args, args.length, gProcessObserver); michael@0: resetEnvironment(); michael@0: michael@0: logTestInfo("finish - launching application to apply update"); michael@0: } michael@0: michael@0: /** michael@0: * The observer for the call to nsIProcess:runAsync. michael@0: */ michael@0: let gProcessObserver = { michael@0: observe: function PO_observe(aSubject, aTopic, aData) { michael@0: logTestInfo("topic: " + aTopic + ", process exitValue: " + michael@0: gProcess.exitValue); michael@0: if (gAppTimer) { michael@0: gAppTimer.cancel(); michael@0: gAppTimer = null; michael@0: } michael@0: if (aTopic != "process-finished" || gProcess.exitValue != 0) { michael@0: do_throw("Failed to launch application"); michael@0: } michael@0: do_timeout(TEST_CHECK_TIMEOUT, checkUpdateFinished); michael@0: }, michael@0: QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIObserver]) michael@0: }; michael@0: michael@0: /** michael@0: * The timer callback to kill the process if it takes too long. michael@0: */ michael@0: let gTimerCallback = { michael@0: notify: function TC_notify(aTimer) { michael@0: gAppTimer = null; michael@0: if (gProcess.isRunning) { michael@0: logTestInfo("attempt to kill process"); michael@0: gProcess.kill(); michael@0: } michael@0: do_throw("launch application timer expired"); michael@0: }, michael@0: QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsITimerCallback]) michael@0: }; michael@0: michael@0: /** michael@0: * The update-staged observer for the call to nsIUpdateProcessor:processUpdate. michael@0: */ michael@0: let gUpdateStagedObserver = { michael@0: observe: function(aSubject, aTopic, aData) { michael@0: if (aTopic == "update-staged") { michael@0: Services.obs.removeObserver(gUpdateStagedObserver, "update-staged"); michael@0: checkUpdateApplied(); michael@0: } michael@0: }, michael@0: QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIObserver]) michael@0: }; michael@0: michael@0: /** michael@0: * Sets the environment that will be used by the application process when it is michael@0: * launched. michael@0: */ michael@0: function setEnvironment() { michael@0: // Prevent setting the environment more than once. michael@0: if (gShouldResetEnv !== undefined) michael@0: return; michael@0: michael@0: gShouldResetEnv = true; michael@0: michael@0: let env = AUS_Cc["@mozilla.org/process/environment;1"]. michael@0: getService(AUS_Ci.nsIEnvironment); michael@0: if (IS_WIN && !env.exists("XRE_NO_WINDOWS_CRASH_DIALOG")) { michael@0: gAddedEnvXRENoWindowsCrashDialog = true; michael@0: logTestInfo("setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " + michael@0: "variable to 1... previously it didn't exist"); michael@0: env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1"); michael@0: } michael@0: michael@0: if (IS_UNIX) { michael@0: let appGreDir = gGREDirOrig.clone(); michael@0: let envGreDir = AUS_Cc["@mozilla.org/file/local;1"]. michael@0: createInstance(AUS_Ci.nsILocalFile); michael@0: let shouldSetEnv = true; michael@0: if (IS_MACOSX) { michael@0: if (env.exists("DYLD_LIBRARY_PATH")) { michael@0: gEnvDyldLibraryPath = env.get("DYLD_LIBRARY_PATH"); michael@0: envGreDir.initWithPath(gEnvDyldLibraryPath); michael@0: if (envGreDir.path == appGreDir.path) { michael@0: gEnvDyldLibraryPath = null; michael@0: shouldSetEnv = false; michael@0: } michael@0: } michael@0: michael@0: if (shouldSetEnv) { michael@0: logTestInfo("setting DYLD_LIBRARY_PATH environment variable value to " + michael@0: appGreDir.path); michael@0: env.set("DYLD_LIBRARY_PATH", appGreDir.path); michael@0: } michael@0: } else { michael@0: if (env.exists("LD_LIBRARY_PATH")) { michael@0: gEnvLdLibraryPath = env.get("LD_LIBRARY_PATH"); michael@0: envGreDir.initWithPath(gEnvLdLibraryPath); michael@0: if (envGreDir.path == appGreDir.path) { michael@0: gEnvLdLibraryPath = null; michael@0: shouldSetEnv = false; michael@0: } michael@0: } michael@0: michael@0: if (shouldSetEnv) { michael@0: logTestInfo("setting LD_LIBRARY_PATH environment variable value to " + michael@0: appGreDir.path); michael@0: env.set("LD_LIBRARY_PATH", appGreDir.path); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (env.exists("XPCOM_MEM_LEAK_LOG")) { michael@0: gEnvXPCOMMemLeakLog = env.get("XPCOM_MEM_LEAK_LOG"); michael@0: logTestInfo("removing the XPCOM_MEM_LEAK_LOG environment variable... " + michael@0: "previous value " + gEnvXPCOMMemLeakLog); michael@0: env.set("XPCOM_MEM_LEAK_LOG", ""); michael@0: } michael@0: michael@0: if (env.exists("XPCOM_DEBUG_BREAK")) { michael@0: gEnvXPCOMDebugBreak = env.get("XPCOM_DEBUG_BREAK"); michael@0: logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " + michael@0: "warn... previous value " + gEnvXPCOMDebugBreak); michael@0: } else { michael@0: logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " + michael@0: "warn... previously it didn't exist"); michael@0: } michael@0: michael@0: env.set("XPCOM_DEBUG_BREAK", "warn"); michael@0: michael@0: if (gStageUpdate) { michael@0: logTestInfo("setting the MOZ_UPDATE_STAGING environment variable to 1\n"); michael@0: env.set("MOZ_UPDATE_STAGING", "1"); michael@0: } michael@0: michael@0: logTestInfo("setting MOZ_NO_SERVICE_FALLBACK environment variable to 1"); michael@0: env.set("MOZ_NO_SERVICE_FALLBACK", "1"); michael@0: } michael@0: michael@0: /** michael@0: * Sets the environment back to the original values after launching the michael@0: * application. michael@0: */ michael@0: function resetEnvironment() { michael@0: // Prevent resetting the environment more than once. michael@0: if (gShouldResetEnv !== true) michael@0: return; michael@0: michael@0: gShouldResetEnv = false; michael@0: michael@0: let env = AUS_Cc["@mozilla.org/process/environment;1"]. michael@0: getService(AUS_Ci.nsIEnvironment); michael@0: michael@0: if (gEnvXPCOMMemLeakLog) { michael@0: logTestInfo("setting the XPCOM_MEM_LEAK_LOG environment variable back to " + michael@0: gEnvXPCOMMemLeakLog); michael@0: env.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog); michael@0: } michael@0: michael@0: if (gEnvXPCOMDebugBreak) { michael@0: logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable back to " + michael@0: gEnvXPCOMDebugBreak); michael@0: env.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak); michael@0: } else { michael@0: logTestInfo("clearing the XPCOM_DEBUG_BREAK environment variable"); michael@0: env.set("XPCOM_DEBUG_BREAK", ""); michael@0: } michael@0: michael@0: if (IS_UNIX) { michael@0: if (IS_MACOSX) { michael@0: if (gEnvDyldLibraryPath) { michael@0: logTestInfo("setting DYLD_LIBRARY_PATH environment variable value " + michael@0: "back to " + gEnvDyldLibraryPath); michael@0: env.set("DYLD_LIBRARY_PATH", gEnvDyldLibraryPath); michael@0: } else { michael@0: logTestInfo("removing DYLD_LIBRARY_PATH environment variable"); michael@0: env.set("DYLD_LIBRARY_PATH", ""); michael@0: } michael@0: } else { michael@0: if (gEnvLdLibraryPath) { michael@0: logTestInfo("setting LD_LIBRARY_PATH environment variable value back " + michael@0: "to " + gEnvLdLibraryPath); michael@0: env.set("LD_LIBRARY_PATH", gEnvLdLibraryPath); michael@0: } else { michael@0: logTestInfo("removing LD_LIBRARY_PATH environment variable"); michael@0: env.set("LD_LIBRARY_PATH", ""); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (IS_WIN && gAddedEnvXRENoWindowsCrashDialog) { michael@0: logTestInfo("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment " + michael@0: "variable"); michael@0: env.set("XRE_NO_WINDOWS_CRASH_DIALOG", ""); michael@0: } michael@0: michael@0: if (gStageUpdate) { michael@0: logTestInfo("removing the MOZ_UPDATE_STAGING environment variable\n"); michael@0: env.set("MOZ_UPDATE_STAGING", ""); michael@0: } michael@0: michael@0: logTestInfo("removing MOZ_NO_SERVICE_FALLBACK environment variable"); michael@0: env.set("MOZ_NO_SERVICE_FALLBACK", ""); michael@0: }