1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/mozapps/update/tests/unit_aus_update/head_update.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3466 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +const INSTALL_LOCALE = "@AB_CD@"; 1.9 +const MOZ_APP_NAME = "@MOZ_APP_NAME@"; 1.10 +const BIN_SUFFIX = "@BIN_SUFFIX@"; 1.11 + 1.12 +#ifdef XP_WIN 1.13 +// MOZ_APP_VENDOR is optional. 1.14 +// On Windows, if MOZ_APP_VENDOR is not defined the updates directory will be 1.15 +// located under %LOCALAPPDATA%\@MOZ_APP_BASENAME@\updates\TaskBarID 1.16 +#ifdef MOZ_APP_VENDOR 1.17 +const MOZ_APP_VENDOR = "@MOZ_APP_VENDOR@"; 1.18 +#else 1.19 +const MOZ_APP_VENDOR = ""; 1.20 +#endif 1.21 + 1.22 +// MOZ_APP_BASENAME is not optional for tests. 1.23 +const MOZ_APP_BASENAME = "@MOZ_APP_BASENAME@"; 1.24 +#endif // XP_WIN 1.25 + 1.26 +const APP_INFO_NAME = "XPCShell"; 1.27 +const APP_INFO_VENDOR = "Mozilla"; 1.28 + 1.29 +#ifdef XP_UNIX 1.30 +const APP_BIN_SUFFIX = "-bin"; 1.31 +#else 1.32 +const APP_BIN_SUFFIX = "@BIN_SUFFIX@"; 1.33 +#endif 1.34 + 1.35 +#ifdef XP_WIN 1.36 +const IS_WIN = true; 1.37 +#else 1.38 +const IS_WIN = false; 1.39 +#endif 1.40 + 1.41 +#ifdef XP_MACOSX 1.42 +const IS_MACOSX = true; 1.43 +#ifdef MOZ_SHARK 1.44 +const IS_SHARK = true; 1.45 +#else 1.46 +const IS_SHARK = false; 1.47 +#endif 1.48 +#else 1.49 +const IS_MACOSX = false; 1.50 +#endif 1.51 + 1.52 +#ifdef XP_UNIX 1.53 +const IS_UNIX = true; 1.54 +#else 1.55 +const IS_UNIX = false; 1.56 +#endif 1.57 + 1.58 +#ifdef ANDROID 1.59 +const IS_ANDROID = true; 1.60 +#else 1.61 +const IS_ANDROID = false; 1.62 +#endif 1.63 + 1.64 +#ifdef MOZ_WIDGET_GONK 1.65 +const IS_TOOLKIT_GONK = true; 1.66 +#else 1.67 +const IS_TOOLKIT_GONK = false; 1.68 +#endif 1.69 + 1.70 +const USE_EXECV = IS_UNIX && !IS_MACOSX; 1.71 + 1.72 +#ifdef MOZ_VERIFY_MAR_SIGNATURE 1.73 +const IS_MAR_CHECKS_ENABLED = true; 1.74 +#else 1.75 +const IS_MAR_CHECKS_ENABLED = false; 1.76 +#endif 1.77 + 1.78 +const URL_HOST = "http://localhost"; 1.79 + 1.80 +const FILE_APP_BIN = MOZ_APP_NAME + APP_BIN_SUFFIX; 1.81 +const FILE_COMPLETE_EXE = "complete.exe"; 1.82 +const FILE_COMPLETE_MAR = "complete.mar"; 1.83 +const FILE_HELPER_BIN = "TestAUSHelper" + BIN_SUFFIX; 1.84 +const FILE_MAINTENANCE_SERVICE_BIN = "maintenanceservice.exe"; 1.85 +const FILE_MAINTENANCE_SERVICE_INSTALLER_BIN = "maintenanceservice_installer.exe"; 1.86 +const FILE_OLD_VERSION_MAR = "old_version.mar"; 1.87 +const FILE_PARTIAL_EXE = "partial.exe"; 1.88 +const FILE_PARTIAL_MAR = "partial.mar"; 1.89 +const FILE_UPDATER_BIN = "updater" + BIN_SUFFIX; 1.90 +const FILE_UPDATER_INI_BAK = "updater.ini.bak"; 1.91 +const FILE_WRONG_CHANNEL_MAR = "wrong_product_channel.mar"; 1.92 + 1.93 +const LOG_COMPLETE_SUCCESS = "complete_log_success"; 1.94 +const LOG_PARTIAL_SUCCESS = "partial_log_success"; 1.95 +const LOG_PARTIAL_FAILURE = "partial_log_failure"; 1.96 + 1.97 +const LOG_SWITCH_SUCCESS = "rename_file: proceeding to rename the directory\n" + 1.98 + "rename_file: proceeding to rename the directory\n" + 1.99 + "Now, remove the tmpDir\n" + 1.100 + "succeeded\n" + 1.101 + "calling QuitProgressUI"; 1.102 + 1.103 +const ERR_RENAME_FILE = "rename_file: failed to rename file"; 1.104 +const ERR_UNABLE_OPEN_DEST = "unable to open destination file"; 1.105 +const ERR_BACKUP_DISCARD = "backup_discard: unable to remove"; 1.106 + 1.107 +const LOG_SVC_SUCCESSFUL_LAUNCH = "Process was started... waiting on result."; 1.108 + 1.109 +// All we care about is that the last modified time has changed so that Mac OS 1.110 +// X Launch Services invalidates its cache so the test allows up to one minute 1.111 +// difference in the last modified time. 1.112 +const MAC_MAX_TIME_DIFFERENCE = 60000; 1.113 + 1.114 +// Time to wait for the test helper process before continuing the test 1.115 +const TEST_HELPER_TIMEOUT = 100; 1.116 + 1.117 +// Time to wait for a check in the test before continuing the test 1.118 +const TEST_CHECK_TIMEOUT = 100; 1.119 + 1.120 +// How many of TEST_CHECK_TIMEOUT to wait before we abort the test. 1.121 +const MAX_TIMEOUT_RUNS = 2000; 1.122 + 1.123 +// Time in seconds the helper application should sleep.the helper's input and output files 1.124 +const HELPER_SLEEP_TIMEOUT = 180; 1.125 + 1.126 +// Maximum number of milliseconds the process that is launched can run before 1.127 +// the test will try to kill it. 1.128 +const APP_TIMER_TIMEOUT = 120000; 1.129 + 1.130 +#ifdef XP_WIN 1.131 +const PIPE_TO_NULL = ">nul"; 1.132 +#else 1.133 +const PIPE_TO_NULL = "> /dev/null 2>&1"; 1.134 +#endif 1.135 + 1.136 +// This default value will be overridden when using the http server. 1.137 +var gURLData = URL_HOST + "/"; 1.138 + 1.139 +var gTestID; 1.140 + 1.141 +var gTestserver; 1.142 + 1.143 +var gRegisteredServiceCleanup; 1.144 + 1.145 +var gXHR; 1.146 +var gXHRCallback; 1.147 + 1.148 +var gUpdatePrompt; 1.149 +var gUpdatePromptCallback; 1.150 + 1.151 +var gCheckFunc; 1.152 +var gResponseBody; 1.153 +var gResponseStatusCode = 200; 1.154 +var gRequestURL; 1.155 +var gUpdateCount; 1.156 +var gUpdates; 1.157 +var gStatusCode; 1.158 +var gStatusText; 1.159 +var gStatusResult; 1.160 + 1.161 +var gProcess; 1.162 +var gAppTimer; 1.163 +var gHandle; 1.164 + 1.165 +var gGREDirOrig; 1.166 +var gAppDirOrig; 1.167 + 1.168 +var gServiceLaunchedCallbackLog = null; 1.169 +var gServiceLaunchedCallbackArgs = null; 1.170 + 1.171 +// Variables are used instead of contants so tests can override these values if 1.172 +// necessary. 1.173 +var gCallbackBinFile = "callback_app" + BIN_SUFFIX; 1.174 +var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"]; 1.175 +var gStageUpdate = false; 1.176 +var gSwitchApp = false; 1.177 +var gDisableReplaceFallback = false; 1.178 +var gUseTestAppDir = true; 1.179 + 1.180 +var gTimeoutRuns = 0; 1.181 + 1.182 +// Environment related globals 1.183 +var gShouldResetEnv = undefined; 1.184 +var gAddedEnvXRENoWindowsCrashDialog = false; 1.185 +var gEnvXPCOMDebugBreak; 1.186 +var gEnvXPCOMMemLeakLog; 1.187 +var gEnvDyldLibraryPath; 1.188 +var gEnvLdLibraryPath; 1.189 + 1.190 +// Set to true to log additional information for debugging. To log additional 1.191 +// information for an individual test set DEBUG_AUS_TEST to true in the test's 1.192 +// run_test function. 1.193 +var DEBUG_AUS_TEST = true; 1.194 +// Never set DEBUG_TEST_LOG to true except when running tests locally or on the 1.195 +// try server since this will force a test that failed a parallel run to fail 1.196 +// when the same test runs non-parallel so the log from parallel test run can 1.197 +// be displayed in the log. 1.198 +var DEBUG_TEST_LOG = false; 1.199 +// Set to false to keep the log file from the failed parallel test run. 1.200 +var gDeleteLogFile = true; 1.201 +var gRealDump; 1.202 +var gTestLogText = ""; 1.203 +var gPassed; 1.204 + 1.205 +#include ../shared.js 1.206 + 1.207 +var gTestFiles = []; 1.208 +var gTestDirs = []; 1.209 + 1.210 +// Common files for both successful and failed updates. 1.211 +var gTestFilesCommon = [ 1.212 +{ 1.213 + description : "Should never change", 1.214 + fileName : FILE_UPDATE_SETTINGS_INI, 1.215 + relPathDir : "a/b/", 1.216 + originalContents : "ShouldNotBeReplaced\n", 1.217 + compareContents : "ShouldNotBeReplaced\n", 1.218 + originalFile : null, 1.219 + compareFile : null, 1.220 + originalPerms : 0o767, 1.221 + comparePerms : 0o767 1.222 +}, { 1.223 + description : "Should never change", 1.224 + fileName : "channel-prefs.js", 1.225 + relPathDir : "a/b/defaults/pref/", 1.226 + originalContents : "ShouldNotBeReplaced\n", 1.227 + compareContents : "ShouldNotBeReplaced\n", 1.228 + originalFile : null, 1.229 + compareFile : null, 1.230 + originalPerms : 0o767, 1.231 + comparePerms : 0o767 1.232 +}]; 1.233 + 1.234 +// Files for a complete successful update. This can be used for a complete 1.235 +// failed update by calling setTestFilesAndDirsForFailure. 1.236 +var gTestFilesCompleteSuccess = [ 1.237 +{ 1.238 + description : "Added by update.manifest (add)", 1.239 + fileName : "precomplete", 1.240 + relPathDir : "", 1.241 + originalContents : null, 1.242 + compareContents : null, 1.243 + originalFile : "partial_precomplete", 1.244 + compareFile : "complete_precomplete", 1.245 + originalPerms : 0o666, 1.246 + comparePerms : 0o644 1.247 +}, { 1.248 + description : "Added by update.manifest (add)", 1.249 + fileName : "searchpluginstext0", 1.250 + relPathDir : "a/b/searchplugins/", 1.251 + originalContents : "ToBeReplacedWithFromComplete\n", 1.252 + compareContents : "FromComplete\n", 1.253 + originalFile : null, 1.254 + compareFile : null, 1.255 + originalPerms : 0o775, 1.256 + comparePerms : 0o644 1.257 +}, { 1.258 + description : "Added by update.manifest (add)", 1.259 + fileName : "searchpluginspng1.png", 1.260 + relPathDir : "a/b/searchplugins/", 1.261 + originalContents : null, 1.262 + compareContents : null, 1.263 + originalFile : null, 1.264 + compareFile : "complete.png", 1.265 + originalPerms : null, 1.266 + comparePerms : 0o644 1.267 +}, { 1.268 + description : "Added by update.manifest (add)", 1.269 + fileName : "searchpluginspng0.png", 1.270 + relPathDir : "a/b/searchplugins/", 1.271 + originalContents : null, 1.272 + compareContents : null, 1.273 + originalFile : "partial.png", 1.274 + compareFile : "complete.png", 1.275 + originalPerms : 0o666, 1.276 + comparePerms : 0o644 1.277 +}, { 1.278 + description : "Added by update.manifest (add)", 1.279 + fileName : "removed-files", 1.280 + relPathDir : "a/b/", 1.281 + originalContents : null, 1.282 + compareContents : null, 1.283 + originalFile : "partial_removed-files", 1.284 + compareFile : "complete_removed-files", 1.285 + originalPerms : 0o666, 1.286 + comparePerms : 0o644 1.287 +}, { 1.288 + description : "Added by update.manifest if the parent directory " + 1.289 + "exists (add-if)", 1.290 + fileName : "extensions1text0", 1.291 + relPathDir : "a/b/distribution/extensions/extensions1/", 1.292 + originalContents : null, 1.293 + compareContents : "FromComplete\n", 1.294 + originalFile : null, 1.295 + compareFile : null, 1.296 + originalPerms : null, 1.297 + comparePerms : 0o644 1.298 +}, { 1.299 + description : "Added by update.manifest if the parent directory " + 1.300 + "exists (add-if)", 1.301 + fileName : "extensions1png1.png", 1.302 + relPathDir : "a/b/distribution/extensions/extensions1/", 1.303 + originalContents : null, 1.304 + compareContents : null, 1.305 + originalFile : "partial.png", 1.306 + compareFile : "complete.png", 1.307 + originalPerms : 0o666, 1.308 + comparePerms : 0o644 1.309 +}, { 1.310 + description : "Added by update.manifest if the parent directory " + 1.311 + "exists (add-if)", 1.312 + fileName : "extensions1png0.png", 1.313 + relPathDir : "a/b/distribution/extensions/extensions1/", 1.314 + originalContents : null, 1.315 + compareContents : null, 1.316 + originalFile : null, 1.317 + compareFile : "complete.png", 1.318 + originalPerms : null, 1.319 + comparePerms : 0o644 1.320 +}, { 1.321 + description : "Added by update.manifest if the parent directory " + 1.322 + "exists (add-if)", 1.323 + fileName : "extensions0text0", 1.324 + relPathDir : "a/b/distribution/extensions/extensions0/", 1.325 + originalContents : "ToBeReplacedWithFromComplete\n", 1.326 + compareContents : "FromComplete\n", 1.327 + originalFile : null, 1.328 + compareFile : null, 1.329 + originalPerms : null, 1.330 + comparePerms : 0o644 1.331 +}, { 1.332 + description : "Added by update.manifest if the parent directory " + 1.333 + "exists (add-if)", 1.334 + fileName : "extensions0png1.png", 1.335 + relPathDir : "a/b/distribution/extensions/extensions0/", 1.336 + originalContents : null, 1.337 + compareContents : null, 1.338 + originalFile : null, 1.339 + compareFile : "complete.png", 1.340 + originalPerms : null, 1.341 + comparePerms : 0o644 1.342 +}, { 1.343 + description : "Added by update.manifest if the parent directory " + 1.344 + "exists (add-if)", 1.345 + fileName : "extensions0png0.png", 1.346 + relPathDir : "a/b/distribution/extensions/extensions0/", 1.347 + originalContents : null, 1.348 + compareContents : null, 1.349 + originalFile : null, 1.350 + compareFile : "complete.png", 1.351 + originalPerms : null, 1.352 + comparePerms : 0o644 1.353 +}, { 1.354 + description : "Added by update.manifest (add)", 1.355 + fileName : "exe0.exe", 1.356 + relPathDir : "a/b/", 1.357 + originalContents : null, 1.358 + compareContents : null, 1.359 + originalFile : FILE_HELPER_BIN, 1.360 + compareFile : FILE_COMPLETE_EXE, 1.361 + originalPerms : 0o777, 1.362 + comparePerms : 0o755 1.363 +}, { 1.364 + description : "Added by update.manifest (add)", 1.365 + fileName : "10text0", 1.366 + relPathDir : "a/b/1/10/", 1.367 + originalContents : "ToBeReplacedWithFromComplete\n", 1.368 + compareContents : "FromComplete\n", 1.369 + originalFile : null, 1.370 + compareFile : null, 1.371 + originalPerms : 0o767, 1.372 + comparePerms : 0o644 1.373 +}, { 1.374 + description : "Added by update.manifest (add)", 1.375 + fileName : "0exe0.exe", 1.376 + relPathDir : "a/b/0/", 1.377 + originalContents : null, 1.378 + compareContents : null, 1.379 + originalFile : FILE_HELPER_BIN, 1.380 + compareFile : FILE_COMPLETE_EXE, 1.381 + originalPerms : 0o777, 1.382 + comparePerms : 0o755 1.383 +}, { 1.384 + description : "Added by update.manifest (add)", 1.385 + fileName : "00text1", 1.386 + relPathDir : "a/b/0/00/", 1.387 + originalContents : "ToBeReplacedWithFromComplete\n", 1.388 + compareContents : "FromComplete\n", 1.389 + originalFile : null, 1.390 + compareFile : null, 1.391 + originalPerms : 0o677, 1.392 + comparePerms : 0o644 1.393 +}, { 1.394 + description : "Added by update.manifest (add)", 1.395 + fileName : "00text0", 1.396 + relPathDir : "a/b/0/00/", 1.397 + originalContents : "ToBeReplacedWithFromComplete\n", 1.398 + compareContents : "FromComplete\n", 1.399 + originalFile : null, 1.400 + compareFile : null, 1.401 + originalPerms : 0o775, 1.402 + comparePerms : 0o644 1.403 +}, { 1.404 + description : "Added by update.manifest (add)", 1.405 + fileName : "00png0.png", 1.406 + relPathDir : "a/b/0/00/", 1.407 + originalContents : null, 1.408 + compareContents : null, 1.409 + originalFile : null, 1.410 + compareFile : "complete.png", 1.411 + originalPerms : 0o776, 1.412 + comparePerms : 0o644 1.413 +}, { 1.414 + description : "Removed by precomplete (remove)", 1.415 + fileName : "20text0", 1.416 + relPathDir : "a/b/2/20/", 1.417 + originalContents : "ToBeDeleted\n", 1.418 + compareContents : null, 1.419 + originalFile : null, 1.420 + compareFile : null, 1.421 + originalPerms : null, 1.422 + comparePerms : null 1.423 +}, { 1.424 + description : "Removed by precomplete (remove)", 1.425 + fileName : "20png0.png", 1.426 + relPathDir : "a/b/2/20/", 1.427 + originalContents : "ToBeDeleted\n", 1.428 + compareContents : null, 1.429 + originalFile : null, 1.430 + compareFile : null, 1.431 + originalPerms : null, 1.432 + comparePerms : null 1.433 +}]; 1.434 + 1.435 +// Concatenate the common files to the end of the array. 1.436 +gTestFilesCompleteSuccess = gTestFilesCompleteSuccess.concat(gTestFilesCommon); 1.437 + 1.438 +// Files for a partial successful update. This can be used for a partial failed 1.439 +// update by calling setTestFilesAndDirsForFailure. 1.440 +var gTestFilesPartialSuccess = [ 1.441 +{ 1.442 + description : "Added by update.manifest (add)", 1.443 + fileName : "precomplete", 1.444 + relPathDir : "", 1.445 + originalContents : null, 1.446 + compareContents : null, 1.447 + originalFile : "complete_precomplete", 1.448 + compareFile : "partial_precomplete", 1.449 + originalPerms : 0o666, 1.450 + comparePerms : 0o644 1.451 +}, { 1.452 + description : "Added by update.manifest (add)", 1.453 + fileName : "searchpluginstext0", 1.454 + relPathDir : "a/b/searchplugins/", 1.455 + originalContents : "ToBeReplacedWithFromPartial\n", 1.456 + compareContents : "FromPartial\n", 1.457 + originalFile : null, 1.458 + compareFile : null, 1.459 + originalPerms : 0o775, 1.460 + comparePerms : 0o644 1.461 +}, { 1.462 + description : "Patched by update.manifest if the file exists " + 1.463 + "(patch-if)", 1.464 + fileName : "searchpluginspng1.png", 1.465 + relPathDir : "a/b/searchplugins/", 1.466 + originalContents : null, 1.467 + compareContents : null, 1.468 + originalFile : "complete.png", 1.469 + compareFile : "partial.png", 1.470 + originalPerms : 0o666, 1.471 + comparePerms : 0o666 1.472 +}, { 1.473 + description : "Patched by update.manifest if the file exists " + 1.474 + "(patch-if)", 1.475 + fileName : "searchpluginspng0.png", 1.476 + relPathDir : "a/b/searchplugins/", 1.477 + originalContents : null, 1.478 + compareContents : null, 1.479 + originalFile : "complete.png", 1.480 + compareFile : "partial.png", 1.481 + originalPerms : 0o666, 1.482 + comparePerms : 0o666 1.483 +}, { 1.484 + description : "Added by update.manifest if the parent directory " + 1.485 + "exists (add-if)", 1.486 + fileName : "extensions1text0", 1.487 + relPathDir : "a/b/distribution/extensions/extensions1/", 1.488 + originalContents : null, 1.489 + compareContents : "FromPartial\n", 1.490 + originalFile : null, 1.491 + compareFile : null, 1.492 + originalPerms : null, 1.493 + comparePerms : 0o644 1.494 +}, { 1.495 + description : "Patched by update.manifest if the parent directory " + 1.496 + "exists (patch-if)", 1.497 + fileName : "extensions1png1.png", 1.498 + relPathDir : "a/b/distribution/extensions/extensions1/", 1.499 + originalContents : null, 1.500 + compareContents : null, 1.501 + originalFile : "complete.png", 1.502 + compareFile : "partial.png", 1.503 + originalPerms : 0o666, 1.504 + comparePerms : 0o666 1.505 +}, { 1.506 + description : "Patched by update.manifest if the parent directory " + 1.507 + "exists (patch-if)", 1.508 + fileName : "extensions1png0.png", 1.509 + relPathDir : "a/b/distribution/extensions/extensions1/", 1.510 + originalContents : null, 1.511 + compareContents : null, 1.512 + originalFile : "complete.png", 1.513 + compareFile : "partial.png", 1.514 + originalPerms : 0o666, 1.515 + comparePerms : 0o666 1.516 +}, { 1.517 + description : "Added by update.manifest if the parent directory " + 1.518 + "exists (add-if)", 1.519 + fileName : "extensions0text0", 1.520 + relPathDir : "a/b/distribution/extensions/extensions0/", 1.521 + originalContents : "ToBeReplacedWithFromPartial\n", 1.522 + compareContents : "FromPartial\n", 1.523 + originalFile : null, 1.524 + compareFile : null, 1.525 + originalPerms : 0o644, 1.526 + comparePerms : 0o644 1.527 +}, { 1.528 + description : "Patched by update.manifest if the parent directory " + 1.529 + "exists (patch-if)", 1.530 + fileName : "extensions0png1.png", 1.531 + relPathDir : "a/b/distribution/extensions/extensions0/", 1.532 + originalContents : null, 1.533 + compareContents : null, 1.534 + originalFile : "complete.png", 1.535 + compareFile : "partial.png", 1.536 + originalPerms : 0o644, 1.537 + comparePerms : 0o644 1.538 +}, { 1.539 + description : "Patched by update.manifest if the parent directory " + 1.540 + "exists (patch-if)", 1.541 + fileName : "extensions0png0.png", 1.542 + relPathDir : "a/b/distribution/extensions/extensions0/", 1.543 + originalContents : null, 1.544 + compareContents : null, 1.545 + originalFile : "complete.png", 1.546 + compareFile : "partial.png", 1.547 + originalPerms : 0o644, 1.548 + comparePerms : 0o644 1.549 +}, { 1.550 + description : "Patched by update.manifest (patch)", 1.551 + fileName : "exe0.exe", 1.552 + relPathDir : "a/b/", 1.553 + originalContents : null, 1.554 + compareContents : null, 1.555 + originalFile : FILE_COMPLETE_EXE, 1.556 + compareFile : FILE_PARTIAL_EXE, 1.557 + originalPerms : 0o755, 1.558 + comparePerms : 0o755 1.559 +}, { 1.560 + description : "Patched by update.manifest (patch)", 1.561 + fileName : "0exe0.exe", 1.562 + relPathDir : "a/b/0/", 1.563 + originalContents : null, 1.564 + compareContents : null, 1.565 + originalFile : FILE_COMPLETE_EXE, 1.566 + compareFile : FILE_PARTIAL_EXE, 1.567 + originalPerms : 0o755, 1.568 + comparePerms : 0o755 1.569 +}, { 1.570 + description : "Added by update.manifest (add)", 1.571 + fileName : "00text0", 1.572 + relPathDir : "a/b/0/00/", 1.573 + originalContents : "ToBeReplacedWithFromPartial\n", 1.574 + compareContents : "FromPartial\n", 1.575 + originalFile : null, 1.576 + compareFile : null, 1.577 + originalPerms : 0o644, 1.578 + comparePerms : 0o644 1.579 +}, { 1.580 + description : "Patched by update.manifest (patch)", 1.581 + fileName : "00png0.png", 1.582 + relPathDir : "a/b/0/00/", 1.583 + originalContents : null, 1.584 + compareContents : null, 1.585 + originalFile : "complete.png", 1.586 + compareFile : "partial.png", 1.587 + originalPerms : 0o666, 1.588 + comparePerms : 0o666 1.589 +}, { 1.590 + description : "Added by update.manifest (add)", 1.591 + fileName : "20text0", 1.592 + relPathDir : "a/b/2/20/", 1.593 + originalContents : null, 1.594 + compareContents : "FromPartial\n", 1.595 + originalFile : null, 1.596 + compareFile : null, 1.597 + originalPerms : null, 1.598 + comparePerms : 0o644 1.599 +}, { 1.600 + description : "Added by update.manifest (add)", 1.601 + fileName : "20png0.png", 1.602 + relPathDir : "a/b/2/20/", 1.603 + originalContents : null, 1.604 + compareContents : null, 1.605 + originalFile : null, 1.606 + compareFile : "partial.png", 1.607 + originalPerms : null, 1.608 + comparePerms : 0o644 1.609 +}, { 1.610 + description : "Added by update.manifest (add)", 1.611 + fileName : "00text2", 1.612 + relPathDir : "a/b/0/00/", 1.613 + originalContents : null, 1.614 + compareContents : "FromPartial\n", 1.615 + originalFile : null, 1.616 + compareFile : null, 1.617 + originalPerms : null, 1.618 + comparePerms : 0o644 1.619 +}, { 1.620 + description : "Removed by update.manifest (remove)", 1.621 + fileName : "10text0", 1.622 + relPathDir : "a/b/1/10/", 1.623 + originalContents : "ToBeDeleted\n", 1.624 + compareContents : null, 1.625 + originalFile : null, 1.626 + compareFile : null, 1.627 + originalPerms : null, 1.628 + comparePerms : null 1.629 +}, { 1.630 + description : "Removed by update.manifest (remove)", 1.631 + fileName : "00text1", 1.632 + relPathDir : "a/b/0/00/", 1.633 + originalContents : "ToBeDeleted\n", 1.634 + compareContents : null, 1.635 + originalFile : null, 1.636 + compareFile : null, 1.637 + originalPerms : null, 1.638 + comparePerms : null 1.639 +}]; 1.640 + 1.641 +// Concatenate the common files to the end of the array. 1.642 +gTestFilesPartialSuccess = gTestFilesPartialSuccess.concat(gTestFilesCommon); 1.643 + 1.644 +/** 1.645 + * The mar files used for the updater tests contain the following remove 1.646 + * operations. 1.647 + * 1.648 + * partial and complete test mar remove operations 1.649 + * ----------------------------------------------- 1.650 + * remove "text1" 1.651 + * remove "text0" 1.652 + * rmrfdir "9/99/" 1.653 + * rmdir "9/99/" 1.654 + * rmrfdir "9/98/" 1.655 + * rmrfdir "9/97/" 1.656 + * rmrfdir "9/96/" 1.657 + * rmrfdir "9/95/" 1.658 + * rmrfdir "9/95/" 1.659 + * rmrfdir "9/94/" 1.660 + * rmdir "9/94/" 1.661 + * rmdir "9/93/" 1.662 + * rmdir "9/92/" 1.663 + * rmdir "9/91/" 1.664 + * rmdir "9/90/" 1.665 + * rmdir "9/90/" 1.666 + * rmrfdir "8/89/" 1.667 + * rmdir "8/89/" 1.668 + * rmrfdir "8/88/" 1.669 + * rmrfdir "8/87/" 1.670 + * rmrfdir "8/86/" 1.671 + * rmrfdir "8/85/" 1.672 + * rmrfdir "8/85/" 1.673 + * rmrfdir "8/84/" 1.674 + * rmdir "8/84/" 1.675 + * rmdir "8/83/" 1.676 + * rmdir "8/82/" 1.677 + * rmdir "8/81/" 1.678 + * rmdir "8/80/" 1.679 + * rmdir "8/80/" 1.680 + * rmrfdir "7/" 1.681 + * rmdir "6/" 1.682 + * remove "5/text1" 1.683 + * remove "5/text0" 1.684 + * rmrfdir "5/" 1.685 + * remove "4/text1" 1.686 + * remove "4/text0" 1.687 + * remove "4/exe0.exe" 1.688 + * rmdir "4/" 1.689 + * remove "3/text1" 1.690 + * remove "3/text0" 1.691 + * 1.692 + * partial test mar additional remove operations 1.693 + * --------------------------------------------- 1.694 + * remove "0/00/00text1" 1.695 + * remove "1/10/10text0" 1.696 + * rmdir "1/10/" 1.697 + * rmdir "1/" 1.698 + */ 1.699 +var gTestDirsCommon = [ 1.700 +{ 1.701 + relPathDir : "a/b/3/", 1.702 + dirRemoved : false, 1.703 + files : ["3text0", "3text1"], 1.704 + filesRemoved : true 1.705 +}, { 1.706 + relPathDir : "a/b/4/", 1.707 + dirRemoved : true, 1.708 + files : ["4text0", "4text1"], 1.709 + filesRemoved : true 1.710 +}, { 1.711 + relPathDir : "a/b/5/", 1.712 + dirRemoved : true, 1.713 + files : ["5test.exe", "5text0", "5text1"], 1.714 + filesRemoved : true 1.715 +}, { 1.716 + relPathDir : "a/b/6/", 1.717 + dirRemoved : true 1.718 +}, { 1.719 + relPathDir : "a/b/7/", 1.720 + dirRemoved : true, 1.721 + files : ["7text0", "7text1"], 1.722 + subDirs : ["70/", "71/"], 1.723 + subDirFiles : ["7xtest.exe", "7xtext0", "7xtext1"] 1.724 +}, { 1.725 + relPathDir : "a/b/8/", 1.726 + dirRemoved : false 1.727 +}, { 1.728 + relPathDir : "a/b/8/80/", 1.729 + dirRemoved : true 1.730 +}, { 1.731 + relPathDir : "a/b/8/81/", 1.732 + dirRemoved : false, 1.733 + files : ["81text0", "81text1"] 1.734 +}, { 1.735 + relPathDir : "a/b/8/82/", 1.736 + dirRemoved : false, 1.737 + subDirs : ["820/", "821/"] 1.738 +}, { 1.739 + relPathDir : "a/b/8/83/", 1.740 + dirRemoved : true 1.741 +}, { 1.742 + relPathDir : "a/b/8/84/", 1.743 + dirRemoved : true 1.744 +}, { 1.745 + relPathDir : "a/b/8/85/", 1.746 + dirRemoved : true 1.747 +}, { 1.748 + relPathDir : "a/b/8/86/", 1.749 + dirRemoved : true, 1.750 + files : ["86text0", "86text1"] 1.751 +}, { 1.752 + relPathDir : "a/b/8/87/", 1.753 + dirRemoved : true, 1.754 + subDirs : ["870/", "871/"], 1.755 + subDirFiles : ["87xtext0", "87xtext1"] 1.756 +}, { 1.757 + relPathDir : "a/b/8/88/", 1.758 + dirRemoved : true 1.759 +}, { 1.760 + relPathDir : "a/b/8/89/", 1.761 + dirRemoved : true 1.762 +}, { 1.763 + relPathDir : "a/b/9/90/", 1.764 + dirRemoved : true 1.765 +}, { 1.766 + relPathDir : "a/b/9/91/", 1.767 + dirRemoved : false, 1.768 + files : ["91text0", "91text1"] 1.769 +}, { 1.770 + relPathDir : "a/b/9/92/", 1.771 + dirRemoved : false, 1.772 + subDirs : ["920/", "921/"] 1.773 +}, { 1.774 + relPathDir : "a/b/9/93/", 1.775 + dirRemoved : true 1.776 +}, { 1.777 + relPathDir : "a/b/9/94/", 1.778 + dirRemoved : true 1.779 +}, { 1.780 + relPathDir : "a/b/9/95/", 1.781 + dirRemoved : true 1.782 +}, { 1.783 + relPathDir : "a/b/9/96/", 1.784 + dirRemoved : true, 1.785 + files : ["96text0", "96text1"] 1.786 +}, { 1.787 + relPathDir : "a/b/9/97/", 1.788 + dirRemoved : true, 1.789 + subDirs : ["970/", "971/"], 1.790 + subDirFiles : ["97xtext0", "97xtext1"] 1.791 +}, { 1.792 + relPathDir : "a/b/9/98/", 1.793 + dirRemoved : true 1.794 +}, { 1.795 + relPathDir : "a/b/9/99/", 1.796 + dirRemoved : true 1.797 +}]; 1.798 + 1.799 +// Directories for a complete successful update. This array can be used for a 1.800 +// complete failed update by calling setTestFilesAndDirsForFailure. 1.801 +var gTestDirsCompleteSuccess = [ 1.802 +{ 1.803 + description : "Removed by precomplete (rmdir)", 1.804 + relPathDir : "a/b/2/20/", 1.805 + dirRemoved : true 1.806 +}, { 1.807 + description : "Removed by precomplete (rmdir)", 1.808 + relPathDir : "a/b/2/", 1.809 + dirRemoved : true 1.810 +}]; 1.811 + 1.812 +// Concatenate the common files to the beginning of the array. 1.813 +gTestDirsCompleteSuccess = gTestDirsCommon.concat(gTestDirsCompleteSuccess); 1.814 + 1.815 +// Directories for a partial successful update. This array can be used for a 1.816 +// partial failed update by calling setTestFilesAndDirsForFailure. 1.817 +var gTestDirsPartialSuccess = [ 1.818 +{ 1.819 + description : "Removed by update.manifest (rmdir)", 1.820 + relPathDir : "a/b/1/10/", 1.821 + dirRemoved : true 1.822 +}, { 1.823 + description : "Removed by update.manifest (rmdir)", 1.824 + relPathDir : "a/b/1/", 1.825 + dirRemoved : true 1.826 +}]; 1.827 + 1.828 +// Concatenate the common files to the beginning of the array. 1.829 +gTestDirsPartialSuccess = gTestDirsCommon.concat(gTestDirsPartialSuccess); 1.830 + 1.831 +// Extra directories to check for existence for both complete and partial 1.832 +// updates. Whether they exist or not is set when calling setupUpdaterTest. 1.833 +var gTestExtraDirs = [ 1.834 +{ 1.835 + relPathDir : DIR_UPDATED, 1.836 + dirExists : false 1.837 +}, { 1.838 + relPathDir : DIR_TOBEDELETED, 1.839 + dirExists : false 1.840 +}]; 1.841 + 1.842 +// This makes it possible to run most tests on xulrunner where the update 1.843 +// channel default preference is not set. 1.844 +if (MOZ_APP_NAME == "xulrunner") { 1.845 + try { 1.846 + gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL); 1.847 + } catch (e) { 1.848 + setUpdateChannel("test_channel"); 1.849 + } 1.850 +} 1.851 + 1.852 +/** 1.853 + * Helper function for setting up the test environment. 1.854 + */ 1.855 +function setupTestCommon() { 1.856 + logTestInfo("start - general test setup"); 1.857 + 1.858 + do_test_pending(); 1.859 + 1.860 + if (gTestID) { 1.861 + do_throw("setupTestCommon should only be called once!"); 1.862 + } 1.863 + 1.864 + let caller = Components.stack.caller; 1.865 + gTestID = caller.filename.toString().split("/").pop().split(".")[0]; 1.866 + 1.867 + if (DEBUG_TEST_LOG) { 1.868 + let logFile = do_get_file(gTestID + ".log", true); 1.869 + if (logFile.exists()) { 1.870 + gPassed = false; 1.871 + logTestInfo("start - dumping previous test run log"); 1.872 + logTestInfo("\n" + readFile(logFile) + "\n"); 1.873 + logTestInfo("finish - dumping previous test run log"); 1.874 + if (gDeleteLogFile) { 1.875 + logFile.remove(false); 1.876 + } 1.877 + do_throw("The parallel run of this test failed. Failing non-parallel " + 1.878 + "test so the log from the parallel run can be displayed in " + 1.879 + "non-parallel log.") 1.880 + } else { 1.881 + gRealDump = dump; 1.882 + dump = dumpOverride; 1.883 + } 1.884 + } 1.885 + 1.886 + // Don't attempt to show a prompt when an update finishes. 1.887 + Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, true); 1.888 + 1.889 + gGREDirOrig = getGREDir(); 1.890 + gAppDirOrig = getAppBaseDir(); 1.891 + 1.892 + let applyDir = getApplyDirFile(null, true).parent; 1.893 + 1.894 + // Try to remove the directory used to apply updates and the updates directory 1.895 + // on platforms other than Windows. Since the test hasn't ran yet and the 1.896 + // directory shouldn't exist finished this is non-fatal for the test. 1.897 + if (applyDir.exists()) { 1.898 + logTestInfo("attempting to remove directory. Path: " + applyDir.path); 1.899 + try { 1.900 + removeDirRecursive(applyDir); 1.901 + } catch (e) { 1.902 + logTestInfo("non-fatal error removing directory. Path: " + 1.903 + applyDir.path + ", Exception: " + e); 1.904 + } 1.905 + } 1.906 + 1.907 + // adjustGeneralPaths registers a cleanup function that calls end_test when 1.908 + // it is defined as a function. 1.909 + adjustGeneralPaths(); 1.910 + 1.911 + // Remove the updates directory on Windows which is located outside of the 1.912 + // application directory after the call to adjustGeneralPaths has set it up. 1.913 + // Since the test hasn't ran yet and the directory shouldn't exist finished 1.914 + // this is non-fatal for the test. 1.915 + if (IS_WIN) { 1.916 + let updatesDir = getMockUpdRootD(); 1.917 + if (updatesDir.exists()) { 1.918 + logTestInfo("attempting to remove directory. Path: " + updatesDir.path); 1.919 + try { 1.920 + removeDirRecursive(updatesDir); 1.921 + } catch (e) { 1.922 + logTestInfo("non-fatal error removing directory. Path: " + 1.923 + updatesDir.path + ", Exception: " + e); 1.924 + } 1.925 + } 1.926 + } 1.927 + 1.928 + logTestInfo("finish - general test setup"); 1.929 +} 1.930 + 1.931 +/** 1.932 + * Nulls out the most commonly used global vars used by tests to prevent leaks 1.933 + * as needed and attempts to restore the system to its original state. 1.934 + */ 1.935 +function cleanupTestCommon() { 1.936 + logTestInfo("start - general test cleanup"); 1.937 + 1.938 + // Force the update manager to reload the update data to prevent it from 1.939 + // writing the old data to the files that have just been removed. 1.940 + reloadUpdateManagerData(); 1.941 + 1.942 + if (gChannel) { 1.943 + gPrefRoot.removeObserver(PREF_APP_UPDATE_CHANNEL, observer); 1.944 + } 1.945 + 1.946 + // Call app update's observe method passing xpcom-shutdown to test that the 1.947 + // shutdown of app update runs without throwing or leaking. The observer 1.948 + // method is used directly instead of calling notifyObservers so components 1.949 + // outside of the scope of this test don't assert and thereby cause app update 1.950 + // tests to fail. 1.951 + gAUS.observe(null, "xpcom-shutdown", ""); 1.952 + 1.953 + if (gXHR) { 1.954 + gXHRCallback = null; 1.955 + 1.956 + gXHR.responseXML = null; 1.957 + // null out the event handlers to prevent a mFreeCount leak of 1 1.958 + gXHR.onerror = null; 1.959 + gXHR.onload = null; 1.960 + gXHR.onprogress = null; 1.961 + 1.962 + gXHR = null; 1.963 + } 1.964 + 1.965 + gTestserver = null; 1.966 + 1.967 + if (IS_UNIX) { 1.968 + // This will delete the launch script if it exists. 1.969 + getLaunchScript(); 1.970 + } 1.971 + 1.972 + if (IS_WIN && MOZ_APP_BASENAME) { 1.973 + let appDir = getApplyDirFile(null, true); 1.974 + let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla"; 1.975 + const REG_PATH = "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME + 1.976 + "\\TaskBarIDs"; 1.977 + let key = AUS_Cc["@mozilla.org/windows-registry-key;1"]. 1.978 + createInstance(AUS_Ci.nsIWindowsRegKey); 1.979 + try { 1.980 + key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH, 1.981 + AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); 1.982 + if (key.hasValue(appDir.path)) { 1.983 + key.removeValue(appDir.path); 1.984 + } 1.985 + } catch (e) { 1.986 + } 1.987 + try { 1.988 + key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, REG_PATH, 1.989 + AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); 1.990 + if (key.hasValue(appDir.path)) { 1.991 + key.removeValue(appDir.path); 1.992 + } 1.993 + } catch (e) { 1.994 + } 1.995 + } 1.996 + 1.997 + // The updates directory is located outside of the application directory on 1.998 + // Windows so it also needs to be removed. 1.999 + if (IS_WIN) { 1.1000 + let updatesDir = getMockUpdRootD(); 1.1001 + // Try to remove the directory used to apply updates. Since the test has 1.1002 + // already finished this is non-fatal for the test. 1.1003 + if (updatesDir.exists()) { 1.1004 + logTestInfo("attempting to remove directory. Path: " + updatesDir.path); 1.1005 + try { 1.1006 + removeDirRecursive(updatesDir); 1.1007 + } catch (e) { 1.1008 + logTestInfo("non-fatal error removing directory. Path: " + 1.1009 + updatesDir.path + ", Exception: " + e); 1.1010 + } 1.1011 + } 1.1012 + } 1.1013 + 1.1014 + let applyDir = getApplyDirFile(null, true).parent; 1.1015 + 1.1016 + // Try to remove the directory used to apply updates. Since the test has 1.1017 + // already finished this is non-fatal for the test. 1.1018 + if (applyDir.exists()) { 1.1019 + logTestInfo("attempting to remove directory. Path: " + applyDir.path); 1.1020 + try { 1.1021 + removeDirRecursive(applyDir); 1.1022 + } catch (e) { 1.1023 + logTestInfo("non-fatal error removing directory. Path: " + 1.1024 + applyDir.path + ", Exception: " + e); 1.1025 + } 1.1026 + } 1.1027 + 1.1028 + resetEnvironment(); 1.1029 + 1.1030 + logTestInfo("finish - general test cleanup"); 1.1031 + 1.1032 + if (gRealDump) { 1.1033 + dump = gRealDump; 1.1034 + gRealDump = null; 1.1035 + } 1.1036 + 1.1037 + if (DEBUG_TEST_LOG && !gPassed) { 1.1038 + let fos = AUS_Cc["@mozilla.org/network/file-output-stream;1"]. 1.1039 + createInstance(AUS_Ci.nsIFileOutputStream); 1.1040 + let logFile = do_get_file(gTestID + ".log", true); 1.1041 + if (!logFile.exists()) { 1.1042 + logFile.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE); 1.1043 + } 1.1044 + fos.init(logFile, MODE_WRONLY | MODE_CREATE | MODE_APPEND, PERMS_FILE, 0); 1.1045 + fos.write(gTestLogText, gTestLogText.length); 1.1046 + fos.close(); 1.1047 + } 1.1048 + 1.1049 + if (DEBUG_TEST_LOG) { 1.1050 + gTestLogText = null; 1.1051 + } else { 1.1052 + let logFile = do_get_file(gTestID + ".log", true); 1.1053 + if (logFile.exists()) { 1.1054 + logFile.remove(false); 1.1055 + } 1.1056 + } 1.1057 +} 1.1058 + 1.1059 +/** 1.1060 + * Helper function to store the log output of calls to dump in a variable so the 1.1061 + * values can be written to a file for a parallel run of a test and printed to 1.1062 + * the log file when the test runs synchronously. 1.1063 + */ 1.1064 +function dumpOverride(aText) { 1.1065 + gTestLogText += aText; 1.1066 + gRealDump(aText); 1.1067 +} 1.1068 + 1.1069 +/** 1.1070 + * Helper function that calls do_test_finished that tracks whether a parallel 1.1071 + * run of a test passed when it runs synchronously so the log output can be 1.1072 + * inspected. 1.1073 + */ 1.1074 +function doTestFinish() { 1.1075 + if (gPassed === undefined) { 1.1076 + gPassed = true; 1.1077 + } 1.1078 + do_test_finished(); 1.1079 +} 1.1080 + 1.1081 +/** 1.1082 + * Sets the most commonly used preferences used by tests 1.1083 + */ 1.1084 +function setDefaultPrefs() { 1.1085 + Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true); 1.1086 + Services.prefs.setBoolPref(PREF_APP_UPDATE_METRO_ENABLED, true); 1.1087 + // Don't display UI for a successful installation. Some apps may not set this 1.1088 + // pref to false like Firefox does. 1.1089 + Services.prefs.setBoolPref(PREF_APP_UPDATE_SHOW_INSTALLED_UI, false); 1.1090 + // Enable Update logging 1.1091 + Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true); 1.1092 +} 1.1093 + 1.1094 +/** 1.1095 + * Helper function for updater binary tests that sets the appropriate values 1.1096 + * to check for update failures. 1.1097 + */ 1.1098 +function setTestFilesAndDirsForFailure() { 1.1099 + gTestFiles.forEach(function STFADFF_Files(aTestFile) { 1.1100 + aTestFile.compareContents = aTestFile.originalContents; 1.1101 + aTestFile.compareFile = aTestFile.originalFile; 1.1102 + aTestFile.comparePerms = aTestFile.originalPerms; 1.1103 + }); 1.1104 + 1.1105 + gTestDirs.forEach(function STFADFF_Dirs(aTestDir) { 1.1106 + aTestDir.dirRemoved = false; 1.1107 + if (aTestDir.filesRemoved) { 1.1108 + aTestDir.filesRemoved = false; 1.1109 + } 1.1110 + }); 1.1111 +} 1.1112 + 1.1113 +/** 1.1114 + * Initializes the most commonly used settings and creates an instance of the 1.1115 + * update service stub. 1.1116 + */ 1.1117 +function standardInit() { 1.1118 + createAppInfo("xpcshell@tests.mozilla.org", APP_INFO_NAME, "1.0", "2.0"); 1.1119 + setDefaultPrefs(); 1.1120 + // Initialize the update service stub component 1.1121 + initUpdateServiceStub(); 1.1122 +} 1.1123 + 1.1124 +/** 1.1125 + * Custom path handler for the http server 1.1126 + * 1.1127 + * @param aMetadata 1.1128 + * The http metadata for the request. 1.1129 + * @param aResponse 1.1130 + * The http response for the request. 1.1131 + */ 1.1132 +function pathHandler(aMetadata, aResponse) { 1.1133 + aResponse.setHeader("Content-Type", "text/xml", false); 1.1134 + aResponse.setStatusLine(aMetadata.httpVersion, gResponseStatusCode, "OK"); 1.1135 + aResponse.bodyOutputStream.write(gResponseBody, gResponseBody.length); 1.1136 +} 1.1137 + 1.1138 +/** 1.1139 + * Helper function for getting the application version from the application.ini 1.1140 + * file. This will look in both the GRE and the application directories for the 1.1141 + * application.ini file. 1.1142 + * 1.1143 + * @return The version string from the application.ini file. 1.1144 + * @throws If the application.ini file is not found. 1.1145 + */ 1.1146 +function getAppVersion() { 1.1147 + // Read the application.ini and use its application version. 1.1148 + let iniFile = gGREDirOrig.clone(); 1.1149 + iniFile.append("application.ini"); 1.1150 + if (!iniFile.exists()) { 1.1151 + iniFile = gAppDirOrig.clone(); 1.1152 + iniFile.append("application.ini"); 1.1153 + } 1.1154 + if (!iniFile.exists()) { 1.1155 + do_throw("Unable to find application.ini!"); 1.1156 + } 1.1157 + let iniParser = AUS_Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. 1.1158 + getService(AUS_Ci.nsIINIParserFactory). 1.1159 + createINIParser(iniFile); 1.1160 + return iniParser.getString("App", "Version"); 1.1161 +} 1.1162 + 1.1163 +/** 1.1164 + * Helper function for getting the relative path to the directory where the 1.1165 + * application binary is located (e.g. <test_file_leafname>/dir.app/). 1.1166 + * 1.1167 + * Note: The dir.app subdirectory under <test_file_leafname> is needed for 1.1168 + * platforms other than Mac OS X so the tests can run in parallel due to 1.1169 + * update staging creating a lock file named moz_update_in_progress.lock in 1.1170 + * the parent directory of the installation directory. 1.1171 + * 1.1172 + * @return The relative path to the directory where application binary is 1.1173 + * located. 1.1174 + */ 1.1175 +function getApplyDirPath() { 1.1176 + return gTestID + "/dir.app/"; 1.1177 +} 1.1178 + 1.1179 +/** 1.1180 + * Helper function for getting the nsIFile for a file in the directory where the 1.1181 + * update will be applied. 1.1182 + * 1.1183 + * The files for the update are located two directories below the apply to 1.1184 + * directory since Mac OS X sets the last modified time for the root directory 1.1185 + * to the current time and if the update changes any files in the root directory 1.1186 + * then it wouldn't be possible to test (bug 600098). 1.1187 + * 1.1188 + * @param aRelPath (optional) 1.1189 + * The relative path to the file or directory to get from the root of 1.1190 + * the test's directory. If not specified the test's directory will be 1.1191 + * returned. 1.1192 + * @param aAllowNonexistent (optional) 1.1193 + * Whether the file must exist. If false or not specified the file must 1.1194 + * exist or the function will throw. 1.1195 + * @return The nsIFile for the file in the directory where the update will be 1.1196 + * applied. 1.1197 + */ 1.1198 +function getApplyDirFile(aRelPath, aAllowNonexistent) { 1.1199 + let relpath = getApplyDirPath() + (aRelPath ? aRelPath : ""); 1.1200 + return do_get_file(relpath, aAllowNonexistent); 1.1201 +} 1.1202 + 1.1203 +/** 1.1204 + * Helper function for getting the relative path to the directory where the 1.1205 + * test data files are located. 1.1206 + * 1.1207 + * @return The relative path to the directory where the test data files are 1.1208 + * located. 1.1209 + */ 1.1210 +function getTestDirPath() { 1.1211 + return "../data/"; 1.1212 +} 1.1213 + 1.1214 +/** 1.1215 + * Helper function for getting the nsIFile for a file in the test data 1.1216 + * directory. 1.1217 + * 1.1218 + * @param aRelPath (optional) 1.1219 + * The relative path to the file or directory to get from the root of 1.1220 + * the test's data directory. If not specified the test's data 1.1221 + * directory will be returned. 1.1222 + * @return The nsIFile for the file in the test data directory. 1.1223 + * @throws If the file or directory does not exist. 1.1224 + */ 1.1225 +function getTestDirFile(aRelPath) { 1.1226 + let relpath = getTestDirPath() + (aRelPath ? aRelPath : ""); 1.1227 + return do_get_file(relpath, false); 1.1228 +} 1.1229 + 1.1230 +/** 1.1231 + * Helper function for getting the directory that was updated. This can either 1.1232 + * be the directory where the application binary is located or the directory 1.1233 + * that contains the staged update. 1.1234 + */ 1.1235 +function getUpdatedDirPath() { 1.1236 + return getApplyDirPath() + (gStageUpdate ? DIR_UPDATED + "/" : ""); 1.1237 +} 1.1238 + 1.1239 +#ifdef XP_WIN 1.1240 +XPCOMUtils.defineLazyGetter(this, "gInstallDirPathHash", 1.1241 + function test_gInstallDirPathHash() { 1.1242 + // Figure out where we should check for a cached hash value 1.1243 + if (!MOZ_APP_BASENAME) 1.1244 + return null; 1.1245 + 1.1246 + let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla"; 1.1247 + let appDir = getApplyDirFile(null, true); 1.1248 + 1.1249 + const REG_PATH = "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME + 1.1250 + "\\TaskBarIDs"; 1.1251 + let regKey = AUS_Cc["@mozilla.org/windows-registry-key;1"]. 1.1252 + createInstance(AUS_Ci.nsIWindowsRegKey); 1.1253 + try { 1.1254 + regKey.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH, 1.1255 + AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); 1.1256 + regKey.writeStringValue(appDir.path, gTestID); 1.1257 + return gTestID; 1.1258 + } catch (e) { 1.1259 + } 1.1260 + 1.1261 + try { 1.1262 + regKey.create(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, REG_PATH, 1.1263 + AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); 1.1264 + regKey.writeStringValue(appDir.path, gTestID); 1.1265 + return gTestID; 1.1266 + } catch (e) { 1.1267 + logTestInfo("failed to create registry key. Registry Path: " + REG_PATH + 1.1268 + ", Key Name: " + appDir.path + ", Key Value: " + gTestID + 1.1269 + ", Exception " + e); 1.1270 + } 1.1271 + return null; 1.1272 +}); 1.1273 + 1.1274 +XPCOMUtils.defineLazyGetter(this, "gLocalAppDataDir", 1.1275 + function test_gLocalAppDataDir() { 1.1276 + const CSIDL_LOCAL_APPDATA = 0x1c; 1.1277 + 1.1278 + AUS_Cu.import("resource://gre/modules/ctypes.jsm"); 1.1279 + let lib = ctypes.open("shell32"); 1.1280 + let SHGetSpecialFolderPath = lib.declare("SHGetSpecialFolderPathW", 1.1281 + ctypes.winapi_abi, 1.1282 + ctypes.bool, /* bool(return) */ 1.1283 + ctypes.int32_t, /* HWND hwndOwner */ 1.1284 + ctypes.jschar.ptr, /* LPTSTR lpszPath */ 1.1285 + ctypes.int32_t, /* int csidl */ 1.1286 + ctypes.bool /* BOOL fCreate */); 1.1287 + 1.1288 + let aryPathLocalAppData = ctypes.jschar.array()(260); 1.1289 + let rv = SHGetSpecialFolderPath(0, aryPathLocalAppData, CSIDL_LOCAL_APPDATA, false); 1.1290 + lib.close(); 1.1291 + 1.1292 + let pathLocalAppData = aryPathLocalAppData.readString(); // Convert the c-string to js-string 1.1293 + let updatesDir = AUS_Cc["@mozilla.org/file/local;1"]. 1.1294 + createInstance(AUS_Ci.nsILocalFile); 1.1295 + updatesDir.initWithPath(pathLocalAppData); 1.1296 + return updatesDir; 1.1297 +}); 1.1298 + 1.1299 +XPCOMUtils.defineLazyGetter(this, "gProgFilesDir", 1.1300 + function test_gProgFilesDir() { 1.1301 + const CSIDL_PROGRAM_FILES = 0x26; 1.1302 + 1.1303 + AUS_Cu.import("resource://gre/modules/ctypes.jsm"); 1.1304 + let lib = ctypes.open("shell32"); 1.1305 + let SHGetSpecialFolderPath = lib.declare("SHGetSpecialFolderPathW", 1.1306 + ctypes.winapi_abi, 1.1307 + ctypes.bool, /* bool(return) */ 1.1308 + ctypes.int32_t, /* HWND hwndOwner */ 1.1309 + ctypes.jschar.ptr, /* LPTSTR lpszPath */ 1.1310 + ctypes.int32_t, /* int csidl */ 1.1311 + ctypes.bool /* BOOL fCreate */); 1.1312 + 1.1313 + let aryPathProgFiles = ctypes.jschar.array()(260); 1.1314 + let rv = SHGetSpecialFolderPath(0, aryPathProgFiles, CSIDL_PROGRAM_FILES, false); 1.1315 + lib.close(); 1.1316 + 1.1317 + let pathProgFiles = aryPathProgFiles.readString(); // Convert the c-string to js-string 1.1318 + let progFilesDir = AUS_Cc["@mozilla.org/file/local;1"]. 1.1319 + createInstance(AUS_Ci.nsILocalFile); 1.1320 + progFilesDir.initWithPath(pathProgFiles); 1.1321 + return progFilesDir; 1.1322 +}); 1.1323 + 1.1324 +/** 1.1325 + * Helper function for getting the update root directory used by the tests. This 1.1326 + * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir 1.1327 + * in nsXREDirProvider.cpp so an application will be able to find the update 1.1328 + * when running a test that launches the application. 1.1329 + */ 1.1330 +function getMockUpdRootD() { 1.1331 + let localAppDataDir = gLocalAppDataDir.clone(); 1.1332 + let progFilesDir = gProgFilesDir.clone(); 1.1333 + let appDir = Services.dirsvc.get(XRE_EXECUTABLE_FILE, AUS_Ci.nsIFile).parent; 1.1334 + 1.1335 + let appDirPath = appDir.path; 1.1336 + var relPathUpdates = ""; 1.1337 + if (gInstallDirPathHash && (MOZ_APP_VENDOR || MOZ_APP_BASENAME)) { 1.1338 + relPathUpdates += (MOZ_APP_VENDOR ? MOZ_APP_VENDOR : MOZ_APP_BASENAME) + 1.1339 + "\\" + DIR_UPDATES + "\\" + gInstallDirPathHash; 1.1340 + } 1.1341 + 1.1342 + if (!relPathUpdates) { 1.1343 + if (appDirPath.length > progFilesDir.path.length) { 1.1344 + if (appDirPath.substr(0, progFilesDir.path.length) == progFilesDir.path) { 1.1345 + if (MOZ_APP_VENDOR && MOZ_APP_BASENAME) { 1.1346 + relPathUpdates += MOZ_APP_VENDOR + "\\" + MOZ_APP_BASENAME; 1.1347 + } else { 1.1348 + relPathUpdates += MOZ_APP_BASENAME; 1.1349 + } 1.1350 + relPathUpdates += appDirPath.substr(progFilesDir.path.length); 1.1351 + } 1.1352 + } 1.1353 + } 1.1354 + 1.1355 + if (!relPathUpdates) { 1.1356 + if (MOZ_APP_VENDOR && MOZ_APP_BASENAME) { 1.1357 + relPathUpdates += MOZ_APP_VENDOR + "\\" + MOZ_APP_BASENAME; 1.1358 + } else { 1.1359 + relPathUpdates += MOZ_APP_BASENAME; 1.1360 + } 1.1361 + relPathUpdates += "\\" + MOZ_APP_NAME; 1.1362 + } 1.1363 + 1.1364 + var updatesDir = AUS_Cc["@mozilla.org/file/local;1"]. 1.1365 + createInstance(AUS_Ci.nsILocalFile); 1.1366 + updatesDir.initWithPath(localAppDataDir.path + "\\" + relPathUpdates); 1.1367 + logTestInfo("returning UpdRootD Path: " + updatesDir.path); 1.1368 + return updatesDir; 1.1369 +} 1.1370 +#else 1.1371 +/** 1.1372 + * Helper function for getting the update root directory used by the tests. This 1.1373 + * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir 1.1374 + * in nsXREDirProvider.cpp so an application will be able to find the update 1.1375 + * when running a test that launches the application. 1.1376 + */ 1.1377 +function getMockUpdRootD() { 1.1378 + return getApplyDirFile(DIR_BIN_REL_PATH, true); 1.1379 +} 1.1380 +#endif 1.1381 + 1.1382 +/** 1.1383 + * Helper function for getting the nsIFile for the directory where the update 1.1384 + * has been applied. 1.1385 + * 1.1386 + * This will be the same as getApplyDirFile for foreground updates, but will 1.1387 + * point to a different file for the case of staged updates. 1.1388 + * 1.1389 + * Functions which attempt to access the files in the updated directory should 1.1390 + * be using this instead of getApplyDirFile. 1.1391 + * 1.1392 + * @param aRelPath (optional) 1.1393 + * The relative path to the file or directory to get from the root of 1.1394 + * the test's directory. If not specified the test's directory will be 1.1395 + * returned. 1.1396 + * @param aAllowNonexistent (optional) 1.1397 + * Whether the file must exist. If false or not specified the file must 1.1398 + * exist or the function will throw. 1.1399 + * @return The nsIFile for the directory where the update has been applied. 1.1400 + */ 1.1401 +function getTargetDirFile(aRelPath, aAllowNonexistent) { 1.1402 + let relpath = getUpdatedDirPath() + (aRelPath ? aRelPath : ""); 1.1403 + return do_get_file(relpath, aAllowNonexistent); 1.1404 +} 1.1405 + 1.1406 +if (IS_WIN) { 1.1407 + const kLockFileName = "updated.update_in_progress.lock"; 1.1408 + /** 1.1409 + * Helper function for locking a directory on Windows. 1.1410 + * 1.1411 + * @param aDir 1.1412 + * The nsIFile for the directory to lock. 1.1413 + */ 1.1414 + function lockDirectory(aDir) { 1.1415 + var file = aDir.clone(); 1.1416 + file.append(kLockFileName); 1.1417 + file.create(file.NORMAL_FILE_TYPE, 0o444); 1.1418 + file.QueryInterface(AUS_Ci.nsILocalFileWin); 1.1419 + file.fileAttributesWin |= file.WFA_READONLY; 1.1420 + file.fileAttributesWin &= ~file.WFA_READWRITE; 1.1421 + logTestInfo("testing the successful creation of the lock file"); 1.1422 + do_check_true(file.exists()); 1.1423 + do_check_false(file.isWritable()); 1.1424 + } 1.1425 + /** 1.1426 + * Helper function for unlocking a directory on Windows. 1.1427 + * 1.1428 + * @param aDir 1.1429 + * The nsIFile for the directory to unlock. 1.1430 + */ 1.1431 + function unlockDirectory(aDir) { 1.1432 + var file = aDir.clone(); 1.1433 + file.append(kLockFileName); 1.1434 + file.QueryInterface(AUS_Ci.nsILocalFileWin); 1.1435 + file.fileAttributesWin |= file.WFA_READWRITE; 1.1436 + file.fileAttributesWin &= ~file.WFA_READONLY; 1.1437 + logTestInfo("removing and testing the successful removal of the lock file"); 1.1438 + file.remove(false); 1.1439 + do_check_false(file.exists()); 1.1440 + } 1.1441 +} 1.1442 + 1.1443 +/** 1.1444 + * Helper function for updater tests for launching the updater binary to apply 1.1445 + * a mar file. 1.1446 + * 1.1447 + * @param aExpectedExitValue 1.1448 + * The expected exit value from the updater binary. 1.1449 + * @param aExpectedStatus 1.1450 + * The expected value of update.status when the test finishes. 1.1451 + * @param aCallback (optional) 1.1452 + * A callback function that will be called when this function finishes. 1.1453 + * If null no function will be called when this function finishes. 1.1454 + * If not specified the checkUpdateApplied function will be called when 1.1455 + * this function finishes. 1.1456 + */ 1.1457 +function runUpdate(aExpectedExitValue, aExpectedStatus, aCallback) { 1.1458 + // Copy the updater binary to the updates directory. 1.1459 + let binDir = gGREDirOrig.clone(); 1.1460 + let updater = binDir.clone(); 1.1461 + updater.append("updater.app"); 1.1462 + if (!updater.exists()) { 1.1463 + updater = binDir.clone(); 1.1464 + updater.append(FILE_UPDATER_BIN); 1.1465 + if (!updater.exists()) { 1.1466 + do_throw("Unable to find updater binary!"); 1.1467 + } 1.1468 + } 1.1469 + 1.1470 + let updatesDir = getUpdatesPatchDir(); 1.1471 + updater.copyToFollowingLinks(updatesDir, updater.leafName); 1.1472 + let updateBin = updatesDir.clone(); 1.1473 + updateBin.append(updater.leafName); 1.1474 + if (updateBin.leafName == "updater.app") { 1.1475 + updateBin.append("Contents"); 1.1476 + updateBin.append("MacOS"); 1.1477 + updateBin.append("updater"); 1.1478 + if (!updateBin.exists()) { 1.1479 + do_throw("Unable to find the updater executable!"); 1.1480 + } 1.1481 + } 1.1482 + 1.1483 + let applyToDir = getApplyDirFile(null, true); 1.1484 + let applyToDirPath = applyToDir.path; 1.1485 + if (gStageUpdate || gSwitchApp) { 1.1486 + applyToDirPath += "/" + DIR_UPDATED + "/"; 1.1487 + } 1.1488 + 1.1489 + if (IS_WIN) { 1.1490 + // Convert to native path 1.1491 + applyToDirPath = applyToDirPath.replace(/\//g, "\\"); 1.1492 + } 1.1493 + 1.1494 + let callbackApp = getApplyDirFile("a/b/" + gCallbackBinFile); 1.1495 + callbackApp.permissions = PERMS_DIRECTORY; 1.1496 + 1.1497 + let args = [updatesDir.path, applyToDirPath, 0]; 1.1498 + if (gStageUpdate) { 1.1499 + args[2] = -1; 1.1500 + } else { 1.1501 + if (gSwitchApp) { 1.1502 + args[2] = "0/replace"; 1.1503 + } 1.1504 + args = args.concat([callbackApp.parent.path, callbackApp.path]); 1.1505 + args = args.concat(gCallbackArgs); 1.1506 + } 1.1507 + logTestInfo("running the updater: " + updateBin.path + " " + args.join(" ")); 1.1508 + 1.1509 + let env = AUS_Cc["@mozilla.org/process/environment;1"]. 1.1510 + getService(AUS_Ci.nsIEnvironment); 1.1511 + if (gDisableReplaceFallback) { 1.1512 + env.set("MOZ_NO_REPLACE_FALLBACK", "1"); 1.1513 + } 1.1514 + 1.1515 + let process = AUS_Cc["@mozilla.org/process/util;1"]. 1.1516 + createInstance(AUS_Ci.nsIProcess); 1.1517 + process.init(updateBin); 1.1518 + process.run(true, args, args.length); 1.1519 + 1.1520 + if (gDisableReplaceFallback) { 1.1521 + env.set("MOZ_NO_REPLACE_FALLBACK", ""); 1.1522 + } 1.1523 + 1.1524 + let status = readStatusFile(); 1.1525 + if (process.exitValue != aExpectedExitValue || status != aExpectedStatus) { 1.1526 + if (process.exitValue != aExpectedExitValue) { 1.1527 + logTestInfo("updater exited with unexpected value! Got: " + 1.1528 + process.exitValue + ", Expected: " + aExpectedExitValue); 1.1529 + } 1.1530 + if (status != aExpectedStatus) { 1.1531 + logTestInfo("update status is not the expected status! Got: " + status + 1.1532 + ", Expected: " + aExpectedStatus); 1.1533 + } 1.1534 + let updateLog = getUpdatesPatchDir(); 1.1535 + updateLog.append(FILE_UPDATE_LOG); 1.1536 + logTestInfo("contents of " + updateLog.path + ":\n" + 1.1537 + readFileBytes(updateLog).replace(/\r\n/g, "\n")); 1.1538 + } 1.1539 + logTestInfo("testing updater binary process exitValue against expected " + 1.1540 + "exit value"); 1.1541 + do_check_eq(process.exitValue, aExpectedExitValue); 1.1542 + logTestInfo("testing update status against expected status"); 1.1543 + do_check_eq(status, aExpectedStatus); 1.1544 + 1.1545 + if (aCallback !== null) { 1.1546 + if (typeof(aCallback) == typeof(Function)) { 1.1547 + aCallback(); 1.1548 + } else { 1.1549 + checkUpdateApplied(); 1.1550 + } 1.1551 + } 1.1552 +} 1.1553 +/** 1.1554 + * Helper function for updater tests to stage an update. 1.1555 + */ 1.1556 +function stageUpdate() { 1.1557 + logTestInfo("start - staging update"); 1.1558 + Services.obs.addObserver(gUpdateStagedObserver, "update-staged", false); 1.1559 + 1.1560 + setEnvironment(); 1.1561 + // Stage the update. 1.1562 + AUS_Cc["@mozilla.org/updates/update-processor;1"]. 1.1563 + createInstance(AUS_Ci.nsIUpdateProcessor). 1.1564 + processUpdate(gUpdateManager.activeUpdate); 1.1565 + resetEnvironment(); 1.1566 + 1.1567 + logTestInfo("finish - staging update"); 1.1568 +} 1.1569 + 1.1570 +/** 1.1571 + * Helper function to check whether the maintenance service updater tests should 1.1572 + * run. See bug 711660 for more details. 1.1573 + * 1.1574 + * @param aFirstTest 1.1575 + * Whether this is the first test within the test. 1.1576 + * @return true if the test should run and false if it shouldn't. 1.1577 + */ 1.1578 +function shouldRunServiceTest(aFirstTest) { 1.1579 + // In case the machine is running an old maintenance service or if it 1.1580 + // is not installed, and permissions exist to install it. Then install 1.1581 + // the newer bin that we have. 1.1582 + attemptServiceInstall(); 1.1583 + 1.1584 + let binDir = getGREDir(); 1.1585 + let updaterBin = binDir.clone(); 1.1586 + updaterBin.append(FILE_UPDATER_BIN); 1.1587 + if (!updaterBin.exists()) { 1.1588 + do_throw("Unable to find updater binary!"); 1.1589 + } 1.1590 + 1.1591 + let updaterBinPath = updaterBin.path; 1.1592 + if (/ /.test(updaterBinPath)) { 1.1593 + updaterBinPath = '"' + updaterBinPath + '"'; 1.1594 + } 1.1595 + 1.1596 + const REG_PATH = "SOFTWARE\\Mozilla\\MaintenanceService\\" + 1.1597 + "3932ecacee736d366d6436db0f55bce4"; 1.1598 + 1.1599 + let key = AUS_Cc["@mozilla.org/windows-registry-key;1"]. 1.1600 + createInstance(AUS_Ci.nsIWindowsRegKey); 1.1601 + try { 1.1602 + key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH, 1.1603 + AUS_Ci.nsIWindowsRegKey.ACCESS_READ | key.WOW64_64); 1.1604 + } catch (e) { 1.1605 +#ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK 1.1606 + // The build system could sign the files and not have the test registry key 1.1607 + // in which case we should fail the test by throwing so it can be fixed. 1.1608 + if (isBinarySigned(updaterBinPath)) { 1.1609 + do_throw("binary is signed but the test registry key does not exists!"); 1.1610 + } 1.1611 +#endif 1.1612 + 1.1613 + logTestInfo("this test can only run on the buildbot build system at this " + 1.1614 + "time."); 1.1615 + return false; 1.1616 + } 1.1617 + 1.1618 + // Check to make sure the service is installed 1.1619 + let helperBin = getTestDirFile(FILE_HELPER_BIN); 1.1620 + let args = ["wait-for-service-stop", "MozillaMaintenance", "10"]; 1.1621 + let process = AUS_Cc["@mozilla.org/process/util;1"]. 1.1622 + createInstance(AUS_Ci.nsIProcess); 1.1623 + process.init(helperBin); 1.1624 + logTestInfo("checking if the service exists on this machine."); 1.1625 + process.run(true, args, args.length); 1.1626 + if (process.exitValue == 0xEE) { 1.1627 + do_throw("test registry key exists but this test can only run on systems " + 1.1628 + "with the maintenance service installed."); 1.1629 + } else { 1.1630 + logTestInfo("service exists, return value: " + process.exitValue); 1.1631 + } 1.1632 + 1.1633 + // If this is the first test in the series, then there is no reason the 1.1634 + // service should be anything but stopped, so be strict here and throw 1.1635 + // an error. 1.1636 + if (aFirstTest && process.exitValue != 0) { 1.1637 + do_throw("First test, check for service stopped state returned error " + 1.1638 + process.exitValue); 1.1639 + } 1.1640 + 1.1641 +#ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK 1.1642 + if (!isBinarySigned(updaterBinPath)) { 1.1643 + logTestInfo("test registry key exists but this test can only run on " + 1.1644 + "builds with signed binaries when " + 1.1645 + "DISABLE_UPDATER_AUTHENTICODE_CHECK is not defined"); 1.1646 + do_throw("this test can only run on builds with signed binaries."); 1.1647 + } 1.1648 +#endif 1.1649 + return true; 1.1650 +} 1.1651 + 1.1652 +/** 1.1653 + * Helper function to check whether the a binary is signed. 1.1654 + * 1.1655 + * @param aBinPath The path to the file to check if it is signed. 1.1656 + * @return true if the file is signed and false if it isn't. 1.1657 + */ 1.1658 +function isBinarySigned(aBinPath) { 1.1659 + let helperBin = getTestDirFile(FILE_HELPER_BIN); 1.1660 + let args = ["check-signature", aBinPath]; 1.1661 + let process = AUS_Cc["@mozilla.org/process/util;1"]. 1.1662 + createInstance(AUS_Ci.nsIProcess); 1.1663 + process.init(helperBin); 1.1664 + process.run(true, args, args.length); 1.1665 + if (process.exitValue != 0) { 1.1666 + logTestInfo("binary is not signed. " + FILE_HELPER_BIN + " returned " + 1.1667 + process.exitValue + " for file " + aBinPath); 1.1668 + return false; 1.1669 + } 1.1670 + return true; 1.1671 +} 1.1672 + 1.1673 +/** 1.1674 + * Helper function for asynchronously setting up the application files required 1.1675 + * to launch the application for the updater tests by either copying or creating 1.1676 + * symlinks for the files. This is needed for Windows debug builds which can 1.1677 + * lock a file that is being copied so that the tests can run in parallel. After 1.1678 + * the files have been copied the setupAppFilesFinished function will be called. 1.1679 + */ 1.1680 +function setupAppFilesAsync() { 1.1681 + gTimeoutRuns++; 1.1682 + try { 1.1683 + setupAppFiles(); 1.1684 + } catch (e) { 1.1685 + if (gTimeoutRuns > MAX_TIMEOUT_RUNS) { 1.1686 + do_throw("Exceeded MAX_TIMEOUT_RUNS while trying to setup application " + 1.1687 + "files. Exception: " + e); 1.1688 + } 1.1689 + do_timeout(TEST_CHECK_TIMEOUT, setupAppFilesAsync); 1.1690 + return; 1.1691 + } 1.1692 + 1.1693 + setupAppFilesFinished(); 1.1694 +} 1.1695 + 1.1696 +/** 1.1697 + * Helper function for setting up the application files required to launch the 1.1698 + * application for the updater tests by either copying or creating symlinks for 1.1699 + * the files. 1.1700 + */ 1.1701 +function setupAppFiles() { 1.1702 + logTestInfo("start - copying or creating symlinks for application files " + 1.1703 + "for the test"); 1.1704 + 1.1705 + let srcDir = getCurrentProcessDir(); 1.1706 + let destDir = getApplyDirFile(null, true); 1.1707 + if (!destDir.exists()) { 1.1708 + try { 1.1709 + destDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); 1.1710 + } catch (e) { 1.1711 + logTestInfo("unable to create directory, Path: " + destDir.path + 1.1712 + ", Exception: " + e); 1.1713 + do_throw(e); 1.1714 + } 1.1715 + } 1.1716 + 1.1717 + // Required files for the application or the test that aren't listed in the 1.1718 + // dependentlibs.list file. 1.1719 + let fileRelPaths = [FILE_APP_BIN, FILE_UPDATER_BIN, 1.1720 + "application.ini", "dependentlibs.list"]; 1.1721 + 1.1722 + // On Linux the updater.png must also be copied 1.1723 + if (IS_UNIX && !IS_MACOSX) { 1.1724 + fileRelPaths.push("icons/updater.png"); 1.1725 + } 1.1726 + 1.1727 + // Read the dependent libs file leafnames from the dependentlibs.list file 1.1728 + // into the array. 1.1729 + let deplibsFile = srcDir.clone(); 1.1730 + deplibsFile.append("dependentlibs.list"); 1.1731 + let istream = AUS_Cc["@mozilla.org/network/file-input-stream;1"]. 1.1732 + createInstance(AUS_Ci.nsIFileInputStream); 1.1733 + istream.init(deplibsFile, 0x01, 0o444, 0); 1.1734 + istream.QueryInterface(AUS_Ci.nsILineInputStream); 1.1735 + 1.1736 + let hasMore; 1.1737 + let line = {}; 1.1738 + do { 1.1739 + hasMore = istream.readLine(line); 1.1740 + fileRelPaths.push(line.value); 1.1741 + } while(hasMore); 1.1742 + 1.1743 + istream.close(); 1.1744 + 1.1745 + fileRelPaths.forEach(function CMAF_FLN_FE(aFileRelPath) { 1.1746 + copyFileToTestAppDir(aFileRelPath); 1.1747 + }); 1.1748 + 1.1749 + logTestInfo("finish - copying or creating symlinks for application files " + 1.1750 + "for the test"); 1.1751 +} 1.1752 + 1.1753 +/** 1.1754 + * Copies the specified files from the dist/bin directory into the test's 1.1755 + * application directory. 1.1756 + * 1.1757 + * @param aFileRelPath 1.1758 + * The relative path of the file to copy. 1.1759 + */ 1.1760 +function copyFileToTestAppDir(aFileRelPath) { 1.1761 + let fileRelPath = aFileRelPath; 1.1762 + let srcFile = gGREDirOrig.clone(); 1.1763 + let pathParts = fileRelPath.split("/"); 1.1764 + for (let i = 0; i < pathParts.length; i++) { 1.1765 + if (pathParts[i]) { 1.1766 + srcFile.append(pathParts[i]); 1.1767 + } 1.1768 + } 1.1769 + 1.1770 + if (IS_MACOSX && !srcFile.exists()) { 1.1771 + logTestInfo("unable to copy file since it doesn't exist! Checking if " + 1.1772 + fileRelPath + ".app exists. Path: " + 1.1773 + srcFile.path); 1.1774 + srcFile = gGREDirOrig.clone(); 1.1775 + for (let i = 0; i < pathParts.length; i++) { 1.1776 + if (pathParts[i]) { 1.1777 + srcFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : "")); 1.1778 + } 1.1779 + } 1.1780 + fileRelPath = fileRelPath + ".app"; 1.1781 + } 1.1782 + 1.1783 + if (!srcFile.exists()) { 1.1784 + do_throw("Unable to copy file since it doesn't exist! Path: " + 1.1785 + srcFile.path); 1.1786 + } 1.1787 + 1.1788 + // Symlink libraries. Note that the XUL library on Mac OS X doesn't have a 1.1789 + // file extension and this will always be false on Windows. 1.1790 + let shouldSymlink = (pathParts[pathParts.length - 1] == "XUL" || 1.1791 + fileRelPath.substr(fileRelPath.length - 3) == ".so" || 1.1792 + fileRelPath.substr(fileRelPath.length - 6) == ".dylib"); 1.1793 + let destFile = getApplyDirFile(DIR_BIN_REL_PATH + fileRelPath, true); 1.1794 + if (!shouldSymlink) { 1.1795 + if (!destFile.exists()) { 1.1796 + try { 1.1797 + srcFile.copyToFollowingLinks(destFile.parent, destFile.leafName); 1.1798 + } catch (e) { 1.1799 + // Just in case it is partially copied 1.1800 + if (destFile.exists()) { 1.1801 + try { 1.1802 + destFile.remove(true); 1.1803 + } catch (e) { 1.1804 + logTestInfo("unable to remove file that failed to copy! Path: " + 1.1805 + destFile.path); 1.1806 + } 1.1807 + } 1.1808 + do_throw("Unable to copy file! Path: " + srcFile.path + 1.1809 + ", Exception: " + e); 1.1810 + } 1.1811 + } 1.1812 + } else { 1.1813 + try { 1.1814 + if (destFile.exists()) { 1.1815 + destFile.remove(false); 1.1816 + } 1.1817 + let ln = AUS_Cc["@mozilla.org/file/local;1"].createInstance(AUS_Ci.nsILocalFile); 1.1818 + ln.initWithPath("/bin/ln"); 1.1819 + let process = AUS_Cc["@mozilla.org/process/util;1"].createInstance(AUS_Ci.nsIProcess); 1.1820 + process.init(ln); 1.1821 + let args = ["-s", srcFile.path, destFile.path]; 1.1822 + process.run(true, args, args.length); 1.1823 + logTestInfo("verifying symlink. Path: " + destFile.path); 1.1824 + do_check_true(destFile.isSymlink()); 1.1825 + } catch (e) { 1.1826 + do_throw("Unable to create symlink for file! Path: " + srcFile.path + 1.1827 + ", Exception: " + e); 1.1828 + } 1.1829 + } 1.1830 +} 1.1831 + 1.1832 +/** 1.1833 + * Attempts to upgrade the maintenance service if permissions are allowed. 1.1834 + * This is useful for XP where we have permission to upgrade in case an 1.1835 + * older service installer exists. Also if the user manually installed into 1.1836 + * a unprivileged location. 1.1837 + */ 1.1838 +function attemptServiceInstall() { 1.1839 + var version = AUS_Cc["@mozilla.org/system-info;1"] 1.1840 + .getService(AUS_Ci.nsIPropertyBag2) 1.1841 + .getProperty("version"); 1.1842 + var isVistaOrHigher = (parseFloat(version) >= 6.0); 1.1843 + if (isVistaOrHigher) { 1.1844 + return; 1.1845 + } 1.1846 + 1.1847 + let binDir = getGREDir(); 1.1848 + let installerFile = binDir.clone(); 1.1849 + installerFile.append(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN); 1.1850 + if (!installerFile.exists()) { 1.1851 + do_throw(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN + " not found."); 1.1852 + } 1.1853 + let installerProcess = AUS_Cc["@mozilla.org/process/util;1"]. 1.1854 + createInstance(AUS_Ci.nsIProcess); 1.1855 + installerProcess.init(installerFile); 1.1856 + logTestInfo("starting installer process..."); 1.1857 + installerProcess.run(true, [], 0); 1.1858 +} 1.1859 + 1.1860 +/** 1.1861 + * Helper function for updater tests for launching the updater using the 1.1862 + * maintenance service to apply a mar file. 1.1863 + * 1.1864 + * @param aInitialStatus 1.1865 + * The initial value of update.status. 1.1866 + * @param aExpectedStatus 1.1867 + * The expected value of update.status when the test finishes. 1.1868 + * @param aCheckSvcLog 1.1869 + * Whether the service log should be checked (optional). 1.1870 + */ 1.1871 +function runUpdateUsingService(aInitialStatus, aExpectedStatus, aCheckSvcLog) { 1.1872 + // Check the service logs for a successful update 1.1873 + function checkServiceLogs(aOriginalContents) { 1.1874 + let contents = readServiceLogFile(); 1.1875 + logTestInfo("the contents of maintenanceservice.log:\n" + contents + "\n"); 1.1876 + do_check_neq(contents, aOriginalContents); 1.1877 + do_check_neq(contents.indexOf(LOG_SVC_SUCCESSFUL_LAUNCH), -1); 1.1878 + } 1.1879 + function readServiceLogFile() { 1.1880 + let file = AUS_Cc["@mozilla.org/file/directory_service;1"]. 1.1881 + getService(AUS_Ci.nsIProperties). 1.1882 + get("CmAppData", AUS_Ci.nsIFile); 1.1883 + file.append("Mozilla"); 1.1884 + file.append("logs"); 1.1885 + file.append("maintenanceservice.log"); 1.1886 + return readFile(file); 1.1887 + } 1.1888 + function waitServiceApps() { 1.1889 + // maintenanceservice_installer.exe is started async during updates. 1.1890 + waitForApplicationStop("maintenanceservice_installer.exe"); 1.1891 + // maintenanceservice_tmp.exe is started async from the service installer. 1.1892 + waitForApplicationStop("maintenanceservice_tmp.exe"); 1.1893 + // In case the SCM thinks the service is stopped, but process still exists. 1.1894 + waitForApplicationStop("maintenanceservice.exe"); 1.1895 + } 1.1896 + function waitForServiceStop(aFailTest) { 1.1897 + waitServiceApps(); 1.1898 + logTestInfo("waiting for service to stop if necessary..."); 1.1899 + // Use the helper bin to ensure the service is stopped. If not 1.1900 + // stopped then wait for the service to be stopped (at most 120 seconds) 1.1901 + let helperBin = getTestDirFile(FILE_HELPER_BIN); 1.1902 + let helperBinArgs = ["wait-for-service-stop", 1.1903 + "MozillaMaintenance", 1.1904 + "120"]; 1.1905 + let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"]. 1.1906 + createInstance(AUS_Ci.nsIProcess); 1.1907 + helperBinProcess.init(helperBin); 1.1908 + logTestInfo("stopping service..."); 1.1909 + helperBinProcess.run(true, helperBinArgs, helperBinArgs.length); 1.1910 + if (helperBinProcess.exitValue == 0xEE) { 1.1911 + do_throw("The service does not exist on this machine. Return value: " + 1.1912 + helperBinProcess.exitValue); 1.1913 + } else if (helperBinProcess.exitValue != 0) { 1.1914 + if (aFailTest) { 1.1915 + do_throw("maintenance service did not stop, last state: " + 1.1916 + helperBinProcess.exitValue + ". Forcing test failure."); 1.1917 + } else { 1.1918 + logTestInfo("maintenance service did not stop, last state: " + 1.1919 + helperBinProcess.exitValue + ". May cause failures."); 1.1920 + } 1.1921 + } else { 1.1922 + logTestInfo("service stopped."); 1.1923 + } 1.1924 + waitServiceApps(); 1.1925 + } 1.1926 + function waitForApplicationStop(aApplication) { 1.1927 + logTestInfo("waiting for " + aApplication + " to stop if " + 1.1928 + "necessary..."); 1.1929 + // Use the helper bin to ensure the application is stopped. 1.1930 + // If not, then wait for it to be stopped (at most 120 seconds) 1.1931 + let helperBin = getTestDirFile(FILE_HELPER_BIN); 1.1932 + let helperBinArgs = ["wait-for-application-exit", 1.1933 + aApplication, 1.1934 + "120"]; 1.1935 + let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"]. 1.1936 + createInstance(AUS_Ci.nsIProcess); 1.1937 + helperBinProcess.init(helperBin); 1.1938 + helperBinProcess.run(true, helperBinArgs, helperBinArgs.length); 1.1939 + if (helperBinProcess.exitValue != 0) { 1.1940 + do_throw(aApplication + " did not stop, last state: " + 1.1941 + helperBinProcess.exitValue + ". Forcing test failure."); 1.1942 + } 1.1943 + } 1.1944 + 1.1945 + // Make sure the service from the previous test is already stopped. 1.1946 + waitForServiceStop(true); 1.1947 + 1.1948 + // Prevent the cleanup function from begin run more than once 1.1949 + if (gRegisteredServiceCleanup === undefined) { 1.1950 + gRegisteredServiceCleanup = true; 1.1951 + 1.1952 + do_register_cleanup(function RUUS_cleanup() { 1.1953 + resetEnvironment(); 1.1954 + 1.1955 + // This will delete the app arguments log file if it exists. 1.1956 + try { 1.1957 + getAppArgsLogPath(); 1.1958 + } catch (e) { 1.1959 + logTestInfo("unable to remove file during cleanup. Exception: " + e); 1.1960 + } 1.1961 + }); 1.1962 + } 1.1963 + 1.1964 + let svcOriginalLog; 1.1965 + // Default to checking the service log if the parameter is not specified. 1.1966 + if (aCheckSvcLog === undefined || aCheckSvcLog) { 1.1967 + svcOriginalLog = readServiceLogFile(); 1.1968 + } 1.1969 + 1.1970 + let appArgsLogPath = getAppArgsLogPath(); 1.1971 + gServiceLaunchedCallbackLog = appArgsLogPath.replace(/^"|"$/g, ""); 1.1972 + 1.1973 + let updatesDir = getUpdatesPatchDir(); 1.1974 + let file = updatesDir.clone(); 1.1975 + writeStatusFile(aInitialStatus); 1.1976 + 1.1977 + // sanity check 1.1978 + do_check_eq(readStatusState(), aInitialStatus); 1.1979 + 1.1980 + writeVersionFile(DEFAULT_UPDATE_VERSION); 1.1981 + 1.1982 + gServiceLaunchedCallbackArgs = [ 1.1983 + "-no-remote", 1.1984 + "-process-updates", 1.1985 + "-dump-args", 1.1986 + appArgsLogPath 1.1987 + ]; 1.1988 + 1.1989 + if (gSwitchApp) { 1.1990 + // We want to set the env vars again 1.1991 + gShouldResetEnv = undefined; 1.1992 + } 1.1993 + 1.1994 + setEnvironment(); 1.1995 + 1.1996 + // There is a security check done by the service to make sure the updater 1.1997 + // we are executing is the same as the one in the apply-to dir. 1.1998 + // To make sure they match from tests we copy updater.exe to the apply-to dir. 1.1999 + copyFileToTestAppDir(FILE_UPDATER_BIN); 1.2000 + 1.2001 + // The service will execute maintenanceservice_installer.exe and 1.2002 + // will copy maintenanceservice.exe out of the same directory from 1.2003 + // the installation directory. So we need to make sure both of those 1.2004 + // bins always exist in the installation directory. 1.2005 + copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_BIN); 1.2006 + copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN); 1.2007 + 1.2008 + let launchBin = getLaunchBin(); 1.2009 + let args = getProcessArgs(["-dump-args", appArgsLogPath]); 1.2010 + 1.2011 + let process = AUS_Cc["@mozilla.org/process/util;1"]. 1.2012 + createInstance(AUS_Ci.nsIProcess); 1.2013 + process.init(launchBin); 1.2014 + logTestInfo("launching " + launchBin.path + " " + args.join(" ")); 1.2015 + // Firefox does not wait for the service command to finish, but 1.2016 + // we still launch the process sync to avoid intermittent failures with 1.2017 + // the log file not being written out yet. 1.2018 + // We will rely on watching the update.status file and waiting for the service 1.2019 + // to stop to know the service command is done. 1.2020 + process.run(true, args, args.length); 1.2021 + 1.2022 + resetEnvironment(); 1.2023 + 1.2024 + function timerCallback(aTimer) { 1.2025 + // Wait for the expected status 1.2026 + let status = readStatusState(); 1.2027 + // status will probably always be equal to STATE_APPLYING but there is a 1.2028 + // race condition where it would be possible on slower machines where status 1.2029 + // could be equal to STATE_PENDING_SVC. 1.2030 + if (status == STATE_APPLYING || 1.2031 + status == STATE_PENDING_SVC) { 1.2032 + logTestInfo("still waiting to see the " + aExpectedStatus + 1.2033 + " status, got " + status + " for now..."); 1.2034 + return; 1.2035 + } 1.2036 + 1.2037 + // Make sure all of the logs are written out. 1.2038 + waitForServiceStop(false); 1.2039 + 1.2040 + aTimer.cancel(); 1.2041 + aTimer = null; 1.2042 + 1.2043 + if (status != aExpectedStatus) { 1.2044 + logTestInfo("update status is not the expected status! Got: " + status + 1.2045 + ", Expected: " + aExpectedStatus); 1.2046 + logTestInfo("update.status contents: " + readStatusFile()); 1.2047 + let updateLog = getUpdatesPatchDir(); 1.2048 + updateLog.append(FILE_UPDATE_LOG); 1.2049 + logTestInfo("contents of " + updateLog.path + ":\n" + 1.2050 + readFileBytes(updateLog).replace(/\r\n/g, "\n")); 1.2051 + } 1.2052 + logTestInfo("testing update status against expected status"); 1.2053 + do_check_eq(status, aExpectedStatus); 1.2054 + 1.2055 + if (aCheckSvcLog) { 1.2056 + checkServiceLogs(svcOriginalLog); 1.2057 + } 1.2058 + 1.2059 + checkUpdateFinished(); 1.2060 + } 1.2061 + 1.2062 + let timer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer); 1.2063 + timer.initWithCallback(timerCallback, 1000, timer.TYPE_REPEATING_SLACK); 1.2064 +} 1.2065 + 1.2066 +/** 1.2067 + * Gets the platform specific shell binary that is launched using nsIProcess and 1.2068 + * in turn launches a binary used for the test (e.g. application, updater, 1.2069 + * etc.). A shell is used so debug console output can be redirected to a file so 1.2070 + * it doesn't end up in the test log. 1.2071 + * 1.2072 + * @return nsIFile for the shell binary to launch using nsIProcess. 1.2073 + * @throws if the shell binary doesn't exist. 1.2074 + */ 1.2075 +function getLaunchBin() { 1.2076 + let launchBin; 1.2077 + if (IS_WIN) { 1.2078 + launchBin = Services.dirsvc.get("WinD", AUS_Ci.nsIFile); 1.2079 + launchBin.append("System32"); 1.2080 + launchBin.append("cmd.exe"); 1.2081 + } else { 1.2082 + launchBin = AUS_Cc["@mozilla.org/file/local;1"]. 1.2083 + createInstance(AUS_Ci.nsILocalFile); 1.2084 + launchBin.initWithPath("/bin/sh"); 1.2085 + } 1.2086 + 1.2087 + if (!launchBin.exists()) 1.2088 + do_throw(launchBin.path + " must exist to run this test!"); 1.2089 + 1.2090 + return launchBin; 1.2091 +} 1.2092 + 1.2093 +/** 1.2094 + * Helper function that waits until the helper has completed its operations and 1.2095 + * is in a sleep state before performing an update by calling doUpdate. 1.2096 + */ 1.2097 +function waitForHelperSleep() { 1.2098 + gTimeoutRuns++; 1.2099 + // Give the lock file process time to lock the file before updating otherwise 1.2100 + // this test can fail intermittently on Windows debug builds. 1.2101 + let output = getApplyDirFile("a/b/output", true); 1.2102 + if (readFile(output) != "sleeping\n") { 1.2103 + if (gTimeoutRuns > MAX_TIMEOUT_RUNS) { 1.2104 + do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the helper to " + 1.2105 + "finish its operation. Path: " + output.path); 1.2106 + } 1.2107 + do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep); 1.2108 + return; 1.2109 + } 1.2110 + try { 1.2111 + output.remove(false); 1.2112 + } 1.2113 + catch (e) { 1.2114 + if (gTimeoutRuns > MAX_TIMEOUT_RUNS) { 1.2115 + do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the helper " + 1.2116 + "message file to no longer be in use. Path: " + output.path); 1.2117 + } 1.2118 + logTestInfo("failed to remove file. Path: " + output.path); 1.2119 + do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep); 1.2120 + return; 1.2121 + } 1.2122 + doUpdate(); 1.2123 +} 1.2124 + 1.2125 +/** 1.2126 + * Helper function that waits until the helper has finished its operations 1.2127 + * before calling waitForHelperFinishFileUnlock to verify that the helper's 1.2128 + * input and output directories are no longer in use. 1.2129 + */ 1.2130 +function waitForHelperFinished() { 1.2131 + // Give the lock file process time to lock the file before updating otherwise 1.2132 + // this test can fail intermittently on Windows debug builds. 1.2133 + let output = getApplyDirFile("a/b/output", true); 1.2134 + if (readFile(output) != "finished\n") { 1.2135 + do_timeout(TEST_HELPER_TIMEOUT, waitForHelperFinished); 1.2136 + return; 1.2137 + } 1.2138 + // Give the lock file process time to unlock the file before deleting the 1.2139 + // input and output files. 1.2140 + waitForHelperFinishFileUnlock(); 1.2141 +} 1.2142 + 1.2143 +/** 1.2144 + * Helper function that waits until the helper's input and output files are no 1.2145 + * longer in use before calling checkUpdate. 1.2146 + */ 1.2147 +function waitForHelperFinishFileUnlock() { 1.2148 + try { 1.2149 + let output = getApplyDirFile("a/b/output", true); 1.2150 + if (output.exists()) { 1.2151 + output.remove(false); 1.2152 + } 1.2153 + let input = getApplyDirFile("a/b/input", true); 1.2154 + if (input.exists()) { 1.2155 + input.remove(false); 1.2156 + } 1.2157 + } catch (e) { 1.2158 + // Give the lock file process time to unlock the file before deleting the 1.2159 + // input and output files. 1.2160 + do_timeout(TEST_HELPER_TIMEOUT, waitForHelperFinishFileUnlock); 1.2161 + return; 1.2162 + } 1.2163 + checkUpdate(); 1.2164 +} 1.2165 + 1.2166 +/** 1.2167 + * Helper function to tell the helper to finish and exit its sleep state. 1.2168 + */ 1.2169 +function setupHelperFinish() { 1.2170 + let input = getApplyDirFile("a/b/input", true); 1.2171 + writeFile(input, "finish\n"); 1.2172 + waitForHelperFinished(); 1.2173 +} 1.2174 + 1.2175 +/** 1.2176 + * Helper function for updater binary tests that creates the files and 1.2177 + * directories used by the test. 1.2178 + * 1.2179 + * @param aMarFile 1.2180 + * The mar file for the update test. 1.2181 + */ 1.2182 +function setupUpdaterTest(aMarFile, aUpdatedDirExists, aToBeDeletedDirExists) { 1.2183 + let updatesPatchDir = getUpdatesPatchDir(); 1.2184 + if (!updatesPatchDir.exists()) { 1.2185 + updatesPatchDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); 1.2186 + } 1.2187 + // Copy the mar that will be applied 1.2188 + let mar = getTestDirFile(aMarFile); 1.2189 + mar.copyToFollowingLinks(updatesPatchDir, FILE_UPDATE_ARCHIVE); 1.2190 + 1.2191 + createUpdateSettingsINI(); 1.2192 + 1.2193 + let applyToDir = getApplyDirFile(null, true); 1.2194 + gTestFiles.forEach(function SUT_TF_FE(aTestFile) { 1.2195 + if (aTestFile.originalFile || aTestFile.originalContents) { 1.2196 + let testDir = getApplyDirFile(aTestFile.relPathDir, true); 1.2197 + if (!testDir.exists()) 1.2198 + testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); 1.2199 + 1.2200 + let testFile; 1.2201 + if (aTestFile.originalFile) { 1.2202 + testFile = getTestDirFile(aTestFile.originalFile); 1.2203 + testFile.copyToFollowingLinks(testDir, aTestFile.fileName); 1.2204 + testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName); 1.2205 + } else { 1.2206 + testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName, 1.2207 + true); 1.2208 + writeFile(testFile, aTestFile.originalContents); 1.2209 + } 1.2210 + 1.2211 + // Skip these tests on Windows and OS/2 since their 1.2212 + // implementaions of chmod doesn't really set permissions. 1.2213 + if (!IS_WIN && aTestFile.originalPerms) { 1.2214 + testFile.permissions = aTestFile.originalPerms; 1.2215 + // Store the actual permissions on the file for reference later after 1.2216 + // setting the permissions. 1.2217 + if (!aTestFile.comparePerms) { 1.2218 + aTestFile.comparePerms = testFile.permissions; 1.2219 + } 1.2220 + } 1.2221 + } 1.2222 + }); 1.2223 + 1.2224 + let helperBin = getTestDirFile(FILE_HELPER_BIN); 1.2225 + let afterApplyBinDir = getApplyDirFile("a/b/", true); 1.2226 + helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile); 1.2227 + 1.2228 + // Add the test directory that will be updated for a successful update or left 1.2229 + // in the initial state for a failed update. 1.2230 + gTestDirs.forEach(function SUT_TD_FE(aTestDir) { 1.2231 + let testDir = getApplyDirFile(aTestDir.relPathDir, true); 1.2232 + if (!testDir.exists()) { 1.2233 + testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); 1.2234 + } 1.2235 + 1.2236 + if (aTestDir.files) { 1.2237 + aTestDir.files.forEach(function SUT_TD_F_FE(aTestFile) { 1.2238 + let testFile = getApplyDirFile(aTestDir.relPathDir + aTestFile, true); 1.2239 + if (!testFile.exists()) { 1.2240 + testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); 1.2241 + } 1.2242 + }); 1.2243 + } 1.2244 + 1.2245 + if (aTestDir.subDirs) { 1.2246 + aTestDir.subDirs.forEach(function SUT_TD_SD_FE(aSubDir) { 1.2247 + let testSubDir = getApplyDirFile(aTestDir.relPathDir + aSubDir, true); 1.2248 + if (!testSubDir.exists()) { 1.2249 + testSubDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); 1.2250 + } 1.2251 + 1.2252 + if (aTestDir.subDirFiles) { 1.2253 + aTestDir.subDirFiles.forEach(function SUT_TD_SDF_FE(aTestFile) { 1.2254 + let testFile = getApplyDirFile(aTestDir.relPathDir + aSubDir + aTestFile, true); 1.2255 + if (!testFile.exists()) { 1.2256 + testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); 1.2257 + } 1.2258 + }); 1.2259 + } 1.2260 + }); 1.2261 + } 1.2262 + }); 1.2263 + 1.2264 + gTestExtraDirs[0].dirExists = aUpdatedDirExists; 1.2265 + gTestExtraDirs[1].dirExists = IS_WIN ? aToBeDeletedDirExists : false; 1.2266 +} 1.2267 + 1.2268 +/** 1.2269 + * Helper function for updater binary tests that creates the update-settings.ini 1.2270 + * file. 1.2271 + */ 1.2272 +function createUpdateSettingsINI() { 1.2273 + updateSettingsIni = getApplyDirFile(null, true); 1.2274 + if (IS_MACOSX) { 1.2275 + updateSettingsIni.append("Contents"); 1.2276 + updateSettingsIni.append("MacOS"); 1.2277 + } 1.2278 + updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI); 1.2279 + writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS); 1.2280 +} 1.2281 + 1.2282 +/** 1.2283 + * Helper function for updater binary tests for verifying the contents of the 1.2284 + * update log after a successful update. 1.2285 + * 1.2286 + * @param aCompareLogFile 1.2287 + * The log file to compare the update log with. 1.2288 + */ 1.2289 +function checkUpdateLogContents(aCompareLogFile) { 1.2290 + let updateLog = getUpdatesPatchDir(); 1.2291 + updateLog.append(FILE_UPDATE_LOG); 1.2292 + let updateLogContents = readFileBytes(updateLog); 1.2293 + 1.2294 + // The channel-prefs.js is defined in gTestFilesCommon which will always be 1.2295 + // located to the end of gTestFiles. 1.2296 + if (gTestFiles.length > 1 && 1.2297 + gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" && 1.2298 + !gTestFiles[gTestFiles.length - 1].originalContents) { 1.2299 + updateLogContents = updateLogContents.replace(/.* a\/b\/defaults\/.*/g, ""); 1.2300 + } 1.2301 + if (gTestFiles.length > 2 && 1.2302 + gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI && 1.2303 + !gTestFiles[gTestFiles.length - 2].originalContents) { 1.2304 + updateLogContents = updateLogContents.replace(/.* a\/b\/update-settings.ini.*/g, ""); 1.2305 + } 1.2306 + if (gStageUpdate) { 1.2307 + // Skip the staged update messages 1.2308 + updateLogContents = updateLogContents.replace(/Performing a staged update/, ""); 1.2309 + } else if (gSwitchApp) { 1.2310 + // Skip the switch app request messages 1.2311 + updateLogContents = updateLogContents.replace(/Performing a staged update/, ""); 1.2312 + updateLogContents = updateLogContents.replace(/Performing a replace request/, ""); 1.2313 + } 1.2314 + // Skip the source/destination lines since they contain absolute paths. 1.2315 + updateLogContents = updateLogContents.replace(/SOURCE DIRECTORY.*/g, ""); 1.2316 + updateLogContents = updateLogContents.replace(/DESTINATION DIRECTORY.*/g, ""); 1.2317 + // Skip lines that log failed attempts to open the callback executable. 1.2318 + updateLogContents = updateLogContents.replace(/NS_main: callback app file .*/g, ""); 1.2319 + if (gSwitchApp) { 1.2320 + // Remove the lines which contain absolute paths 1.2321 + updateLogContents = updateLogContents.replace(/^Begin moving.*$/mg, ""); 1.2322 + if (IS_MACOSX) { 1.2323 + // Remove the entire section about moving the precomplete file as it contains 1.2324 + // absolute paths. 1.2325 + updateLogContents = updateLogContents.replace(/\n/g, "%%%EOL%%%"); 1.2326 + updateLogContents = updateLogContents.replace(/Moving the precomplete file.*Finished moving the precomplete file/, ""); 1.2327 + updateLogContents = updateLogContents.replace(/%%%EOL%%%/g, "\n"); 1.2328 + } 1.2329 + } 1.2330 + updateLogContents = updateLogContents.replace(/\r/g, ""); 1.2331 + // Replace error codes since they are different on each platform. 1.2332 + updateLogContents = updateLogContents.replace(/, err:.*\n/g, "\n"); 1.2333 + // Replace to make the log parsing happy. 1.2334 + updateLogContents = updateLogContents.replace(/non-fatal error /g, ""); 1.2335 + // The FindFile results when enumerating the filesystem on Windows is not 1.2336 + // determistic so the results matching the following need to be ignored. 1.2337 + updateLogContents = updateLogContents.replace(/.* a\/b\/7\/7text.*\n/g, ""); 1.2338 + // Remove consecutive newlines 1.2339 + updateLogContents = updateLogContents.replace(/\n+/g, "\n"); 1.2340 + // Remove leading and trailing newlines 1.2341 + updateLogContents = updateLogContents.replace(/^\n|\n$/g, ""); 1.2342 + // The update log when running the service tests sometimes starts with data 1.2343 + // from the previous launch of the updater. 1.2344 + updateLogContents = updateLogContents.replace(/^calling QuitProgressUI\n[^\n]*\nUPDATE TYPE/g, "UPDATE TYPE"); 1.2345 + 1.2346 + let compareLogContents = ""; 1.2347 + if (aCompareLogFile) { 1.2348 + compareLogContents = readFileBytes(getTestDirFile(aCompareLogFile)); 1.2349 + } 1.2350 + if (gSwitchApp) { 1.2351 + compareLogContents += LOG_SWITCH_SUCCESS; 1.2352 + } 1.2353 + // The channel-prefs.js is defined in gTestFilesCommon which will always be 1.2354 + // located to the end of gTestFiles. 1.2355 + if (gTestFiles.length > 1 && 1.2356 + gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" && 1.2357 + !gTestFiles[gTestFiles.length - 1].originalContents) { 1.2358 + compareLogContents = compareLogContents.replace(/.* a\/b\/defaults\/.*/g, ""); 1.2359 + } 1.2360 + if (gTestFiles.length > 2 && 1.2361 + gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI && 1.2362 + !gTestFiles[gTestFiles.length - 2].originalContents) { 1.2363 + compareLogContents = compareLogContents.replace(/.* a\/b\/update-settings.ini.*/g, ""); 1.2364 + } 1.2365 + // Remove leading and trailing newlines 1.2366 + compareLogContents = compareLogContents.replace(/\n+/g, "\n"); 1.2367 + // Remove leading and trailing newlines 1.2368 + compareLogContents = compareLogContents.replace(/^\n|\n$/g, ""); 1.2369 + 1.2370 + // Don't write the contents of the file to the log to reduce log spam 1.2371 + // unless there is a failure. 1.2372 + if (compareLogContents == updateLogContents) { 1.2373 + logTestInfo("log contents are correct"); 1.2374 + do_check_true(true); 1.2375 + } else { 1.2376 + logTestInfo("log contents are not correct"); 1.2377 + do_check_eq(compareLogContents, updateLogContents); 1.2378 + } 1.2379 +} 1.2380 + 1.2381 +/** 1.2382 + * Helper function to check if the update log contains a string. 1.2383 + * 1.2384 + * @param aCheckString 1.2385 + * The string to check if the update log contains. 1.2386 + */ 1.2387 +function checkUpdateLogContains(aCheckString) { 1.2388 + let updateLog = getUpdatesPatchDir(); 1.2389 + updateLog.append(FILE_UPDATE_LOG); 1.2390 + let updateLogContents = readFileBytes(updateLog); 1.2391 + if (updateLogContents.indexOf(aCheckString) != -1) { 1.2392 + logTestInfo("log file does contain: " + aCheckString); 1.2393 + do_check_true(true); 1.2394 + } else { 1.2395 + logTestInfo("log file does not contain: " + aCheckString); 1.2396 + logTestInfo("log file contents:\n" + updateLogContents); 1.2397 + do_check_true(false); 1.2398 + } 1.2399 +} 1.2400 + 1.2401 +/** 1.2402 + * Helper function for updater binary tests for verifying the state of files and 1.2403 + * directories after a successful update. 1.2404 + */ 1.2405 +function checkFilesAfterUpdateSuccess() { 1.2406 + logTestInfo("testing contents of files after a successful update"); 1.2407 + gTestFiles.forEach(function CFAUS_TF_FE(aTestFile) { 1.2408 + let testFile = getTargetDirFile(aTestFile.relPathDir + aTestFile.fileName, 1.2409 + true); 1.2410 + logTestInfo("testing file: " + testFile.path); 1.2411 + if (aTestFile.compareFile || aTestFile.compareContents) { 1.2412 + do_check_true(testFile.exists()); 1.2413 + 1.2414 + // Skip these tests on Windows and OS/2 since their 1.2415 + // implementaions of chmod doesn't really set permissions. 1.2416 + if (!IS_WIN && aTestFile.comparePerms) { 1.2417 + // Check if the permssions as set in the complete mar file are correct. 1.2418 + let logPerms = "testing file permissions - "; 1.2419 + if (aTestFile.originalPerms) { 1.2420 + logPerms += "original permissions: " + 1.2421 + aTestFile.originalPerms.toString(8) + ", "; 1.2422 + } 1.2423 + logPerms += "compare permissions : " + 1.2424 + aTestFile.comparePerms.toString(8) + ", "; 1.2425 + logPerms += "updated permissions : " + testFile.permissions.toString(8); 1.2426 + logTestInfo(logPerms); 1.2427 + do_check_eq(testFile.permissions & 0xfff, aTestFile.comparePerms & 0xfff); 1.2428 + } 1.2429 + 1.2430 + let fileContents1 = readFileBytes(testFile); 1.2431 + let fileContents2 = aTestFile.compareFile ? 1.2432 + readFileBytes(getTestDirFile(aTestFile.compareFile)) : 1.2433 + aTestFile.compareContents; 1.2434 + // Don't write the contents of the file to the log to reduce log spam 1.2435 + // unless there is a failure. 1.2436 + if (fileContents1 == fileContents2) { 1.2437 + logTestInfo("file contents are correct"); 1.2438 + do_check_true(true); 1.2439 + } else { 1.2440 + logTestInfo("file contents are not correct"); 1.2441 + do_check_eq(fileContents1, fileContents2); 1.2442 + } 1.2443 + } else { 1.2444 + do_check_false(testFile.exists()); 1.2445 + } 1.2446 + }); 1.2447 + 1.2448 + logTestInfo("testing operations specified in removed-files were performed " + 1.2449 + "after a successful update"); 1.2450 + gTestDirs.forEach(function CFAUS_TD_FE(aTestDir) { 1.2451 + let testDir = getTargetDirFile(aTestDir.relPathDir, true); 1.2452 + logTestInfo("testing directory: " + testDir.path); 1.2453 + if (aTestDir.dirRemoved) { 1.2454 + do_check_false(testDir.exists()); 1.2455 + } else { 1.2456 + do_check_true(testDir.exists()); 1.2457 + 1.2458 + if (aTestDir.files) { 1.2459 + aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) { 1.2460 + let testFile = getTargetDirFile(aTestDir.relPathDir + aTestFile, true); 1.2461 + logTestInfo("testing directory file: " + testFile.path); 1.2462 + if (aTestDir.filesRemoved) { 1.2463 + do_check_false(testFile.exists()); 1.2464 + } else { 1.2465 + do_check_true(testFile.exists()); 1.2466 + } 1.2467 + }); 1.2468 + } 1.2469 + 1.2470 + if (aTestDir.subDirs) { 1.2471 + aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) { 1.2472 + let testSubDir = getTargetDirFile(aTestDir.relPathDir + aSubDir, true); 1.2473 + logTestInfo("testing sub-directory: " + testSubDir.path); 1.2474 + do_check_true(testSubDir.exists()); 1.2475 + if (aTestDir.subDirFiles) { 1.2476 + aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) { 1.2477 + let testFile = getTargetDirFile(aTestDir.relPathDir + aSubDir + aTestFile, true); 1.2478 + logTestInfo("testing sub-directory file: " + testFile.path); 1.2479 + do_check_true(testFile.exists()); 1.2480 + }); 1.2481 + } 1.2482 + }); 1.2483 + } 1.2484 + } 1.2485 + }); 1.2486 + 1.2487 + checkFilesAfterUpdateCommon(); 1.2488 +} 1.2489 + 1.2490 +/** 1.2491 + * Helper function for updater binary tests for verifying the state of files and 1.2492 + * directories after a failed update. 1.2493 + * 1.2494 + * @param aGetDirectory: the function used to get the files in the target directory. 1.2495 + * Pass getApplyDirFile if you want to test the case of a failed switch request. 1.2496 + */ 1.2497 +function checkFilesAfterUpdateFailure(aGetDirectory) { 1.2498 + let getdir = aGetDirectory || getTargetDirFile; 1.2499 + logTestInfo("testing contents of files after a failed update"); 1.2500 + gTestFiles.forEach(function CFAUF_TF_FE(aTestFile) { 1.2501 + let testFile = getdir(aTestFile.relPathDir + aTestFile.fileName, true); 1.2502 + logTestInfo("testing file: " + testFile.path); 1.2503 + if (aTestFile.compareFile || aTestFile.compareContents) { 1.2504 + do_check_true(testFile.exists()); 1.2505 + 1.2506 + // Skip these tests on Windows and OS/2 since their 1.2507 + // implementaions of chmod doesn't really set permissions. 1.2508 + if (!IS_WIN && aTestFile.comparePerms) { 1.2509 + // Check the original permssions are retained on the file. 1.2510 + let logPerms = "testing file permissions - "; 1.2511 + if (aTestFile.originalPerms) { 1.2512 + logPerms += "original permissions: " + 1.2513 + aTestFile.originalPerms.toString(8) + ", "; 1.2514 + } 1.2515 + logPerms += "compare permissions : " + 1.2516 + aTestFile.comparePerms.toString(8) + ", "; 1.2517 + logPerms += "updated permissions : " + testFile.permissions.toString(8); 1.2518 + logTestInfo(logPerms); 1.2519 + do_check_eq(testFile.permissions & 0xfff, aTestFile.comparePerms & 0xfff); 1.2520 + } 1.2521 + 1.2522 + let fileContents1 = readFileBytes(testFile); 1.2523 + let fileContents2 = aTestFile.compareFile ? 1.2524 + readFileBytes(getTestDirFile(aTestFile.compareFile)) : 1.2525 + aTestFile.compareContents; 1.2526 + // Don't write the contents of the file to the log to reduce log spam 1.2527 + // unless there is a failure. 1.2528 + if (fileContents1 == fileContents2) { 1.2529 + logTestInfo("file contents are correct"); 1.2530 + do_check_true(true); 1.2531 + } else { 1.2532 + logTestInfo("file contents are not correct"); 1.2533 + do_check_eq(fileContents1, fileContents2); 1.2534 + } 1.2535 + } else { 1.2536 + do_check_false(testFile.exists()); 1.2537 + } 1.2538 + }); 1.2539 + 1.2540 + logTestInfo("testing operations specified in removed-files were not " + 1.2541 + "performed after a failed update"); 1.2542 + gTestDirs.forEach(function CFAUF_TD_FE(aTestDir) { 1.2543 + let testDir = getdir(aTestDir.relPathDir, true); 1.2544 + logTestInfo("testing directory: " + testDir.path); 1.2545 + do_check_true(testDir.exists()); 1.2546 + 1.2547 + if (aTestDir.files) { 1.2548 + aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) { 1.2549 + let testFile = getdir(aTestDir.relPathDir + aTestFile, true); 1.2550 + logTestInfo("testing directory file: " + testFile.path); 1.2551 + do_check_true(testFile.exists()); 1.2552 + }); 1.2553 + } 1.2554 + 1.2555 + if (aTestDir.subDirs) { 1.2556 + aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) { 1.2557 + let testSubDir = getdir(aTestDir.relPathDir + aSubDir, true); 1.2558 + logTestInfo("testing sub-directory: " + testSubDir.path); 1.2559 + do_check_true(testSubDir.exists()); 1.2560 + if (aTestDir.subDirFiles) { 1.2561 + aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) { 1.2562 + let testFile = getdir(aTestDir.relPathDir + aSubDir + aTestFile, 1.2563 + true); 1.2564 + logTestInfo("testing sub-directory file: " + testFile.path); 1.2565 + do_check_true(testFile.exists()); 1.2566 + }); 1.2567 + } 1.2568 + }); 1.2569 + } 1.2570 + }); 1.2571 + 1.2572 + checkFilesAfterUpdateCommon(); 1.2573 +} 1.2574 + 1.2575 +/** 1.2576 + * Helper function for updater binary tests for verifying the state of common 1.2577 + * files and directories after a successful or failed update. 1.2578 + */ 1.2579 +function checkFilesAfterUpdateCommon() { 1.2580 + logTestInfo("testing extra directories"); 1.2581 + gTestExtraDirs.forEach(function CFAUC_TED_FE(aTestExtraDir) { 1.2582 + let testDir = getTargetDirFile(aTestExtraDir.relPathDir, true); 1.2583 + logTestInfo("testing directory: " + testDir.path); 1.2584 + if (aTestExtraDir.dirExists) { 1.2585 + do_check_true(testDir.exists()); 1.2586 + } else { 1.2587 + do_check_false(testDir.exists()); 1.2588 + } 1.2589 + }); 1.2590 + 1.2591 + logTestInfo("testing updating directory doesn't exist in the application " + 1.2592 + "directory"); 1.2593 + let updatingDir = getTargetDirFile("updating", true); 1.2594 + do_check_false(updatingDir.exists()); 1.2595 + 1.2596 + if (gStageUpdate) { 1.2597 + logTestInfo("testing updating directory doesn't exist in the updated " + 1.2598 + "directory"); 1.2599 + updatingDir = getApplyDirFile("updating", true); 1.2600 + do_check_false(updatingDir.exists()); 1.2601 + 1.2602 + // This should never exist since the update was applied to the updated 1.2603 + // directory and the files should never be in use. 1.2604 + logTestInfo("testing tobedeleted directory doesn't exist in the updated " + 1.2605 + "directory"); 1.2606 + let toBeDeletedDir = getApplyDirFile(DIR_TOBEDELETED, true); 1.2607 + do_check_false(toBeDeletedDir.exists()); 1.2608 + } 1.2609 + 1.2610 + logTestInfo("testing patch files should not be left behind"); 1.2611 + let updatesDir = getUpdatesPatchDir(); 1.2612 + let entries = updatesDir.QueryInterface(AUS_Ci.nsIFile).directoryEntries; 1.2613 + while (entries.hasMoreElements()) { 1.2614 + let entry = entries.getNext().QueryInterface(AUS_Ci.nsIFile); 1.2615 + do_check_neq(getFileExtension(entry), "patch"); 1.2616 + } 1.2617 + 1.2618 + logTestInfo("testing backup files should not be left behind"); 1.2619 + let applyToDir = getTargetDirFile(null, true); 1.2620 + checkFilesInDirRecursive(applyToDir, checkForBackupFiles); 1.2621 +} 1.2622 + 1.2623 +/** 1.2624 + * Helper function for updater binary tests for verifying the contents of the 1.2625 + * updater callback application log which should contain the arguments passed to 1.2626 + * the callback application. 1.2627 + */ 1.2628 +function checkCallbackAppLog() { 1.2629 + let appLaunchLog = getApplyDirFile("a/b/" + gCallbackArgs[1], true); 1.2630 + if (!appLaunchLog.exists()) { 1.2631 + do_timeout(TEST_HELPER_TIMEOUT, checkCallbackAppLog); 1.2632 + return; 1.2633 + } 1.2634 + 1.2635 + let expectedLogContents = gCallbackArgs.join("\n") + "\n"; 1.2636 + let logContents = readFile(appLaunchLog); 1.2637 + // It is possible for the log file contents check to occur before the log file 1.2638 + // contents are completely written so wait until the contents are the expected 1.2639 + // value. If the contents are never the expected value then the test will 1.2640 + // fail by timing out. 1.2641 + if (logContents != expectedLogContents) { 1.2642 + do_timeout(TEST_HELPER_TIMEOUT, checkCallbackAppLog); 1.2643 + return; 1.2644 + } 1.2645 + 1.2646 + if (logContents == expectedLogContents) { 1.2647 + logTestInfo("callback log file contents are correct"); 1.2648 + do_check_true(true); 1.2649 + } else { 1.2650 + logTestInfo("callback log file contents are not correct"); 1.2651 + do_check_eq(logContents, expectedLogContents); 1.2652 + } 1.2653 + 1.2654 + waitForFilesInUse(); 1.2655 +} 1.2656 + 1.2657 +/** 1.2658 + * Helper function for updater service tests for verifying the contents of the 1.2659 + * updater callback application log which should contain the arguments passed to 1.2660 + * the callback application. 1.2661 + */ 1.2662 +function checkCallbackServiceLog() { 1.2663 + do_check_neq(gServiceLaunchedCallbackLog, null); 1.2664 + 1.2665 + let expectedLogContents = gServiceLaunchedCallbackArgs.join("\n") + "\n"; 1.2666 + let logFile = AUS_Cc["@mozilla.org/file/local;1"].createInstance(AUS_Ci.nsILocalFile); 1.2667 + logFile.initWithPath(gServiceLaunchedCallbackLog); 1.2668 + let logContents = readFile(logFile); 1.2669 + // It is possible for the log file contents check to occur before the log file 1.2670 + // contents are completely written so wait until the contents are the expected 1.2671 + // value. If the contents are never the expected value then the test will 1.2672 + // fail by timing out. 1.2673 + if (logContents != expectedLogContents) { 1.2674 + logTestInfo("callback service log not expected value, waiting longer"); 1.2675 + do_timeout(TEST_HELPER_TIMEOUT, checkCallbackServiceLog); 1.2676 + return; 1.2677 + } 1.2678 + 1.2679 + logTestInfo("testing that the callback application successfully launched " + 1.2680 + "and the expected command line arguments were passed to it"); 1.2681 + do_check_eq(logContents, expectedLogContents); 1.2682 + 1.2683 + waitForFilesInUse(); 1.2684 +} 1.2685 + 1.2686 +// Waits until files that are in use that break tests are no longer in use and 1.2687 +// then calls do_test_finished. 1.2688 +function waitForFilesInUse() { 1.2689 + if (IS_WIN) { 1.2690 + let appBin = getApplyDirFile(FILE_APP_BIN, true); 1.2691 + let maintSvcInstaller = getApplyDirFile(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN, true); 1.2692 + let helper = getApplyDirFile("uninstall/helper.exe", true); 1.2693 + let updater = getUpdatesPatchDir(); 1.2694 + updater.append(FILE_UPDATER_BIN); 1.2695 + 1.2696 + let files = [appBin, updater, maintSvcInstaller, helper]; 1.2697 + 1.2698 + for (var i = 0; i < files.length; ++i) { 1.2699 + let file = files[i]; 1.2700 + let fileBak = file.parent.clone(); 1.2701 + if (file.exists()) { 1.2702 + fileBak.append(file.leafName + ".bak"); 1.2703 + try { 1.2704 + if (fileBak.exists()) { 1.2705 + fileBak.remove(false); 1.2706 + } 1.2707 + file.copyTo(fileBak.parent, fileBak.leafName); 1.2708 + file.remove(false); 1.2709 + fileBak.moveTo(file.parent, file.leafName); 1.2710 + logTestInfo("file is not in use. Path: " + file.path); 1.2711 + } catch (e) { 1.2712 + logTestInfo("file in use, will try again after " + TEST_CHECK_TIMEOUT + 1.2713 + " ms, Path: " + file.path + ", Exception: " + e); 1.2714 + try { 1.2715 + if (fileBak.exists()) { 1.2716 + fileBak.remove(false); 1.2717 + } 1.2718 + } catch (e) { 1.2719 + logTestInfo("unable to remove file, this should never happen! " + 1.2720 + "Path: " + fileBak.path + ", Exception: " + e); 1.2721 + } 1.2722 + do_timeout(TEST_CHECK_TIMEOUT, waitForFilesInUse); 1.2723 + return; 1.2724 + } 1.2725 + } 1.2726 + } 1.2727 + } 1.2728 + 1.2729 + logTestInfo("calling doTestFinish"); 1.2730 + doTestFinish(); 1.2731 +} 1.2732 + 1.2733 +/** 1.2734 + * Helper function for updater binary tests for verifying there are no update 1.2735 + * backup files left behind after an update. 1.2736 + * 1.2737 + * @param aFile 1.2738 + * An nsIFile to check if it has moz-backup for its extension. 1.2739 + */ 1.2740 +function checkForBackupFiles(aFile) { 1.2741 + do_check_neq(getFileExtension(aFile), "moz-backup"); 1.2742 +} 1.2743 + 1.2744 +/** 1.2745 + * Helper function for updater binary tests for recursively enumerating a 1.2746 + * directory and calling a callback function with the file as a parameter for 1.2747 + * each file found. 1.2748 + * 1.2749 + * @param aDir 1.2750 + * A nsIFile for the directory to be deleted 1.2751 + * @param aCallback 1.2752 + * A callback function that will be called with the file as a 1.2753 + * parameter for each file found. 1.2754 + */ 1.2755 +function checkFilesInDirRecursive(aDir, aCallback) { 1.2756 + if (!aDir.exists()) 1.2757 + do_throw("Directory must exist!"); 1.2758 + 1.2759 + let dirEntries = aDir.directoryEntries; 1.2760 + while (dirEntries.hasMoreElements()) { 1.2761 + let entry = dirEntries.getNext().QueryInterface(AUS_Ci.nsIFile); 1.2762 + 1.2763 + if (entry.isDirectory()) { 1.2764 + checkFilesInDirRecursive(entry, aCallback); 1.2765 + } else { 1.2766 + aCallback(entry); 1.2767 + } 1.2768 + } 1.2769 +} 1.2770 + 1.2771 +/** 1.2772 + * Sets up the bare bones XMLHttpRequest implementation below. 1.2773 + * 1.2774 + * @param aCallback 1.2775 + * The callback function that will call the nsIDomEventListener's 1.2776 + * handleEvent method. 1.2777 + * 1.2778 + * Example of the callback function 1.2779 + * 1.2780 + * function callHandleEvent() { 1.2781 + * gXHR.status = gExpectedStatus; 1.2782 + * var e = { target: gXHR }; 1.2783 + * gXHR.onload.handleEvent(e); 1.2784 + * } 1.2785 + */ 1.2786 +function overrideXHR(aCallback) { 1.2787 + gXHRCallback = aCallback; 1.2788 + gXHR = new xhr(); 1.2789 + var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); 1.2790 + registrar.registerFactory(gXHR.classID, gXHR.classDescription, 1.2791 + gXHR.contractID, gXHR); 1.2792 +} 1.2793 + 1.2794 + 1.2795 +/** 1.2796 + * Bare bones XMLHttpRequest implementation for testing onprogress, onerror, 1.2797 + * and onload nsIDomEventListener handleEvent. 1.2798 + */ 1.2799 +function makeHandler(aVal) { 1.2800 + if (typeof aVal == "function") 1.2801 + return { handleEvent: aVal }; 1.2802 + return aVal; 1.2803 +} 1.2804 +function xhr() { 1.2805 +} 1.2806 +xhr.prototype = { 1.2807 + overrideMimeType: function(aMimetype) { }, 1.2808 + setRequestHeader: function(aHeader, aValue) { }, 1.2809 + status: null, 1.2810 + channel: { set notificationCallbacks(aVal) { } }, 1.2811 + _url: null, 1.2812 + _method: null, 1.2813 + open: function(aMethod, aUrl) { 1.2814 + gXHR.channel.originalURI = Services.io.newURI(aUrl, null, null); 1.2815 + gXHR._method = aMethod; gXHR._url = aUrl; 1.2816 + }, 1.2817 + responseXML: null, 1.2818 + responseText: null, 1.2819 + send: function(aBody) { 1.2820 + do_execute_soon(gXHRCallback); // Use a timeout so the XHR completes 1.2821 + }, 1.2822 + _onprogress: null, 1.2823 + set onprogress(aValue) { gXHR._onprogress = makeHandler(aValue); }, 1.2824 + get onprogress() { return gXHR._onprogress; }, 1.2825 + _onerror: null, 1.2826 + set onerror(aValue) { gXHR._onerror = makeHandler(aValue); }, 1.2827 + get onerror() { return gXHR._onerror; }, 1.2828 + _onload: null, 1.2829 + set onload(aValue) { gXHR._onload = makeHandler(aValue); }, 1.2830 + get onload() { return gXHR._onload; }, 1.2831 + addEventListener: function(aEvent, aValue, aCapturing) { 1.2832 + eval("gXHR._on" + aEvent + " = aValue"); 1.2833 + }, 1.2834 + flags: AUS_Ci.nsIClassInfo.SINGLETON, 1.2835 + implementationLanguage: AUS_Ci.nsIProgrammingLanguage.JAVASCRIPT, 1.2836 + getHelperForLanguage: function(aLanguage) null, 1.2837 + getInterfaces: function(aCount) { 1.2838 + var interfaces = [AUS_Ci.nsISupports]; 1.2839 + aCount.value = interfaces.length; 1.2840 + return interfaces; 1.2841 + }, 1.2842 + classDescription: "XMLHttpRequest", 1.2843 + contractID: "@mozilla.org/xmlextras/xmlhttprequest;1", 1.2844 + classID: Components.ID("{c9b37f43-4278-4304-a5e0-600991ab08cb}"), 1.2845 + createInstance: function(aOuter, aIID) { 1.2846 + if (aOuter == null) 1.2847 + return gXHR.QueryInterface(aIID); 1.2848 + throw AUS_Cr.NS_ERROR_NO_AGGREGATION; 1.2849 + }, 1.2850 + QueryInterface: function(aIID) { 1.2851 + if (aIID.equals(AUS_Ci.nsIClassInfo) || 1.2852 + aIID.equals(AUS_Ci.nsISupports)) 1.2853 + return gXHR; 1.2854 + throw AUS_Cr.NS_ERROR_NO_INTERFACE; 1.2855 + }, 1.2856 + get wrappedJSObject() { return this; } 1.2857 +}; 1.2858 + 1.2859 +/** 1.2860 + * Helper function to override the update prompt component to verify whether it 1.2861 + * is called or not. 1.2862 + * 1.2863 + * @param aCallback 1.2864 + * The callback to call if the update prompt component is called. 1.2865 + */ 1.2866 +function overrideUpdatePrompt(aCallback) { 1.2867 + var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); 1.2868 + gUpdatePrompt = new UpdatePrompt(); 1.2869 + gUpdatePromptCallback = aCallback; 1.2870 + registrar.registerFactory(gUpdatePrompt.classID, gUpdatePrompt.classDescription, 1.2871 + gUpdatePrompt.contractID, gUpdatePrompt); 1.2872 +} 1.2873 + 1.2874 +function UpdatePrompt() { 1.2875 + var fns = ["checkForUpdates", "showUpdateAvailable", "showUpdateDownloaded", 1.2876 + "showUpdateError", "showUpdateHistory", "showUpdateInstalled"]; 1.2877 + 1.2878 + fns.forEach(function(aPromptFn) { 1.2879 + UpdatePrompt.prototype[aPromptFn] = function() { 1.2880 + if (!gUpdatePromptCallback) { 1.2881 + return; 1.2882 + } 1.2883 + 1.2884 + var callback = gUpdatePromptCallback[aPromptFn]; 1.2885 + if (!callback) { 1.2886 + return; 1.2887 + } 1.2888 + 1.2889 + callback.apply(gUpdatePromptCallback, 1.2890 + Array.prototype.slice.call(arguments)); 1.2891 + } 1.2892 + }); 1.2893 +} 1.2894 + 1.2895 +UpdatePrompt.prototype = { 1.2896 + flags: AUS_Ci.nsIClassInfo.SINGLETON, 1.2897 + implementationLanguage: AUS_Ci.nsIProgrammingLanguage.JAVASCRIPT, 1.2898 + getHelperForLanguage: function(aLanguage) null, 1.2899 + getInterfaces: function(aCount) { 1.2900 + var interfaces = [AUS_Ci.nsISupports, AUS_Ci.nsIUpdatePrompt]; 1.2901 + aCount.value = interfaces.length; 1.2902 + return interfaces; 1.2903 + }, 1.2904 + classDescription: "UpdatePrompt", 1.2905 + contractID: "@mozilla.org/updates/update-prompt;1", 1.2906 + classID: Components.ID("{8c350a15-9b90-4622-93a1-4d320308664b}"), 1.2907 + createInstance: function(aOuter, aIID) { 1.2908 + if (aOuter == null) 1.2909 + return gUpdatePrompt.QueryInterface(aIID); 1.2910 + throw AUS_Cr.NS_ERROR_NO_AGGREGATION; 1.2911 + }, 1.2912 + QueryInterface: function(aIID) { 1.2913 + if (aIID.equals(AUS_Ci.nsIClassInfo) || 1.2914 + aIID.equals(AUS_Ci.nsISupports) || 1.2915 + aIID.equals(AUS_Ci.nsIUpdatePrompt)) 1.2916 + return gUpdatePrompt; 1.2917 + throw AUS_Cr.NS_ERROR_NO_INTERFACE; 1.2918 + }, 1.2919 +}; 1.2920 + 1.2921 +/* Update check listener */ 1.2922 +const updateCheckListener = { 1.2923 + onProgress: function UCL_onProgress(aRequest, aPosition, aTotalSize) { 1.2924 + }, 1.2925 + 1.2926 + onCheckComplete: function UCL_onCheckComplete(aRequest, aUpdates, aUpdateCount) { 1.2927 + gRequestURL = aRequest.channel.originalURI.spec; 1.2928 + gUpdateCount = aUpdateCount; 1.2929 + gUpdates = aUpdates; 1.2930 + logTestInfo("url = " + gRequestURL + ", " + 1.2931 + "request.status = " + aRequest.status + ", " + 1.2932 + "update.statusText = " + aRequest.statusText + ", " + 1.2933 + "updateCount = " + aUpdateCount); 1.2934 + // Use a timeout to allow the XHR to complete 1.2935 + do_execute_soon(gCheckFunc); 1.2936 + }, 1.2937 + 1.2938 + onError: function UCL_onError(aRequest, aUpdate) { 1.2939 + gRequestURL = aRequest.channel.originalURI.spec; 1.2940 + gStatusCode = aRequest.status; 1.2941 + 1.2942 + gStatusText = aUpdate.statusText; 1.2943 + logTestInfo("url = " + gRequestURL + ", " + 1.2944 + "request.status = " + gStatusCode + ", " + 1.2945 + "update.statusText = " + gStatusText); 1.2946 + // Use a timeout to allow the XHR to complete 1.2947 + do_execute_soon(gCheckFunc.bind(null, aRequest, aUpdate)); 1.2948 + }, 1.2949 + 1.2950 + QueryInterface: function(aIID) { 1.2951 + if (!aIID.equals(AUS_Ci.nsIUpdateCheckListener) && 1.2952 + !aIID.equals(AUS_Ci.nsISupports)) 1.2953 + throw AUS_Cr.NS_ERROR_NO_INTERFACE; 1.2954 + return this; 1.2955 + } 1.2956 +}; 1.2957 + 1.2958 +/* Update download listener - nsIRequestObserver */ 1.2959 +const downloadListener = { 1.2960 + onStartRequest: function DL_onStartRequest(aRequest, aContext) { 1.2961 + }, 1.2962 + 1.2963 + onProgress: function DL_onProgress(aRequest, aContext, aProgress, aMaxProgress) { 1.2964 + }, 1.2965 + 1.2966 + onStatus: function DL_onStatus(aRequest, aContext, aStatus, aStatusText) { 1.2967 + }, 1.2968 + 1.2969 + onStopRequest: function DL_onStopRequest(aRequest, aContext, aStatus) { 1.2970 + gStatusResult = aStatus; 1.2971 + // Use a timeout to allow the request to complete 1.2972 + do_execute_soon(gCheckFunc); 1.2973 + }, 1.2974 + 1.2975 + QueryInterface: function DL_QueryInterface(aIID) { 1.2976 + if (!aIID.equals(AUS_Ci.nsIRequestObserver) && 1.2977 + !aIID.equals(AUS_Ci.nsIProgressEventSink) && 1.2978 + !aIID.equals(AUS_Ci.nsISupports)) 1.2979 + throw AUS_Cr.NS_ERROR_NO_INTERFACE; 1.2980 + return this; 1.2981 + } 1.2982 +}; 1.2983 + 1.2984 +/** 1.2985 + * Helper for starting the http server used by the tests 1.2986 + */ 1.2987 +function start_httpserver() { 1.2988 + let dir = getTestDirFile(); 1.2989 + logTestInfo("http server directory path: " + dir.path); 1.2990 + 1.2991 + if (!dir.isDirectory()) { 1.2992 + do_throw("A file instead of a directory was specified for HttpServer " + 1.2993 + "registerDirectory! Path: " + dir.path + "\n"); 1.2994 + } 1.2995 + 1.2996 + AUS_Cu.import("resource://testing-common/httpd.js"); 1.2997 + gTestserver = new HttpServer(); 1.2998 + gTestserver.registerDirectory("/", dir); 1.2999 + gTestserver.start(-1); 1.3000 + let testserverPort = gTestserver.identity.primaryPort; 1.3001 + gURLData = URL_HOST + ":" + testserverPort + "/"; 1.3002 + logTestInfo("http server port = " + testserverPort); 1.3003 +} 1.3004 + 1.3005 +/** 1.3006 + * Helper for stopping the http server used by the tests 1.3007 + * 1.3008 + * @param aCallback 1.3009 + * The callback to call after stopping the http server. 1.3010 + */ 1.3011 +function stop_httpserver(aCallback) { 1.3012 + do_check_true(!!aCallback); 1.3013 + gTestserver.stop(aCallback); 1.3014 +} 1.3015 + 1.3016 +/** 1.3017 + * Creates an nsIXULAppInfo 1.3018 + * 1.3019 + * @param aID 1.3020 + * The ID of the test application 1.3021 + * @param aName 1.3022 + * A name for the test application 1.3023 + * @param aVersion 1.3024 + * The version of the application 1.3025 + * @param aPlatformVersion 1.3026 + * The gecko version of the application 1.3027 + */ 1.3028 +function createAppInfo(aID, aName, aVersion, aPlatformVersion) { 1.3029 + const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; 1.3030 + const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}"); 1.3031 + var XULAppInfo = { 1.3032 + vendor: APP_INFO_VENDOR, 1.3033 + name: aName, 1.3034 + ID: aID, 1.3035 + version: aVersion, 1.3036 + appBuildID: "2007010101", 1.3037 + platformVersion: aPlatformVersion, 1.3038 + platformBuildID: "2007010101", 1.3039 + inSafeMode: false, 1.3040 + logConsoleErrors: true, 1.3041 + OS: "XPCShell", 1.3042 + XPCOMABI: "noarch-spidermonkey", 1.3043 + 1.3044 + QueryInterface: function QueryInterface(aIID) { 1.3045 + if (aIID.equals(AUS_Ci.nsIXULAppInfo) || 1.3046 + aIID.equals(AUS_Ci.nsIXULRuntime) || 1.3047 +#ifdef XP_WIN 1.3048 + aIID.equals(AUS_Ci.nsIWinAppHelper) || 1.3049 +#endif 1.3050 + aIID.equals(AUS_Ci.nsISupports)) 1.3051 + return this; 1.3052 + throw AUS_Cr.NS_ERROR_NO_INTERFACE; 1.3053 + } 1.3054 + }; 1.3055 + 1.3056 + var XULAppInfoFactory = { 1.3057 + createInstance: function (aOuter, aIID) { 1.3058 + if (aOuter == null) 1.3059 + return XULAppInfo.QueryInterface(aIID); 1.3060 + throw AUS_Cr.NS_ERROR_NO_AGGREGATION; 1.3061 + } 1.3062 + }; 1.3063 + 1.3064 + var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); 1.3065 + registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo", 1.3066 + XULAPPINFO_CONTRACTID, XULAppInfoFactory); 1.3067 +} 1.3068 + 1.3069 +/** 1.3070 + * Returns the platform specific arguments used by nsIProcess when launching 1.3071 + * the application. 1.3072 + * 1.3073 + * @param aExtraArgs (optional) 1.3074 + * An array of extra arguments to append to the default arguments. 1.3075 + * @return an array of arguments to be passed to nsIProcess. 1.3076 + * 1.3077 + * Note: a shell is necessary to pipe the application's console output which 1.3078 + * would otherwise pollute the xpcshell log. 1.3079 + * 1.3080 + * Command line arguments used when launching the application: 1.3081 + * -no-remote prevents shell integration from being affected by an existing 1.3082 + * application process. 1.3083 + * -process-updates makes the application exits after being relaunched by the 1.3084 + * updater. 1.3085 + * the platform specific string defined by PIPE_TO_NULL to output both stdout 1.3086 + * and stderr to null. This is needed to prevent output from the application 1.3087 + * from ending up in the xpchsell log. 1.3088 + */ 1.3089 +function getProcessArgs(aExtraArgs) { 1.3090 + if (!aExtraArgs) { 1.3091 + aExtraArgs = []; 1.3092 + } 1.3093 + 1.3094 + let appBinPath = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false).path; 1.3095 + if (/ /.test(appBinPath)) { 1.3096 + appBinPath = '"' + appBinPath + '"'; 1.3097 + } 1.3098 + 1.3099 + let args; 1.3100 + if (IS_UNIX) { 1.3101 + let launchScript = getLaunchScript(); 1.3102 + // Precreate the script with executable permissions 1.3103 + launchScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY); 1.3104 + 1.3105 + let scriptContents = "#! /bin/sh\n"; 1.3106 + scriptContents += appBinPath + " -no-remote -process-updates " + 1.3107 + aExtraArgs.join(" ") + " " + PIPE_TO_NULL; 1.3108 + writeFile(launchScript, scriptContents); 1.3109 + logTestInfo("created " + launchScript.path + " containing:\n" + 1.3110 + scriptContents); 1.3111 + args = [launchScript.path]; 1.3112 + } else { 1.3113 + args = ["/D", "/Q", "/C", appBinPath, "-no-remote", "-process-updates"]. 1.3114 + concat(aExtraArgs).concat([PIPE_TO_NULL]); 1.3115 + } 1.3116 + return args; 1.3117 +} 1.3118 + 1.3119 +/** 1.3120 + * Gets a file path for the application to dump its arguments into. This is used 1.3121 + * to verify that a callback application is launched. 1.3122 + * 1.3123 + * @return the file for the application to dump its arguments into. 1.3124 + */ 1.3125 +function getAppArgsLogPath() { 1.3126 + let appArgsLog = do_get_file("/", true); 1.3127 + appArgsLog.append(gTestID + "_app_args_log"); 1.3128 + if (appArgsLog.exists()) { 1.3129 + appArgsLog.remove(false); 1.3130 + } 1.3131 + let appArgsLogPath = appArgsLog.path; 1.3132 + if (/ /.test(appArgsLogPath)) { 1.3133 + appArgsLogPath = '"' + appArgsLogPath + '"'; 1.3134 + } 1.3135 + return appArgsLogPath; 1.3136 +} 1.3137 + 1.3138 +/** 1.3139 + * Gets the nsIFile reference for the shell script to launch the application. If 1.3140 + * the file exists it will be removed by this function. 1.3141 + * 1.3142 + * @return the nsIFile for the shell script to launch the application. 1.3143 + */ 1.3144 +function getLaunchScript() { 1.3145 + let launchScript = do_get_file("/", true); 1.3146 + launchScript.append(gTestID + "_launch.sh"); 1.3147 + if (launchScript.exists()) { 1.3148 + launchScript.remove(false); 1.3149 + } 1.3150 + return launchScript; 1.3151 +} 1.3152 + 1.3153 +/** 1.3154 + * Makes GreD, XREExeF, and UpdRootD point to unique file system locations so 1.3155 + * xpcshell tests can run in parallel and to keep the environment clean. 1.3156 + */ 1.3157 +function adjustGeneralPaths() { 1.3158 + let dirProvider = { 1.3159 + getFile: function AGP_DP_getFile(aProp, aPersistent) { 1.3160 + aPersistent.value = true; 1.3161 + switch (aProp) { 1.3162 + case NS_GRE_DIR: 1.3163 + if (gUseTestAppDir) { 1.3164 + return getApplyDirFile(DIR_BIN_REL_PATH, true); 1.3165 + } 1.3166 + break; 1.3167 + case XRE_EXECUTABLE_FILE: 1.3168 + if (gUseTestAppDir) { 1.3169 + return getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, true); 1.3170 + } 1.3171 + break; 1.3172 + case XRE_UPDATE_ROOT_DIR: 1.3173 + return getMockUpdRootD(); 1.3174 + } 1.3175 + return null; 1.3176 + }, 1.3177 + QueryInterface: function(aIID) { 1.3178 + if (aIID.equals(AUS_Ci.nsIDirectoryServiceProvider) || 1.3179 + aIID.equals(AUS_Ci.nsISupports)) 1.3180 + return this; 1.3181 + throw AUS_Cr.NS_ERROR_NO_INTERFACE; 1.3182 + } 1.3183 + }; 1.3184 + let ds = Services.dirsvc.QueryInterface(AUS_Ci.nsIDirectoryService); 1.3185 + ds.QueryInterface(AUS_Ci.nsIProperties).undefine(NS_GRE_DIR); 1.3186 + ds.QueryInterface(AUS_Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE); 1.3187 + ds.registerProvider(dirProvider); 1.3188 + do_register_cleanup(function AGP_cleanup() { 1.3189 + logTestInfo("start - unregistering directory provider"); 1.3190 + 1.3191 + if (gAppTimer) { 1.3192 + logTestInfo("start - cancel app timer"); 1.3193 + gAppTimer.cancel(); 1.3194 + gAppTimer = null; 1.3195 + logTestInfo("finish - cancel app timer"); 1.3196 + } 1.3197 + 1.3198 + if (gProcess && gProcess.isRunning) { 1.3199 + logTestInfo("start - kill process"); 1.3200 + try { 1.3201 + gProcess.kill(); 1.3202 + } catch (e) { 1.3203 + logTestInfo("kill process failed. Exception: " + e); 1.3204 + } 1.3205 + gProcess = null; 1.3206 + logTestInfo("finish - kill process"); 1.3207 + } 1.3208 + 1.3209 + if (gHandle) { 1.3210 + try { 1.3211 + logTestInfo("start - closing handle"); 1.3212 + let kernel32 = ctypes.open("kernel32"); 1.3213 + let CloseHandle = kernel32.declare("CloseHandle", ctypes.default_abi, 1.3214 + ctypes.bool, /*return*/ 1.3215 + ctypes.voidptr_t /*handle*/); 1.3216 + if (!CloseHandle(gHandle)) { 1.3217 + logTestInfo("call to CloseHandle failed"); 1.3218 + } 1.3219 + kernel32.close(); 1.3220 + gHandle = null; 1.3221 + logTestInfo("finish - closing handle"); 1.3222 + } catch (e) { 1.3223 + logTestInfo("call to CloseHandle failed. Exception: " + e); 1.3224 + } 1.3225 + } 1.3226 + 1.3227 + // Call end_test first before the directory provider is unregistered 1.3228 + if (typeof(end_test) == typeof(Function)) { 1.3229 + logTestInfo("calling end_test"); 1.3230 + end_test(); 1.3231 + } 1.3232 + 1.3233 + ds.unregisterProvider(dirProvider); 1.3234 + cleanupTestCommon(); 1.3235 + 1.3236 + logTestInfo("finish - unregistering directory provider"); 1.3237 + }); 1.3238 +} 1.3239 + 1.3240 + 1.3241 +/** 1.3242 + * Helper function for launching the application to apply an update. 1.3243 + */ 1.3244 +function launchAppToApplyUpdate() { 1.3245 + logTestInfo("start - launching application to apply update"); 1.3246 + 1.3247 + let appBin = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false); 1.3248 + 1.3249 + if (typeof(customLaunchAppToApplyUpdate) == typeof(Function)) { 1.3250 + customLaunchAppToApplyUpdate(); 1.3251 + } 1.3252 + 1.3253 + let launchBin = getLaunchBin(); 1.3254 + let args = getProcessArgs(); 1.3255 + logTestInfo("launching " + launchBin.path + " " + args.join(" ")); 1.3256 + 1.3257 + gProcess = AUS_Cc["@mozilla.org/process/util;1"]. 1.3258 + createInstance(AUS_Ci.nsIProcess); 1.3259 + gProcess.init(launchBin); 1.3260 + 1.3261 + gAppTimer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer); 1.3262 + gAppTimer.initWithCallback(gTimerCallback, APP_TIMER_TIMEOUT, 1.3263 + AUS_Ci.nsITimer.TYPE_ONE_SHOT); 1.3264 + 1.3265 + setEnvironment(); 1.3266 + logTestInfo("launching application"); 1.3267 + gProcess.runAsync(args, args.length, gProcessObserver); 1.3268 + resetEnvironment(); 1.3269 + 1.3270 + logTestInfo("finish - launching application to apply update"); 1.3271 +} 1.3272 + 1.3273 +/** 1.3274 + * The observer for the call to nsIProcess:runAsync. 1.3275 + */ 1.3276 +let gProcessObserver = { 1.3277 + observe: function PO_observe(aSubject, aTopic, aData) { 1.3278 + logTestInfo("topic: " + aTopic + ", process exitValue: " + 1.3279 + gProcess.exitValue); 1.3280 + if (gAppTimer) { 1.3281 + gAppTimer.cancel(); 1.3282 + gAppTimer = null; 1.3283 + } 1.3284 + if (aTopic != "process-finished" || gProcess.exitValue != 0) { 1.3285 + do_throw("Failed to launch application"); 1.3286 + } 1.3287 + do_timeout(TEST_CHECK_TIMEOUT, checkUpdateFinished); 1.3288 + }, 1.3289 + QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIObserver]) 1.3290 +}; 1.3291 + 1.3292 +/** 1.3293 + * The timer callback to kill the process if it takes too long. 1.3294 + */ 1.3295 +let gTimerCallback = { 1.3296 + notify: function TC_notify(aTimer) { 1.3297 + gAppTimer = null; 1.3298 + if (gProcess.isRunning) { 1.3299 + logTestInfo("attempt to kill process"); 1.3300 + gProcess.kill(); 1.3301 + } 1.3302 + do_throw("launch application timer expired"); 1.3303 + }, 1.3304 + QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsITimerCallback]) 1.3305 +}; 1.3306 + 1.3307 +/** 1.3308 + * The update-staged observer for the call to nsIUpdateProcessor:processUpdate. 1.3309 + */ 1.3310 +let gUpdateStagedObserver = { 1.3311 + observe: function(aSubject, aTopic, aData) { 1.3312 + if (aTopic == "update-staged") { 1.3313 + Services.obs.removeObserver(gUpdateStagedObserver, "update-staged"); 1.3314 + checkUpdateApplied(); 1.3315 + } 1.3316 + }, 1.3317 + QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIObserver]) 1.3318 +}; 1.3319 + 1.3320 +/** 1.3321 + * Sets the environment that will be used by the application process when it is 1.3322 + * launched. 1.3323 + */ 1.3324 +function setEnvironment() { 1.3325 + // Prevent setting the environment more than once. 1.3326 + if (gShouldResetEnv !== undefined) 1.3327 + return; 1.3328 + 1.3329 + gShouldResetEnv = true; 1.3330 + 1.3331 + let env = AUS_Cc["@mozilla.org/process/environment;1"]. 1.3332 + getService(AUS_Ci.nsIEnvironment); 1.3333 + if (IS_WIN && !env.exists("XRE_NO_WINDOWS_CRASH_DIALOG")) { 1.3334 + gAddedEnvXRENoWindowsCrashDialog = true; 1.3335 + logTestInfo("setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " + 1.3336 + "variable to 1... previously it didn't exist"); 1.3337 + env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1"); 1.3338 + } 1.3339 + 1.3340 + if (IS_UNIX) { 1.3341 + let appGreDir = gGREDirOrig.clone(); 1.3342 + let envGreDir = AUS_Cc["@mozilla.org/file/local;1"]. 1.3343 + createInstance(AUS_Ci.nsILocalFile); 1.3344 + let shouldSetEnv = true; 1.3345 + if (IS_MACOSX) { 1.3346 + if (env.exists("DYLD_LIBRARY_PATH")) { 1.3347 + gEnvDyldLibraryPath = env.get("DYLD_LIBRARY_PATH"); 1.3348 + envGreDir.initWithPath(gEnvDyldLibraryPath); 1.3349 + if (envGreDir.path == appGreDir.path) { 1.3350 + gEnvDyldLibraryPath = null; 1.3351 + shouldSetEnv = false; 1.3352 + } 1.3353 + } 1.3354 + 1.3355 + if (shouldSetEnv) { 1.3356 + logTestInfo("setting DYLD_LIBRARY_PATH environment variable value to " + 1.3357 + appGreDir.path); 1.3358 + env.set("DYLD_LIBRARY_PATH", appGreDir.path); 1.3359 + } 1.3360 + } else { 1.3361 + if (env.exists("LD_LIBRARY_PATH")) { 1.3362 + gEnvLdLibraryPath = env.get("LD_LIBRARY_PATH"); 1.3363 + envGreDir.initWithPath(gEnvLdLibraryPath); 1.3364 + if (envGreDir.path == appGreDir.path) { 1.3365 + gEnvLdLibraryPath = null; 1.3366 + shouldSetEnv = false; 1.3367 + } 1.3368 + } 1.3369 + 1.3370 + if (shouldSetEnv) { 1.3371 + logTestInfo("setting LD_LIBRARY_PATH environment variable value to " + 1.3372 + appGreDir.path); 1.3373 + env.set("LD_LIBRARY_PATH", appGreDir.path); 1.3374 + } 1.3375 + } 1.3376 + } 1.3377 + 1.3378 + if (env.exists("XPCOM_MEM_LEAK_LOG")) { 1.3379 + gEnvXPCOMMemLeakLog = env.get("XPCOM_MEM_LEAK_LOG"); 1.3380 + logTestInfo("removing the XPCOM_MEM_LEAK_LOG environment variable... " + 1.3381 + "previous value " + gEnvXPCOMMemLeakLog); 1.3382 + env.set("XPCOM_MEM_LEAK_LOG", ""); 1.3383 + } 1.3384 + 1.3385 + if (env.exists("XPCOM_DEBUG_BREAK")) { 1.3386 + gEnvXPCOMDebugBreak = env.get("XPCOM_DEBUG_BREAK"); 1.3387 + logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " + 1.3388 + "warn... previous value " + gEnvXPCOMDebugBreak); 1.3389 + } else { 1.3390 + logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " + 1.3391 + "warn... previously it didn't exist"); 1.3392 + } 1.3393 + 1.3394 + env.set("XPCOM_DEBUG_BREAK", "warn"); 1.3395 + 1.3396 + if (gStageUpdate) { 1.3397 + logTestInfo("setting the MOZ_UPDATE_STAGING environment variable to 1\n"); 1.3398 + env.set("MOZ_UPDATE_STAGING", "1"); 1.3399 + } 1.3400 + 1.3401 + logTestInfo("setting MOZ_NO_SERVICE_FALLBACK environment variable to 1"); 1.3402 + env.set("MOZ_NO_SERVICE_FALLBACK", "1"); 1.3403 +} 1.3404 + 1.3405 +/** 1.3406 + * Sets the environment back to the original values after launching the 1.3407 + * application. 1.3408 + */ 1.3409 +function resetEnvironment() { 1.3410 + // Prevent resetting the environment more than once. 1.3411 + if (gShouldResetEnv !== true) 1.3412 + return; 1.3413 + 1.3414 + gShouldResetEnv = false; 1.3415 + 1.3416 + let env = AUS_Cc["@mozilla.org/process/environment;1"]. 1.3417 + getService(AUS_Ci.nsIEnvironment); 1.3418 + 1.3419 + if (gEnvXPCOMMemLeakLog) { 1.3420 + logTestInfo("setting the XPCOM_MEM_LEAK_LOG environment variable back to " + 1.3421 + gEnvXPCOMMemLeakLog); 1.3422 + env.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog); 1.3423 + } 1.3424 + 1.3425 + if (gEnvXPCOMDebugBreak) { 1.3426 + logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable back to " + 1.3427 + gEnvXPCOMDebugBreak); 1.3428 + env.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak); 1.3429 + } else { 1.3430 + logTestInfo("clearing the XPCOM_DEBUG_BREAK environment variable"); 1.3431 + env.set("XPCOM_DEBUG_BREAK", ""); 1.3432 + } 1.3433 + 1.3434 + if (IS_UNIX) { 1.3435 + if (IS_MACOSX) { 1.3436 + if (gEnvDyldLibraryPath) { 1.3437 + logTestInfo("setting DYLD_LIBRARY_PATH environment variable value " + 1.3438 + "back to " + gEnvDyldLibraryPath); 1.3439 + env.set("DYLD_LIBRARY_PATH", gEnvDyldLibraryPath); 1.3440 + } else { 1.3441 + logTestInfo("removing DYLD_LIBRARY_PATH environment variable"); 1.3442 + env.set("DYLD_LIBRARY_PATH", ""); 1.3443 + } 1.3444 + } else { 1.3445 + if (gEnvLdLibraryPath) { 1.3446 + logTestInfo("setting LD_LIBRARY_PATH environment variable value back " + 1.3447 + "to " + gEnvLdLibraryPath); 1.3448 + env.set("LD_LIBRARY_PATH", gEnvLdLibraryPath); 1.3449 + } else { 1.3450 + logTestInfo("removing LD_LIBRARY_PATH environment variable"); 1.3451 + env.set("LD_LIBRARY_PATH", ""); 1.3452 + } 1.3453 + } 1.3454 + } 1.3455 + 1.3456 + if (IS_WIN && gAddedEnvXRENoWindowsCrashDialog) { 1.3457 + logTestInfo("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment " + 1.3458 + "variable"); 1.3459 + env.set("XRE_NO_WINDOWS_CRASH_DIALOG", ""); 1.3460 + } 1.3461 + 1.3462 + if (gStageUpdate) { 1.3463 + logTestInfo("removing the MOZ_UPDATE_STAGING environment variable\n"); 1.3464 + env.set("MOZ_UPDATE_STAGING", ""); 1.3465 + } 1.3466 + 1.3467 + logTestInfo("removing MOZ_NO_SERVICE_FALLBACK environment variable"); 1.3468 + env.set("MOZ_NO_SERVICE_FALLBACK", ""); 1.3469 +}