toolkit/mozapps/update/tests/unit_aus_update/head_update.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 const INSTALL_LOCALE = "@AB_CD@";
     6 const MOZ_APP_NAME = "@MOZ_APP_NAME@";
     7 const BIN_SUFFIX = "@BIN_SUFFIX@";
     9 #ifdef XP_WIN
    10 // MOZ_APP_VENDOR is optional.
    11 // On Windows, if MOZ_APP_VENDOR is not defined the updates directory will be
    12 // located under %LOCALAPPDATA%\@MOZ_APP_BASENAME@\updates\TaskBarID
    13 #ifdef MOZ_APP_VENDOR
    14 const MOZ_APP_VENDOR = "@MOZ_APP_VENDOR@";
    15 #else
    16 const MOZ_APP_VENDOR = "";
    17 #endif
    19 // MOZ_APP_BASENAME is not optional for tests.
    20 const MOZ_APP_BASENAME = "@MOZ_APP_BASENAME@";
    21 #endif // XP_WIN
    23 const APP_INFO_NAME = "XPCShell";
    24 const APP_INFO_VENDOR = "Mozilla";
    26 #ifdef XP_UNIX
    27 const APP_BIN_SUFFIX = "-bin";
    28 #else
    29 const APP_BIN_SUFFIX = "@BIN_SUFFIX@";
    30 #endif
    32 #ifdef XP_WIN
    33 const IS_WIN = true;
    34 #else
    35 const IS_WIN = false;
    36 #endif
    38 #ifdef XP_MACOSX
    39 const IS_MACOSX = true;
    40 #ifdef MOZ_SHARK
    41 const IS_SHARK = true;
    42 #else
    43 const IS_SHARK = false;
    44 #endif
    45 #else
    46 const IS_MACOSX = false;
    47 #endif
    49 #ifdef XP_UNIX
    50 const IS_UNIX = true;
    51 #else
    52 const IS_UNIX = false;
    53 #endif
    55 #ifdef ANDROID
    56 const IS_ANDROID = true;
    57 #else
    58 const IS_ANDROID = false;
    59 #endif
    61 #ifdef MOZ_WIDGET_GONK
    62 const IS_TOOLKIT_GONK = true;
    63 #else
    64 const IS_TOOLKIT_GONK = false;
    65 #endif
    67 const USE_EXECV = IS_UNIX && !IS_MACOSX;
    69 #ifdef MOZ_VERIFY_MAR_SIGNATURE
    70 const IS_MAR_CHECKS_ENABLED = true;
    71 #else
    72 const IS_MAR_CHECKS_ENABLED = false;
    73 #endif
    75 const URL_HOST = "http://localhost";
    77 const FILE_APP_BIN = MOZ_APP_NAME + APP_BIN_SUFFIX;
    78 const FILE_COMPLETE_EXE = "complete.exe";
    79 const FILE_COMPLETE_MAR = "complete.mar";
    80 const FILE_HELPER_BIN = "TestAUSHelper" + BIN_SUFFIX;
    81 const FILE_MAINTENANCE_SERVICE_BIN = "maintenanceservice.exe";
    82 const FILE_MAINTENANCE_SERVICE_INSTALLER_BIN = "maintenanceservice_installer.exe";
    83 const FILE_OLD_VERSION_MAR = "old_version.mar";
    84 const FILE_PARTIAL_EXE = "partial.exe";
    85 const FILE_PARTIAL_MAR = "partial.mar";
    86 const FILE_UPDATER_BIN = "updater" + BIN_SUFFIX;
    87 const FILE_UPDATER_INI_BAK = "updater.ini.bak";
    88 const FILE_WRONG_CHANNEL_MAR = "wrong_product_channel.mar";
    90 const LOG_COMPLETE_SUCCESS = "complete_log_success";
    91 const LOG_PARTIAL_SUCCESS  = "partial_log_success";
    92 const LOG_PARTIAL_FAILURE  = "partial_log_failure";
    94 const LOG_SWITCH_SUCCESS = "rename_file: proceeding to rename the directory\n" +
    95                            "rename_file: proceeding to rename the directory\n" +
    96                            "Now, remove the tmpDir\n" +
    97                            "succeeded\n" +
    98                            "calling QuitProgressUI";
   100 const ERR_RENAME_FILE = "rename_file: failed to rename file";
   101 const ERR_UNABLE_OPEN_DEST = "unable to open destination file";
   102 const ERR_BACKUP_DISCARD = "backup_discard: unable to remove";
   104 const LOG_SVC_SUCCESSFUL_LAUNCH = "Process was started... waiting on result.";
   106 // All we care about is that the last modified time has changed so that Mac OS
   107 // X Launch Services invalidates its cache so the test allows up to one minute
   108 // difference in the last modified time.
   109 const MAC_MAX_TIME_DIFFERENCE = 60000;
   111 // Time to wait for the test helper process before continuing the test
   112 const TEST_HELPER_TIMEOUT = 100;
   114 // Time to wait for a check in the test before continuing the test
   115 const TEST_CHECK_TIMEOUT = 100;
   117 // How many of TEST_CHECK_TIMEOUT to wait before we abort the test.
   118 const MAX_TIMEOUT_RUNS = 2000;
   120 // Time in seconds the helper application should sleep.the helper's input and output files
   121 const HELPER_SLEEP_TIMEOUT = 180;
   123 // Maximum number of milliseconds the process that is launched can run before
   124 // the test will try to kill it.
   125 const APP_TIMER_TIMEOUT = 120000;
   127 #ifdef XP_WIN
   128 const PIPE_TO_NULL = ">nul";
   129 #else
   130 const PIPE_TO_NULL = "> /dev/null 2>&1";
   131 #endif
   133 // This default value will be overridden when using the http server.
   134 var gURLData = URL_HOST + "/";
   136 var gTestID;
   138 var gTestserver;
   140 var gRegisteredServiceCleanup;
   142 var gXHR;
   143 var gXHRCallback;
   145 var gUpdatePrompt;
   146 var gUpdatePromptCallback;
   148 var gCheckFunc;
   149 var gResponseBody;
   150 var gResponseStatusCode = 200;
   151 var gRequestURL;
   152 var gUpdateCount;
   153 var gUpdates;
   154 var gStatusCode;
   155 var gStatusText;
   156 var gStatusResult;
   158 var gProcess;
   159 var gAppTimer;
   160 var gHandle;
   162 var gGREDirOrig;
   163 var gAppDirOrig;
   165 var gServiceLaunchedCallbackLog = null;
   166 var gServiceLaunchedCallbackArgs = null;
   168 // Variables are used instead of contants so tests can override these values if
   169 // necessary.
   170 var gCallbackBinFile = "callback_app" + BIN_SUFFIX;
   171 var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"];
   172 var gStageUpdate = false;
   173 var gSwitchApp = false;
   174 var gDisableReplaceFallback = false;
   175 var gUseTestAppDir = true;
   177 var gTimeoutRuns = 0;
   179 // Environment related globals
   180 var gShouldResetEnv = undefined;
   181 var gAddedEnvXRENoWindowsCrashDialog = false;
   182 var gEnvXPCOMDebugBreak;
   183 var gEnvXPCOMMemLeakLog;
   184 var gEnvDyldLibraryPath;
   185 var gEnvLdLibraryPath;
   187 // Set to true to log additional information for debugging. To log additional
   188 // information for an individual test set DEBUG_AUS_TEST to true in the test's
   189 // run_test function.
   190 var DEBUG_AUS_TEST = true;
   191 // Never set DEBUG_TEST_LOG to true except when running tests locally or on the
   192 // try server since this will force a test that failed a parallel run to fail
   193 // when the same test runs non-parallel so the log from parallel test run can
   194 // be displayed in the log.
   195 var DEBUG_TEST_LOG = false;
   196 // Set to false to keep the log file from the failed parallel test run.
   197 var gDeleteLogFile = true;
   198 var gRealDump;
   199 var gTestLogText = "";
   200 var gPassed;
   202 #include ../shared.js
   204 var gTestFiles = [];
   205 var gTestDirs = [];
   207 // Common files for both successful and failed updates.
   208 var gTestFilesCommon = [
   209 {
   210   description      : "Should never change",
   211   fileName         : FILE_UPDATE_SETTINGS_INI,
   212   relPathDir       : "a/b/",
   213   originalContents : "ShouldNotBeReplaced\n",
   214   compareContents  : "ShouldNotBeReplaced\n",
   215   originalFile     : null,
   216   compareFile      : null,
   217   originalPerms    : 0o767,
   218   comparePerms     : 0o767
   219 }, {
   220   description      : "Should never change",
   221   fileName         : "channel-prefs.js",
   222   relPathDir       : "a/b/defaults/pref/",
   223   originalContents : "ShouldNotBeReplaced\n",
   224   compareContents  : "ShouldNotBeReplaced\n",
   225   originalFile     : null,
   226   compareFile      : null,
   227   originalPerms    : 0o767,
   228   comparePerms     : 0o767
   229 }];
   231 // Files for a complete successful update. This can be used for a complete
   232 // failed update by calling setTestFilesAndDirsForFailure.
   233 var gTestFilesCompleteSuccess = [
   234 {
   235   description      : "Added by update.manifest (add)",
   236   fileName         : "precomplete",
   237   relPathDir       : "",
   238   originalContents : null,
   239   compareContents  : null,
   240   originalFile     : "partial_precomplete",
   241   compareFile      : "complete_precomplete",
   242   originalPerms    : 0o666,
   243   comparePerms     : 0o644
   244 }, {
   245   description      : "Added by update.manifest (add)",
   246   fileName         : "searchpluginstext0",
   247   relPathDir       : "a/b/searchplugins/",
   248   originalContents : "ToBeReplacedWithFromComplete\n",
   249   compareContents  : "FromComplete\n",
   250   originalFile     : null,
   251   compareFile      : null,
   252   originalPerms    : 0o775,
   253   comparePerms     : 0o644
   254 }, {
   255   description      : "Added by update.manifest (add)",
   256   fileName         : "searchpluginspng1.png",
   257   relPathDir       : "a/b/searchplugins/",
   258   originalContents : null,
   259   compareContents  : null,
   260   originalFile     : null,
   261   compareFile      : "complete.png",
   262   originalPerms    : null,
   263   comparePerms     : 0o644
   264 }, {
   265   description      : "Added by update.manifest (add)",
   266   fileName         : "searchpluginspng0.png",
   267   relPathDir       : "a/b/searchplugins/",
   268   originalContents : null,
   269   compareContents  : null,
   270   originalFile     : "partial.png",
   271   compareFile      : "complete.png",
   272   originalPerms    : 0o666,
   273   comparePerms     : 0o644
   274 }, {
   275   description      : "Added by update.manifest (add)",
   276   fileName         : "removed-files",
   277   relPathDir       : "a/b/",
   278   originalContents : null,
   279   compareContents  : null,
   280   originalFile     : "partial_removed-files",
   281   compareFile      : "complete_removed-files",
   282   originalPerms    : 0o666,
   283   comparePerms     : 0o644
   284 }, {
   285   description      : "Added by update.manifest if the parent directory " +
   286                      "exists (add-if)",
   287   fileName         : "extensions1text0",
   288   relPathDir       : "a/b/distribution/extensions/extensions1/",
   289   originalContents : null,
   290   compareContents  : "FromComplete\n",
   291   originalFile     : null,
   292   compareFile      : null,
   293   originalPerms    : null,
   294   comparePerms     : 0o644
   295 }, {
   296   description      : "Added by update.manifest if the parent directory " +
   297                      "exists (add-if)",
   298   fileName         : "extensions1png1.png",
   299   relPathDir       : "a/b/distribution/extensions/extensions1/",
   300   originalContents : null,
   301   compareContents  : null,
   302   originalFile     : "partial.png",
   303   compareFile      : "complete.png",
   304   originalPerms    : 0o666,
   305   comparePerms     : 0o644
   306 }, {
   307   description      : "Added by update.manifest if the parent directory " +
   308                      "exists (add-if)",
   309   fileName         : "extensions1png0.png",
   310   relPathDir       : "a/b/distribution/extensions/extensions1/",
   311   originalContents : null,
   312   compareContents  : null,
   313   originalFile     : null,
   314   compareFile      : "complete.png",
   315   originalPerms    : null,
   316   comparePerms     : 0o644
   317 }, {
   318   description      : "Added by update.manifest if the parent directory " +
   319                      "exists (add-if)",
   320   fileName         : "extensions0text0",
   321   relPathDir       : "a/b/distribution/extensions/extensions0/",
   322   originalContents : "ToBeReplacedWithFromComplete\n",
   323   compareContents  : "FromComplete\n",
   324   originalFile     : null,
   325   compareFile      : null,
   326   originalPerms    : null,
   327   comparePerms     : 0o644
   328 }, {
   329   description      : "Added by update.manifest if the parent directory " +
   330                      "exists (add-if)",
   331   fileName         : "extensions0png1.png",
   332   relPathDir       : "a/b/distribution/extensions/extensions0/",
   333   originalContents : null,
   334   compareContents  : null,
   335   originalFile     : null,
   336   compareFile      : "complete.png",
   337   originalPerms    : null,
   338   comparePerms     : 0o644
   339 }, {
   340   description      : "Added by update.manifest if the parent directory " +
   341                      "exists (add-if)",
   342   fileName         : "extensions0png0.png",
   343   relPathDir       : "a/b/distribution/extensions/extensions0/",
   344   originalContents : null,
   345   compareContents  : null,
   346   originalFile     : null,
   347   compareFile      : "complete.png",
   348   originalPerms    : null,
   349   comparePerms     : 0o644
   350 }, {
   351   description      : "Added by update.manifest (add)",
   352   fileName         : "exe0.exe",
   353   relPathDir       : "a/b/",
   354   originalContents : null,
   355   compareContents  : null,
   356   originalFile     : FILE_HELPER_BIN,
   357   compareFile      : FILE_COMPLETE_EXE,
   358   originalPerms    : 0o777,
   359   comparePerms     : 0o755
   360 }, {
   361   description      : "Added by update.manifest (add)",
   362   fileName         : "10text0",
   363   relPathDir       : "a/b/1/10/",
   364   originalContents : "ToBeReplacedWithFromComplete\n",
   365   compareContents  : "FromComplete\n",
   366   originalFile     : null,
   367   compareFile      : null,
   368   originalPerms    : 0o767,
   369   comparePerms     : 0o644
   370 }, {
   371   description      : "Added by update.manifest (add)",
   372   fileName         : "0exe0.exe",
   373   relPathDir       : "a/b/0/",
   374   originalContents : null,
   375   compareContents  : null,
   376   originalFile     : FILE_HELPER_BIN,
   377   compareFile      : FILE_COMPLETE_EXE,
   378   originalPerms    : 0o777,
   379   comparePerms     : 0o755
   380 }, {
   381   description      : "Added by update.manifest (add)",
   382   fileName         : "00text1",
   383   relPathDir       : "a/b/0/00/",
   384   originalContents : "ToBeReplacedWithFromComplete\n",
   385   compareContents  : "FromComplete\n",
   386   originalFile     : null,
   387   compareFile      : null,
   388   originalPerms    : 0o677,
   389   comparePerms     : 0o644
   390 }, {
   391   description      : "Added by update.manifest (add)",
   392   fileName         : "00text0",
   393   relPathDir       : "a/b/0/00/",
   394   originalContents : "ToBeReplacedWithFromComplete\n",
   395   compareContents  : "FromComplete\n",
   396   originalFile     : null,
   397   compareFile      : null,
   398   originalPerms    : 0o775,
   399   comparePerms     : 0o644
   400 }, {
   401   description      : "Added by update.manifest (add)",
   402   fileName         : "00png0.png",
   403   relPathDir       : "a/b/0/00/",
   404   originalContents : null,
   405   compareContents  : null,
   406   originalFile     : null,
   407   compareFile      : "complete.png",
   408   originalPerms    : 0o776,
   409   comparePerms     : 0o644
   410 }, {
   411   description      : "Removed by precomplete (remove)",
   412   fileName         : "20text0",
   413   relPathDir       : "a/b/2/20/",
   414   originalContents : "ToBeDeleted\n",
   415   compareContents  : null,
   416   originalFile     : null,
   417   compareFile      : null,
   418   originalPerms    : null,
   419   comparePerms     : null
   420 }, {
   421   description      : "Removed by precomplete (remove)",
   422   fileName         : "20png0.png",
   423   relPathDir       : "a/b/2/20/",
   424   originalContents : "ToBeDeleted\n",
   425   compareContents  : null,
   426   originalFile     : null,
   427   compareFile      : null,
   428   originalPerms    : null,
   429   comparePerms     : null
   430 }];
   432 // Concatenate the common files to the end of the array.
   433 gTestFilesCompleteSuccess = gTestFilesCompleteSuccess.concat(gTestFilesCommon);
   435 // Files for a partial successful update. This can be used for a partial failed
   436 // update by calling setTestFilesAndDirsForFailure.
   437 var gTestFilesPartialSuccess = [
   438 {
   439   description      : "Added by update.manifest (add)",
   440   fileName         : "precomplete",
   441   relPathDir       : "",
   442   originalContents : null,
   443   compareContents  : null,
   444   originalFile     : "complete_precomplete",
   445   compareFile      : "partial_precomplete",
   446   originalPerms    : 0o666,
   447   comparePerms     : 0o644
   448 }, {
   449   description      : "Added by update.manifest (add)",
   450   fileName         : "searchpluginstext0",
   451   relPathDir       : "a/b/searchplugins/",
   452   originalContents : "ToBeReplacedWithFromPartial\n",
   453   compareContents  : "FromPartial\n",
   454   originalFile     : null,
   455   compareFile      : null,
   456   originalPerms    : 0o775,
   457   comparePerms     : 0o644
   458 }, {
   459   description      : "Patched by update.manifest if the file exists " +
   460                      "(patch-if)",
   461   fileName         : "searchpluginspng1.png",
   462   relPathDir       : "a/b/searchplugins/",
   463   originalContents : null,
   464   compareContents  : null,
   465   originalFile     : "complete.png",
   466   compareFile      : "partial.png",
   467   originalPerms    : 0o666,
   468   comparePerms     : 0o666
   469 }, {
   470   description      : "Patched by update.manifest if the file exists " +
   471                      "(patch-if)",
   472   fileName         : "searchpluginspng0.png",
   473   relPathDir       : "a/b/searchplugins/",
   474   originalContents : null,
   475   compareContents  : null,
   476   originalFile     : "complete.png",
   477   compareFile      : "partial.png",
   478   originalPerms    : 0o666,
   479   comparePerms     : 0o666
   480 }, {
   481   description      : "Added by update.manifest if the parent directory " +
   482                      "exists (add-if)",
   483   fileName         : "extensions1text0",
   484   relPathDir       : "a/b/distribution/extensions/extensions1/",
   485   originalContents : null,
   486   compareContents  : "FromPartial\n",
   487   originalFile     : null,
   488   compareFile      : null,
   489   originalPerms    : null,
   490   comparePerms     : 0o644
   491 }, {
   492   description      : "Patched by update.manifest if the parent directory " +
   493                      "exists (patch-if)",
   494   fileName         : "extensions1png1.png",
   495   relPathDir       : "a/b/distribution/extensions/extensions1/",
   496   originalContents : null,
   497   compareContents  : null,
   498   originalFile     : "complete.png",
   499   compareFile      : "partial.png",
   500   originalPerms    : 0o666,
   501   comparePerms     : 0o666
   502 }, {
   503   description      : "Patched by update.manifest if the parent directory " +
   504                      "exists (patch-if)",
   505   fileName         : "extensions1png0.png",
   506   relPathDir       : "a/b/distribution/extensions/extensions1/",
   507   originalContents : null,
   508   compareContents  : null,
   509   originalFile     : "complete.png",
   510   compareFile      : "partial.png",
   511   originalPerms    : 0o666,
   512   comparePerms     : 0o666
   513 }, {
   514   description      : "Added by update.manifest if the parent directory " +
   515                      "exists (add-if)",
   516   fileName         : "extensions0text0",
   517   relPathDir       : "a/b/distribution/extensions/extensions0/",
   518   originalContents : "ToBeReplacedWithFromPartial\n",
   519   compareContents  : "FromPartial\n",
   520   originalFile     : null,
   521   compareFile      : null,
   522   originalPerms    : 0o644,
   523   comparePerms     : 0o644
   524 }, {
   525   description      : "Patched by update.manifest if the parent directory " +
   526                      "exists (patch-if)",
   527   fileName         : "extensions0png1.png",
   528   relPathDir       : "a/b/distribution/extensions/extensions0/",
   529   originalContents : null,
   530   compareContents  : null,
   531   originalFile     : "complete.png",
   532   compareFile      : "partial.png",
   533   originalPerms    : 0o644,
   534   comparePerms     : 0o644
   535 }, {
   536   description      : "Patched by update.manifest if the parent directory " +
   537                      "exists (patch-if)",
   538   fileName         : "extensions0png0.png",
   539   relPathDir       : "a/b/distribution/extensions/extensions0/",
   540   originalContents : null,
   541   compareContents  : null,
   542   originalFile     : "complete.png",
   543   compareFile      : "partial.png",
   544   originalPerms    : 0o644,
   545   comparePerms     : 0o644
   546 }, {
   547   description      : "Patched by update.manifest (patch)",
   548   fileName         : "exe0.exe",
   549   relPathDir       : "a/b/",
   550   originalContents : null,
   551   compareContents  : null,
   552   originalFile     : FILE_COMPLETE_EXE,
   553   compareFile      : FILE_PARTIAL_EXE,
   554   originalPerms    : 0o755,
   555   comparePerms     : 0o755
   556 }, {
   557   description      : "Patched by update.manifest (patch)",
   558   fileName         : "0exe0.exe",
   559   relPathDir       : "a/b/0/",
   560   originalContents : null,
   561   compareContents  : null,
   562   originalFile     : FILE_COMPLETE_EXE,
   563   compareFile      : FILE_PARTIAL_EXE,
   564   originalPerms    : 0o755,
   565   comparePerms     : 0o755
   566 }, {
   567   description      : "Added by update.manifest (add)",
   568   fileName         : "00text0",
   569   relPathDir       : "a/b/0/00/",
   570   originalContents : "ToBeReplacedWithFromPartial\n",
   571   compareContents  : "FromPartial\n",
   572   originalFile     : null,
   573   compareFile      : null,
   574   originalPerms    : 0o644,
   575   comparePerms     : 0o644
   576 }, {
   577   description      : "Patched by update.manifest (patch)",
   578   fileName         : "00png0.png",
   579   relPathDir       : "a/b/0/00/",
   580   originalContents : null,
   581   compareContents  : null,
   582   originalFile     : "complete.png",
   583   compareFile      : "partial.png",
   584   originalPerms    : 0o666,
   585   comparePerms     : 0o666
   586 }, {
   587   description      : "Added by update.manifest (add)",
   588   fileName         : "20text0",
   589   relPathDir       : "a/b/2/20/",
   590   originalContents : null,
   591   compareContents  : "FromPartial\n",
   592   originalFile     : null,
   593   compareFile      : null,
   594   originalPerms    : null,
   595   comparePerms     : 0o644
   596 }, {
   597   description      : "Added by update.manifest (add)",
   598   fileName         : "20png0.png",
   599   relPathDir       : "a/b/2/20/",
   600   originalContents : null,
   601   compareContents  : null,
   602   originalFile     : null,
   603   compareFile      : "partial.png",
   604   originalPerms    : null,
   605   comparePerms     : 0o644
   606 }, {
   607   description      : "Added by update.manifest (add)",
   608   fileName         : "00text2",
   609   relPathDir       : "a/b/0/00/",
   610   originalContents : null,
   611   compareContents  : "FromPartial\n",
   612   originalFile     : null,
   613   compareFile      : null,
   614   originalPerms    : null,
   615   comparePerms     : 0o644
   616 }, {
   617   description      : "Removed by update.manifest (remove)",
   618   fileName         : "10text0",
   619   relPathDir       : "a/b/1/10/",
   620   originalContents : "ToBeDeleted\n",
   621   compareContents  : null,
   622   originalFile     : null,
   623   compareFile      : null,
   624   originalPerms    : null,
   625   comparePerms     : null
   626 }, {
   627   description      : "Removed by update.manifest (remove)",
   628   fileName         : "00text1",
   629   relPathDir       : "a/b/0/00/",
   630   originalContents : "ToBeDeleted\n",
   631   compareContents  : null,
   632   originalFile     : null,
   633   compareFile      : null,
   634   originalPerms    : null,
   635   comparePerms     : null
   636 }];
   638 // Concatenate the common files to the end of the array.
   639 gTestFilesPartialSuccess = gTestFilesPartialSuccess.concat(gTestFilesCommon);
   641 /**
   642  * The mar files used for the updater tests contain the following remove
   643  * operations.
   644  *
   645  * partial and complete test mar remove operations
   646  * -----------------------------------------------
   647  * remove "text1"
   648  * remove "text0"
   649  * rmrfdir "9/99/"
   650  * rmdir "9/99/"
   651  * rmrfdir "9/98/"
   652  * rmrfdir "9/97/"
   653  * rmrfdir "9/96/"
   654  * rmrfdir "9/95/"
   655  * rmrfdir "9/95/"
   656  * rmrfdir "9/94/"
   657  * rmdir "9/94/"
   658  * rmdir "9/93/"
   659  * rmdir "9/92/"
   660  * rmdir "9/91/"
   661  * rmdir "9/90/"
   662  * rmdir "9/90/"
   663  * rmrfdir "8/89/"
   664  * rmdir "8/89/"
   665  * rmrfdir "8/88/"
   666  * rmrfdir "8/87/"
   667  * rmrfdir "8/86/"
   668  * rmrfdir "8/85/"
   669  * rmrfdir "8/85/"
   670  * rmrfdir "8/84/"
   671  * rmdir "8/84/"
   672  * rmdir "8/83/"
   673  * rmdir "8/82/"
   674  * rmdir "8/81/"
   675  * rmdir "8/80/"
   676  * rmdir "8/80/"
   677  * rmrfdir "7/"
   678  * rmdir "6/"
   679  * remove "5/text1"
   680  * remove "5/text0"
   681  * rmrfdir "5/"
   682  * remove "4/text1"
   683  * remove "4/text0"
   684  * remove "4/exe0.exe"
   685  * rmdir "4/"
   686  * remove "3/text1"
   687  * remove "3/text0"
   688  *
   689  * partial test mar additional remove operations
   690  * ---------------------------------------------
   691  * remove "0/00/00text1"
   692  * remove "1/10/10text0"
   693  * rmdir "1/10/"
   694  * rmdir "1/"
   695  */
   696 var gTestDirsCommon = [
   697 {
   698   relPathDir   : "a/b/3/",
   699   dirRemoved   : false,
   700   files        : ["3text0", "3text1"],
   701   filesRemoved : true
   702 }, {
   703   relPathDir   : "a/b/4/",
   704   dirRemoved   : true,
   705   files        : ["4text0", "4text1"],
   706   filesRemoved : true
   707 }, {
   708   relPathDir   : "a/b/5/",
   709   dirRemoved   : true,
   710   files        : ["5test.exe", "5text0", "5text1"],
   711   filesRemoved : true
   712 }, {
   713   relPathDir   : "a/b/6/",
   714   dirRemoved   : true
   715 }, {
   716   relPathDir   : "a/b/7/",
   717   dirRemoved   : true,
   718   files        : ["7text0", "7text1"],
   719   subDirs      : ["70/", "71/"],
   720   subDirFiles  : ["7xtest.exe", "7xtext0", "7xtext1"]
   721 }, {
   722   relPathDir   : "a/b/8/",
   723   dirRemoved   : false
   724 }, {
   725   relPathDir   : "a/b/8/80/",
   726   dirRemoved   : true
   727 }, {
   728   relPathDir   : "a/b/8/81/",
   729   dirRemoved   : false,
   730   files        : ["81text0", "81text1"]
   731 }, {
   732   relPathDir   : "a/b/8/82/",
   733   dirRemoved   : false,
   734   subDirs      : ["820/", "821/"]
   735 }, {
   736   relPathDir   : "a/b/8/83/",
   737   dirRemoved   : true
   738 }, {
   739   relPathDir   : "a/b/8/84/",
   740   dirRemoved   : true
   741 }, {
   742   relPathDir   : "a/b/8/85/",
   743   dirRemoved   : true
   744 }, {
   745   relPathDir   : "a/b/8/86/",
   746   dirRemoved   : true,
   747   files        : ["86text0", "86text1"]
   748 }, {
   749   relPathDir   : "a/b/8/87/",
   750   dirRemoved   : true,
   751   subDirs      : ["870/", "871/"],
   752   subDirFiles  : ["87xtext0", "87xtext1"]
   753 }, {
   754   relPathDir   : "a/b/8/88/",
   755   dirRemoved   : true
   756 }, {
   757   relPathDir   : "a/b/8/89/",
   758   dirRemoved   : true
   759 }, {
   760   relPathDir   : "a/b/9/90/",
   761   dirRemoved   : true
   762 }, {
   763   relPathDir   : "a/b/9/91/",
   764   dirRemoved   : false,
   765   files        : ["91text0", "91text1"]
   766 }, {
   767   relPathDir   : "a/b/9/92/",
   768   dirRemoved   : false,
   769   subDirs      : ["920/", "921/"]
   770 }, {
   771   relPathDir   : "a/b/9/93/",
   772   dirRemoved   : true
   773 }, {
   774   relPathDir   : "a/b/9/94/",
   775   dirRemoved   : true
   776 }, {
   777   relPathDir   : "a/b/9/95/",
   778   dirRemoved   : true
   779 }, {
   780   relPathDir   : "a/b/9/96/",
   781   dirRemoved   : true,
   782   files        : ["96text0", "96text1"]
   783 }, {
   784   relPathDir   : "a/b/9/97/",
   785   dirRemoved   : true,
   786   subDirs      : ["970/", "971/"],
   787   subDirFiles  : ["97xtext0", "97xtext1"]
   788 }, {
   789   relPathDir   : "a/b/9/98/",
   790   dirRemoved   : true
   791 }, {
   792   relPathDir   : "a/b/9/99/",
   793   dirRemoved   : true
   794 }];
   796 // Directories for a complete successful update. This array can be used for a
   797 // complete failed update by calling setTestFilesAndDirsForFailure.
   798 var gTestDirsCompleteSuccess = [
   799 {
   800   description  : "Removed by precomplete (rmdir)",
   801   relPathDir   : "a/b/2/20/",
   802   dirRemoved   : true
   803 }, {
   804   description  : "Removed by precomplete (rmdir)",
   805   relPathDir   : "a/b/2/",
   806   dirRemoved   : true
   807 }];
   809 // Concatenate the common files to the beginning of the array.
   810 gTestDirsCompleteSuccess = gTestDirsCommon.concat(gTestDirsCompleteSuccess);
   812 // Directories for a partial successful update. This array can be used for a
   813 // partial failed update by calling setTestFilesAndDirsForFailure.
   814 var gTestDirsPartialSuccess = [
   815 {
   816   description  : "Removed by update.manifest (rmdir)",
   817   relPathDir   : "a/b/1/10/",
   818   dirRemoved   : true
   819 }, {
   820   description  : "Removed by update.manifest (rmdir)",
   821   relPathDir   : "a/b/1/",
   822   dirRemoved   : true
   823 }];
   825 // Concatenate the common files to the beginning of the array.
   826 gTestDirsPartialSuccess = gTestDirsCommon.concat(gTestDirsPartialSuccess);
   828 // Extra directories to check for existence for both complete and partial
   829 // updates. Whether they exist or not is set when calling setupUpdaterTest.
   830 var gTestExtraDirs = [
   831 {
   832   relPathDir   : DIR_UPDATED,
   833   dirExists    : false
   834 }, {
   835   relPathDir   : DIR_TOBEDELETED,
   836   dirExists    : false
   837 }];
   839 // This makes it possible to run most tests on xulrunner where the update
   840 // channel default preference is not set.
   841 if (MOZ_APP_NAME == "xulrunner") {
   842   try {
   843     gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL);
   844   } catch (e) {
   845     setUpdateChannel("test_channel");
   846   }
   847 }
   849 /**
   850  * Helper function for setting up the test environment.
   851  */
   852 function setupTestCommon() {
   853   logTestInfo("start - general test setup");
   855   do_test_pending();
   857   if (gTestID) {
   858     do_throw("setupTestCommon should only be called once!");
   859   }
   861   let caller = Components.stack.caller;
   862   gTestID = caller.filename.toString().split("/").pop().split(".")[0];
   864   if (DEBUG_TEST_LOG) {
   865     let logFile = do_get_file(gTestID + ".log", true);
   866     if (logFile.exists()) {
   867       gPassed = false;
   868       logTestInfo("start - dumping previous test run log");
   869       logTestInfo("\n" + readFile(logFile) + "\n");
   870       logTestInfo("finish - dumping previous test run log");
   871       if (gDeleteLogFile) {
   872         logFile.remove(false);
   873       }
   874       do_throw("The parallel run of this test failed. Failing non-parallel " +
   875                "test so the log from the parallel run can be displayed in " +
   876                "non-parallel log.")
   877     } else {
   878       gRealDump = dump;
   879       dump = dumpOverride;
   880     }
   881   }
   883   // Don't attempt to show a prompt when an update finishes.
   884   Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, true);
   886   gGREDirOrig = getGREDir();
   887   gAppDirOrig = getAppBaseDir();
   889   let applyDir = getApplyDirFile(null, true).parent;
   891   // Try to remove the directory used to apply updates and the updates directory
   892   // on platforms other than Windows. Since the test hasn't ran yet and the
   893   // directory shouldn't exist finished this is non-fatal for the test.
   894   if (applyDir.exists()) {
   895     logTestInfo("attempting to remove directory. Path: " + applyDir.path);
   896     try {
   897       removeDirRecursive(applyDir);
   898     } catch (e) {
   899       logTestInfo("non-fatal error removing directory. Path: " +
   900                   applyDir.path + ", Exception: " + e);
   901     }
   902   }
   904   // adjustGeneralPaths registers a cleanup function that calls end_test when
   905   // it is defined as a function.
   906   adjustGeneralPaths();
   908   // Remove the updates directory on Windows which is located outside of the
   909   // application directory after the call to adjustGeneralPaths has set it up.
   910   // Since the test hasn't ran yet and the directory shouldn't exist finished
   911   // this is non-fatal for the test.
   912   if (IS_WIN) {
   913     let updatesDir = getMockUpdRootD();
   914     if (updatesDir.exists())  {
   915       logTestInfo("attempting to remove directory. Path: " + updatesDir.path);
   916       try {
   917         removeDirRecursive(updatesDir);
   918       } catch (e) {
   919         logTestInfo("non-fatal error removing directory. Path: " +
   920                     updatesDir.path + ", Exception: " + e);
   921       }
   922     }
   923   }
   925   logTestInfo("finish - general test setup");
   926 }
   928 /**
   929  * Nulls out the most commonly used global vars used by tests to prevent leaks
   930  * as needed and attempts to restore the system to its original state.
   931  */
   932 function cleanupTestCommon() {
   933   logTestInfo("start - general test cleanup");
   935   // Force the update manager to reload the update data to prevent it from
   936   // writing the old data to the files that have just been removed.
   937   reloadUpdateManagerData();
   939   if (gChannel) {
   940     gPrefRoot.removeObserver(PREF_APP_UPDATE_CHANNEL, observer);
   941   }
   943   // Call app update's observe method passing xpcom-shutdown to test that the
   944   // shutdown of app update runs without throwing or leaking. The observer
   945   // method is used directly instead of calling notifyObservers so components
   946   // outside of the scope of this test don't assert and thereby cause app update
   947   // tests to fail.
   948   gAUS.observe(null, "xpcom-shutdown", "");
   950   if (gXHR) {
   951     gXHRCallback     = null;
   953     gXHR.responseXML = null;
   954     // null out the event handlers to prevent a mFreeCount leak of 1
   955     gXHR.onerror     = null;
   956     gXHR.onload      = null;
   957     gXHR.onprogress  = null;
   959     gXHR             = null;
   960   }
   962   gTestserver = null;
   964   if (IS_UNIX) {
   965     // This will delete the launch script if it exists.
   966     getLaunchScript();
   967   }
   969   if (IS_WIN && MOZ_APP_BASENAME) {
   970     let appDir = getApplyDirFile(null, true);
   971     let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla";
   972     const REG_PATH = "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME +
   973                      "\\TaskBarIDs";
   974     let key = AUS_Cc["@mozilla.org/windows-registry-key;1"].
   975               createInstance(AUS_Ci.nsIWindowsRegKey);
   976     try {
   977       key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH,
   978                AUS_Ci.nsIWindowsRegKey.ACCESS_ALL);
   979       if (key.hasValue(appDir.path)) {
   980         key.removeValue(appDir.path);
   981       }
   982     } catch (e) {
   983     }
   984     try {
   985       key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, REG_PATH,
   986                AUS_Ci.nsIWindowsRegKey.ACCESS_ALL);
   987       if (key.hasValue(appDir.path)) {
   988         key.removeValue(appDir.path);
   989       }
   990     } catch (e) {
   991     }
   992   }
   994   // The updates directory is located outside of the application directory on
   995   // Windows so it also needs to be removed.
   996   if (IS_WIN) {
   997     let updatesDir = getMockUpdRootD();
   998     // Try to remove the directory used to apply updates. Since the test has
   999     // already finished this is non-fatal for the test.
  1000     if (updatesDir.exists()) {
  1001       logTestInfo("attempting to remove directory. Path: " + updatesDir.path);
  1002       try {
  1003         removeDirRecursive(updatesDir);
  1004       } catch (e) {
  1005         logTestInfo("non-fatal error removing directory. Path: " +
  1006                     updatesDir.path + ", Exception: " + e);
  1011   let applyDir = getApplyDirFile(null, true).parent;
  1013   // Try to remove the directory used to apply updates. Since the test has
  1014   // already finished this is non-fatal for the test.
  1015   if (applyDir.exists()) {
  1016     logTestInfo("attempting to remove directory. Path: " + applyDir.path);
  1017     try {
  1018       removeDirRecursive(applyDir);
  1019     } catch (e) {
  1020       logTestInfo("non-fatal error removing directory. Path: " +
  1021                   applyDir.path + ", Exception: " + e);
  1025   resetEnvironment();
  1027   logTestInfo("finish - general test cleanup");
  1029   if (gRealDump) {
  1030     dump = gRealDump;
  1031     gRealDump = null;
  1034   if (DEBUG_TEST_LOG && !gPassed) {
  1035     let fos = AUS_Cc["@mozilla.org/network/file-output-stream;1"].
  1036               createInstance(AUS_Ci.nsIFileOutputStream);
  1037     let logFile = do_get_file(gTestID + ".log", true);
  1038     if (!logFile.exists()) {
  1039       logFile.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
  1041     fos.init(logFile, MODE_WRONLY | MODE_CREATE | MODE_APPEND, PERMS_FILE, 0);
  1042     fos.write(gTestLogText, gTestLogText.length);
  1043     fos.close();
  1046   if (DEBUG_TEST_LOG) {
  1047     gTestLogText = null;
  1048   } else {
  1049     let logFile = do_get_file(gTestID + ".log", true);
  1050     if (logFile.exists()) {
  1051       logFile.remove(false);
  1056 /**
  1057  * Helper function to store the log output of calls to dump in a variable so the
  1058  * values can be written to a file for a parallel run of a test and printed to
  1059  * the log file when the test runs synchronously.
  1060  */
  1061 function dumpOverride(aText) {
  1062   gTestLogText += aText;
  1063   gRealDump(aText);
  1066 /**
  1067  * Helper function that calls do_test_finished that tracks whether a parallel
  1068  * run of a test passed when it runs synchronously so the log output can be
  1069  * inspected.
  1070  */
  1071 function doTestFinish() {
  1072   if (gPassed === undefined) {
  1073     gPassed = true;
  1075   do_test_finished();
  1078 /**
  1079  * Sets the most commonly used preferences used by tests
  1080  */
  1081 function setDefaultPrefs() {
  1082   Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true);
  1083   Services.prefs.setBoolPref(PREF_APP_UPDATE_METRO_ENABLED, true);
  1084   // Don't display UI for a successful installation. Some apps may not set this
  1085   // pref to false like Firefox does.
  1086   Services.prefs.setBoolPref(PREF_APP_UPDATE_SHOW_INSTALLED_UI, false);
  1087   // Enable Update logging
  1088   Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true);
  1091 /**
  1092  * Helper function for updater binary tests that sets the appropriate values
  1093  * to check for update failures.
  1094  */
  1095 function setTestFilesAndDirsForFailure() {
  1096   gTestFiles.forEach(function STFADFF_Files(aTestFile) {
  1097     aTestFile.compareContents = aTestFile.originalContents;
  1098     aTestFile.compareFile = aTestFile.originalFile;
  1099     aTestFile.comparePerms = aTestFile.originalPerms;
  1100   });
  1102   gTestDirs.forEach(function STFADFF_Dirs(aTestDir) {
  1103     aTestDir.dirRemoved = false;
  1104     if (aTestDir.filesRemoved) {
  1105       aTestDir.filesRemoved = false;
  1107   });
  1110 /**
  1111  * Initializes the most commonly used settings and creates an instance of the
  1112  * update service stub.
  1113  */
  1114 function standardInit() {
  1115   createAppInfo("xpcshell@tests.mozilla.org", APP_INFO_NAME, "1.0", "2.0");
  1116   setDefaultPrefs();
  1117   // Initialize the update service stub component
  1118   initUpdateServiceStub();
  1121 /**
  1122  * Custom path handler for the http server
  1124  * @param   aMetadata
  1125  *          The http metadata for the request.
  1126  * @param   aResponse
  1127  *          The http response for the request.
  1128  */
  1129 function pathHandler(aMetadata, aResponse) {
  1130   aResponse.setHeader("Content-Type", "text/xml", false);
  1131   aResponse.setStatusLine(aMetadata.httpVersion, gResponseStatusCode, "OK");
  1132   aResponse.bodyOutputStream.write(gResponseBody, gResponseBody.length);
  1135 /**
  1136  * Helper function for getting the application version from the application.ini
  1137  * file. This will look in both the GRE and the application directories for the
  1138  * application.ini file.
  1140  * @return  The version string from the application.ini file.
  1141  * @throws  If the application.ini file is not found.
  1142  */
  1143 function getAppVersion() {
  1144   // Read the application.ini and use its application version.
  1145   let iniFile = gGREDirOrig.clone();
  1146   iniFile.append("application.ini");
  1147   if (!iniFile.exists()) {
  1148     iniFile = gAppDirOrig.clone();
  1149     iniFile.append("application.ini");
  1151   if (!iniFile.exists()) {
  1152     do_throw("Unable to find application.ini!");
  1154   let iniParser = AUS_Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
  1155                   getService(AUS_Ci.nsIINIParserFactory).
  1156                   createINIParser(iniFile);
  1157   return iniParser.getString("App", "Version");
  1160 /**
  1161  * Helper function for getting the relative path to the directory where the
  1162  * application binary is located (e.g. <test_file_leafname>/dir.app/).
  1164  * Note: The dir.app subdirectory under <test_file_leafname> is needed for
  1165  *       platforms other than Mac OS X so the tests can run in parallel due to
  1166  *       update staging creating a lock file named moz_update_in_progress.lock in
  1167  *       the parent directory of the installation directory.
  1169  * @return  The relative path to the directory where application binary is
  1170  *          located.
  1171  */
  1172 function getApplyDirPath() {
  1173   return gTestID + "/dir.app/";
  1176 /**
  1177  * Helper function for getting the nsIFile for a file in the directory where the
  1178  * update will be applied.
  1180  * The files for the update are located two directories below the apply to
  1181  * directory since Mac OS X sets the last modified time for the root directory
  1182  * to the current time and if the update changes any files in the root directory
  1183  * then it wouldn't be possible to test (bug 600098).
  1185  * @param   aRelPath (optional)
  1186  *          The relative path to the file or directory to get from the root of
  1187  *          the test's directory. If not specified the test's directory will be
  1188  *          returned.
  1189  * @param   aAllowNonexistent (optional)
  1190  *          Whether the file must exist. If false or not specified the file must
  1191  *          exist or the function will throw.
  1192  * @return  The nsIFile for the file in the directory where the update will be
  1193  *          applied.
  1194  */
  1195 function getApplyDirFile(aRelPath, aAllowNonexistent) {
  1196   let relpath = getApplyDirPath() + (aRelPath ? aRelPath : "");
  1197   return do_get_file(relpath, aAllowNonexistent);
  1200 /**
  1201  * Helper function for getting the relative path to the directory where the
  1202  * test data files are located.
  1204  * @return  The relative path to the directory where the test data files are
  1205  *          located.
  1206  */
  1207 function getTestDirPath() {
  1208   return "../data/";
  1211 /**
  1212  * Helper function for getting the nsIFile for a file in the test data
  1213  * directory.
  1215  * @param   aRelPath (optional)
  1216  *          The relative path to the file or directory to get from the root of
  1217  *          the test's data directory. If not specified the test's data
  1218  *          directory will be returned.
  1219  * @return  The nsIFile for the file in the test data directory.
  1220  * @throws  If the file or directory does not exist.
  1221  */
  1222 function getTestDirFile(aRelPath) {
  1223   let relpath = getTestDirPath() + (aRelPath ? aRelPath : "");
  1224   return do_get_file(relpath, false);
  1227 /**
  1228  * Helper function for getting the directory that was updated. This can either
  1229  * be the directory where the application binary is located or the directory
  1230  * that contains the staged update.
  1231  */
  1232 function getUpdatedDirPath() {
  1233   return getApplyDirPath() + (gStageUpdate ? DIR_UPDATED +  "/" : "");
  1236 #ifdef XP_WIN
  1237 XPCOMUtils.defineLazyGetter(this, "gInstallDirPathHash",
  1238                             function test_gInstallDirPathHash() {
  1239   // Figure out where we should check for a cached hash value
  1240   if (!MOZ_APP_BASENAME)
  1241     return null;
  1243   let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla";
  1244   let appDir = getApplyDirFile(null, true);
  1246   const REG_PATH = "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME +
  1247                    "\\TaskBarIDs";
  1248   let regKey = AUS_Cc["@mozilla.org/windows-registry-key;1"].
  1249                createInstance(AUS_Ci.nsIWindowsRegKey);
  1250   try {
  1251     regKey.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH,
  1252                 AUS_Ci.nsIWindowsRegKey.ACCESS_ALL);
  1253     regKey.writeStringValue(appDir.path, gTestID);
  1254     return gTestID;
  1255   } catch (e) {
  1258   try {
  1259     regKey.create(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, REG_PATH,
  1260                   AUS_Ci.nsIWindowsRegKey.ACCESS_ALL);
  1261     regKey.writeStringValue(appDir.path, gTestID);
  1262     return gTestID;
  1263   } catch (e) {
  1264     logTestInfo("failed to create registry key. Registry Path: " + REG_PATH +
  1265                 ", Key Name: " + appDir.path + ", Key Value: " + gTestID +
  1266                 ", Exception " + e);
  1268   return null;
  1269 });
  1271 XPCOMUtils.defineLazyGetter(this, "gLocalAppDataDir",
  1272                             function test_gLocalAppDataDir() {
  1273   const CSIDL_LOCAL_APPDATA = 0x1c;
  1275   AUS_Cu.import("resource://gre/modules/ctypes.jsm");
  1276   let lib = ctypes.open("shell32");
  1277   let SHGetSpecialFolderPath = lib.declare("SHGetSpecialFolderPathW",
  1278                                            ctypes.winapi_abi,
  1279                                            ctypes.bool, /* bool(return) */
  1280                                            ctypes.int32_t, /* HWND hwndOwner */
  1281                                            ctypes.jschar.ptr, /* LPTSTR lpszPath */
  1282                                            ctypes.int32_t, /* int csidl */
  1283                                            ctypes.bool /* BOOL fCreate */);
  1285   let aryPathLocalAppData = ctypes.jschar.array()(260);
  1286   let rv = SHGetSpecialFolderPath(0, aryPathLocalAppData, CSIDL_LOCAL_APPDATA, false);
  1287   lib.close();
  1289   let pathLocalAppData = aryPathLocalAppData.readString(); // Convert the c-string to js-string
  1290   let updatesDir = AUS_Cc["@mozilla.org/file/local;1"].
  1291                    createInstance(AUS_Ci.nsILocalFile);
  1292   updatesDir.initWithPath(pathLocalAppData);
  1293   return updatesDir;
  1294 });
  1296 XPCOMUtils.defineLazyGetter(this, "gProgFilesDir",
  1297                             function test_gProgFilesDir() {
  1298   const CSIDL_PROGRAM_FILES = 0x26;
  1300   AUS_Cu.import("resource://gre/modules/ctypes.jsm");
  1301   let lib = ctypes.open("shell32");
  1302   let SHGetSpecialFolderPath = lib.declare("SHGetSpecialFolderPathW",
  1303                                            ctypes.winapi_abi,
  1304                                            ctypes.bool, /* bool(return) */
  1305                                            ctypes.int32_t, /* HWND hwndOwner */
  1306                                            ctypes.jschar.ptr, /* LPTSTR lpszPath */
  1307                                            ctypes.int32_t, /* int csidl */
  1308                                            ctypes.bool /* BOOL fCreate */);
  1310   let aryPathProgFiles = ctypes.jschar.array()(260);
  1311   let rv = SHGetSpecialFolderPath(0, aryPathProgFiles, CSIDL_PROGRAM_FILES, false);
  1312   lib.close();
  1314   let pathProgFiles = aryPathProgFiles.readString(); // Convert the c-string to js-string
  1315   let progFilesDir = AUS_Cc["@mozilla.org/file/local;1"].
  1316                      createInstance(AUS_Ci.nsILocalFile);
  1317   progFilesDir.initWithPath(pathProgFiles);
  1318   return progFilesDir;
  1319 });
  1321 /**
  1322  * Helper function for getting the update root directory used by the tests. This
  1323  * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir
  1324  * in nsXREDirProvider.cpp so an application will be able to find the update
  1325  * when running a test that launches the application.
  1326  */
  1327 function getMockUpdRootD() {
  1328   let localAppDataDir = gLocalAppDataDir.clone();
  1329   let progFilesDir = gProgFilesDir.clone();
  1330   let appDir = Services.dirsvc.get(XRE_EXECUTABLE_FILE, AUS_Ci.nsIFile).parent;
  1332   let appDirPath = appDir.path;
  1333   var relPathUpdates = "";
  1334   if (gInstallDirPathHash && (MOZ_APP_VENDOR || MOZ_APP_BASENAME)) {
  1335     relPathUpdates += (MOZ_APP_VENDOR ? MOZ_APP_VENDOR : MOZ_APP_BASENAME) +
  1336                       "\\" + DIR_UPDATES + "\\" + gInstallDirPathHash;
  1339   if (!relPathUpdates) {
  1340     if (appDirPath.length > progFilesDir.path.length) {
  1341       if (appDirPath.substr(0, progFilesDir.path.length) == progFilesDir.path) {
  1342         if (MOZ_APP_VENDOR && MOZ_APP_BASENAME) {
  1343           relPathUpdates += MOZ_APP_VENDOR + "\\" + MOZ_APP_BASENAME;
  1344         } else {
  1345           relPathUpdates += MOZ_APP_BASENAME;
  1347         relPathUpdates += appDirPath.substr(progFilesDir.path.length);
  1352   if (!relPathUpdates) {
  1353     if (MOZ_APP_VENDOR && MOZ_APP_BASENAME) {
  1354       relPathUpdates += MOZ_APP_VENDOR + "\\" + MOZ_APP_BASENAME;
  1355     } else {
  1356       relPathUpdates += MOZ_APP_BASENAME;
  1358     relPathUpdates += "\\" + MOZ_APP_NAME;
  1361   var updatesDir = AUS_Cc["@mozilla.org/file/local;1"].
  1362                    createInstance(AUS_Ci.nsILocalFile);
  1363   updatesDir.initWithPath(localAppDataDir.path + "\\" + relPathUpdates);
  1364   logTestInfo("returning UpdRootD Path: " + updatesDir.path);
  1365   return updatesDir;
  1367 #else
  1368 /**
  1369  * Helper function for getting the update root directory used by the tests. This
  1370  * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir
  1371  * in nsXREDirProvider.cpp so an application will be able to find the update
  1372  * when running a test that launches the application.
  1373  */
  1374 function getMockUpdRootD() {
  1375   return getApplyDirFile(DIR_BIN_REL_PATH, true);
  1377 #endif
  1379 /**
  1380  * Helper function for getting the nsIFile for the directory where the update
  1381  * has been applied.
  1383  * This will be the same as getApplyDirFile for foreground updates, but will
  1384  * point to a different file for the case of staged updates.
  1386  * Functions which attempt to access the files in the updated directory should
  1387  * be using this instead of getApplyDirFile.
  1389  * @param   aRelPath (optional)
  1390  *          The relative path to the file or directory to get from the root of
  1391  *          the test's directory. If not specified the test's directory will be
  1392  *          returned.
  1393  * @param   aAllowNonexistent (optional)
  1394  *          Whether the file must exist. If false or not specified the file must
  1395  *          exist or the function will throw.
  1396  * @return  The nsIFile for the directory where the update has been applied.
  1397  */
  1398 function getTargetDirFile(aRelPath, aAllowNonexistent) {
  1399   let relpath = getUpdatedDirPath() + (aRelPath ? aRelPath : "");
  1400   return do_get_file(relpath, aAllowNonexistent);
  1403 if (IS_WIN) {
  1404   const kLockFileName = "updated.update_in_progress.lock";
  1405   /**
  1406    * Helper function for locking a directory on Windows.
  1408    * @param   aDir
  1409    *          The nsIFile for the directory to lock.
  1410    */
  1411   function lockDirectory(aDir) {
  1412     var file = aDir.clone();
  1413     file.append(kLockFileName);
  1414     file.create(file.NORMAL_FILE_TYPE, 0o444);
  1415     file.QueryInterface(AUS_Ci.nsILocalFileWin);
  1416     file.fileAttributesWin |= file.WFA_READONLY;
  1417     file.fileAttributesWin &= ~file.WFA_READWRITE;
  1418     logTestInfo("testing the successful creation of the lock file");
  1419     do_check_true(file.exists());
  1420     do_check_false(file.isWritable());
  1422   /**
  1423    * Helper function for unlocking a directory on Windows.
  1425    * @param   aDir
  1426    *          The nsIFile for the directory to unlock.
  1427    */
  1428   function unlockDirectory(aDir) {
  1429     var file = aDir.clone();
  1430     file.append(kLockFileName);
  1431     file.QueryInterface(AUS_Ci.nsILocalFileWin);
  1432     file.fileAttributesWin |= file.WFA_READWRITE;
  1433     file.fileAttributesWin &= ~file.WFA_READONLY;
  1434     logTestInfo("removing and testing the successful removal of the lock file");
  1435     file.remove(false);
  1436     do_check_false(file.exists());
  1440 /**
  1441  * Helper function for updater tests for launching the updater binary to apply
  1442  * a mar file.
  1444  * @param   aExpectedExitValue
  1445  *          The expected exit value from the updater binary.
  1446  * @param   aExpectedStatus
  1447  *          The expected value of update.status when the test finishes.
  1448  * @param   aCallback (optional)
  1449  *          A callback function that will be called when this function finishes.
  1450  *          If null no function will be called when this function finishes.
  1451  *          If not specified the checkUpdateApplied function will be called when
  1452  *          this function finishes.
  1453  */
  1454 function runUpdate(aExpectedExitValue, aExpectedStatus, aCallback) {
  1455   // Copy the updater binary to the updates directory.
  1456   let binDir = gGREDirOrig.clone();
  1457   let updater = binDir.clone();
  1458   updater.append("updater.app");
  1459   if (!updater.exists()) {
  1460     updater = binDir.clone();
  1461     updater.append(FILE_UPDATER_BIN);
  1462     if (!updater.exists()) {
  1463       do_throw("Unable to find updater binary!");
  1467   let updatesDir = getUpdatesPatchDir();
  1468   updater.copyToFollowingLinks(updatesDir, updater.leafName);
  1469   let updateBin = updatesDir.clone();
  1470   updateBin.append(updater.leafName);
  1471   if (updateBin.leafName == "updater.app") {
  1472     updateBin.append("Contents");
  1473     updateBin.append("MacOS");
  1474     updateBin.append("updater");
  1475     if (!updateBin.exists()) {
  1476       do_throw("Unable to find the updater executable!");
  1480   let applyToDir = getApplyDirFile(null, true);
  1481   let applyToDirPath = applyToDir.path;
  1482   if (gStageUpdate || gSwitchApp) {
  1483     applyToDirPath += "/" + DIR_UPDATED + "/";
  1486   if (IS_WIN) {
  1487     // Convert to native path
  1488     applyToDirPath = applyToDirPath.replace(/\//g, "\\");
  1491   let callbackApp = getApplyDirFile("a/b/" + gCallbackBinFile);
  1492   callbackApp.permissions = PERMS_DIRECTORY;
  1494   let args = [updatesDir.path, applyToDirPath, 0];
  1495   if (gStageUpdate) {
  1496     args[2] = -1;
  1497   } else {
  1498     if (gSwitchApp) {
  1499       args[2] = "0/replace";
  1501     args = args.concat([callbackApp.parent.path, callbackApp.path]);
  1502     args = args.concat(gCallbackArgs);
  1504   logTestInfo("running the updater: " + updateBin.path + " " + args.join(" "));
  1506   let env = AUS_Cc["@mozilla.org/process/environment;1"].
  1507             getService(AUS_Ci.nsIEnvironment);
  1508   if (gDisableReplaceFallback) {
  1509     env.set("MOZ_NO_REPLACE_FALLBACK", "1");
  1512   let process = AUS_Cc["@mozilla.org/process/util;1"].
  1513                 createInstance(AUS_Ci.nsIProcess);
  1514   process.init(updateBin);
  1515   process.run(true, args, args.length);
  1517   if (gDisableReplaceFallback) {
  1518     env.set("MOZ_NO_REPLACE_FALLBACK", "");
  1521   let status = readStatusFile();
  1522   if (process.exitValue != aExpectedExitValue || status != aExpectedStatus) {
  1523     if (process.exitValue != aExpectedExitValue) {
  1524       logTestInfo("updater exited with unexpected value! Got: " +
  1525                   process.exitValue + ", Expected: " +  aExpectedExitValue);
  1527     if (status != aExpectedStatus) {
  1528       logTestInfo("update status is not the expected status! Got: " + status +
  1529                   ", Expected: " +  aExpectedStatus);
  1531     let updateLog = getUpdatesPatchDir();
  1532     updateLog.append(FILE_UPDATE_LOG);
  1533     logTestInfo("contents of " + updateLog.path + ":\n" +
  1534                 readFileBytes(updateLog).replace(/\r\n/g, "\n"));
  1536   logTestInfo("testing updater binary process exitValue against expected " +
  1537               "exit value");
  1538   do_check_eq(process.exitValue, aExpectedExitValue);
  1539   logTestInfo("testing update status against expected status");
  1540   do_check_eq(status, aExpectedStatus);
  1542   if (aCallback !== null) {
  1543     if (typeof(aCallback) == typeof(Function)) {
  1544       aCallback();
  1545     } else {
  1546       checkUpdateApplied();
  1550 /**
  1551  * Helper function for updater tests to stage an update.
  1552  */
  1553 function stageUpdate() {
  1554   logTestInfo("start - staging update");
  1555   Services.obs.addObserver(gUpdateStagedObserver, "update-staged", false);
  1557   setEnvironment();
  1558   // Stage the update.
  1559   AUS_Cc["@mozilla.org/updates/update-processor;1"].
  1560     createInstance(AUS_Ci.nsIUpdateProcessor).
  1561     processUpdate(gUpdateManager.activeUpdate);
  1562   resetEnvironment();
  1564   logTestInfo("finish - staging update");
  1567 /**
  1568  * Helper function to check whether the maintenance service updater tests should
  1569  * run. See bug 711660 for more details.
  1571  * @param  aFirstTest
  1572  *         Whether this is the first test within the test.
  1573  * @return true if the test should run and false if it shouldn't.
  1574  */
  1575 function shouldRunServiceTest(aFirstTest) {
  1576   // In case the machine is running an old maintenance service or if it
  1577   // is not installed, and permissions exist to install it.  Then install
  1578   // the newer bin that we have.
  1579   attemptServiceInstall();
  1581   let binDir = getGREDir();
  1582   let updaterBin = binDir.clone();
  1583   updaterBin.append(FILE_UPDATER_BIN);
  1584   if (!updaterBin.exists()) {
  1585     do_throw("Unable to find updater binary!");
  1588   let updaterBinPath = updaterBin.path;
  1589   if (/ /.test(updaterBinPath)) {
  1590     updaterBinPath = '"' + updaterBinPath + '"';
  1593   const REG_PATH = "SOFTWARE\\Mozilla\\MaintenanceService\\" +
  1594                    "3932ecacee736d366d6436db0f55bce4";
  1596   let key = AUS_Cc["@mozilla.org/windows-registry-key;1"].
  1597             createInstance(AUS_Ci.nsIWindowsRegKey);
  1598   try {
  1599     key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH,
  1600              AUS_Ci.nsIWindowsRegKey.ACCESS_READ | key.WOW64_64);
  1601   } catch (e) {
  1602 #ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK
  1603     // The build system could sign the files and not have the test registry key
  1604     // in which case we should fail the test by throwing so it can be fixed.
  1605     if (isBinarySigned(updaterBinPath)) {
  1606       do_throw("binary is signed but the test registry key does not exists!");
  1608 #endif
  1610     logTestInfo("this test can only run on the buildbot build system at this " +
  1611                 "time.");
  1612     return false;
  1615   // Check to make sure the service is installed
  1616   let helperBin = getTestDirFile(FILE_HELPER_BIN);
  1617   let args = ["wait-for-service-stop", "MozillaMaintenance", "10"];
  1618   let process = AUS_Cc["@mozilla.org/process/util;1"].
  1619                 createInstance(AUS_Ci.nsIProcess);
  1620   process.init(helperBin);
  1621   logTestInfo("checking if the service exists on this machine.");
  1622   process.run(true, args, args.length);
  1623   if (process.exitValue == 0xEE) {
  1624     do_throw("test registry key exists but this test can only run on systems " +
  1625              "with the maintenance service installed.");
  1626   } else {
  1627     logTestInfo("service exists, return value: " + process.exitValue);
  1630   // If this is the first test in the series, then there is no reason the
  1631   // service should be anything but stopped, so be strict here and throw
  1632   // an error.
  1633   if (aFirstTest && process.exitValue != 0) {
  1634     do_throw("First test, check for service stopped state returned error " +
  1635              process.exitValue);
  1638 #ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK
  1639   if (!isBinarySigned(updaterBinPath)) {
  1640     logTestInfo("test registry key exists but this test can only run on " +
  1641                 "builds with signed binaries when " +
  1642                 "DISABLE_UPDATER_AUTHENTICODE_CHECK is not defined");
  1643     do_throw("this test can only run on builds with signed binaries.");
  1645 #endif
  1646   return true;
  1649 /**
  1650  * Helper function to check whether the a binary is signed.
  1652  * @param  aBinPath The path to the file to check if it is signed.
  1653  * @return true if the file is signed and false if it isn't.
  1654  */
  1655 function isBinarySigned(aBinPath) {
  1656   let helperBin = getTestDirFile(FILE_HELPER_BIN);
  1657   let args = ["check-signature", aBinPath];
  1658   let process = AUS_Cc["@mozilla.org/process/util;1"].
  1659                 createInstance(AUS_Ci.nsIProcess);
  1660   process.init(helperBin);
  1661   process.run(true, args, args.length);
  1662   if (process.exitValue != 0) {
  1663     logTestInfo("binary is not signed. " + FILE_HELPER_BIN + " returned " +
  1664                 process.exitValue + " for file " + aBinPath);
  1665     return false;
  1667   return true;
  1670 /**
  1671  * Helper function for asynchronously setting up the application files required
  1672  * to launch the application for the updater tests by either copying or creating
  1673  * symlinks for the files. This is needed for Windows debug builds which can
  1674  * lock a file that is being copied so that the tests can run in parallel. After
  1675  * the files have been copied the setupAppFilesFinished function will be called.
  1676  */
  1677 function setupAppFilesAsync() {
  1678   gTimeoutRuns++;
  1679   try {
  1680     setupAppFiles();
  1681   } catch (e) {
  1682     if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
  1683       do_throw("Exceeded MAX_TIMEOUT_RUNS while trying to setup application " +
  1684                "files. Exception: " + e);
  1686     do_timeout(TEST_CHECK_TIMEOUT, setupAppFilesAsync);
  1687     return;
  1690   setupAppFilesFinished();
  1693 /**
  1694  * Helper function for setting up the application files required to launch the
  1695  * application for the updater tests by either copying or creating symlinks for
  1696  * the files.
  1697  */
  1698 function setupAppFiles() {
  1699   logTestInfo("start - copying or creating symlinks for application files " +
  1700               "for the test");
  1702   let srcDir = getCurrentProcessDir();
  1703   let destDir = getApplyDirFile(null, true);
  1704   if (!destDir.exists()) {
  1705     try {
  1706       destDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
  1707     } catch (e) {
  1708       logTestInfo("unable to create directory, Path: " + destDir.path +
  1709                   ", Exception: " + e);
  1710       do_throw(e);
  1714   // Required files for the application or the test that aren't listed in the
  1715   // dependentlibs.list file.
  1716   let fileRelPaths = [FILE_APP_BIN, FILE_UPDATER_BIN,
  1717                       "application.ini", "dependentlibs.list"];
  1719   // On Linux the updater.png must also be copied
  1720   if (IS_UNIX && !IS_MACOSX) {
  1721     fileRelPaths.push("icons/updater.png");
  1724   // Read the dependent libs file leafnames from the dependentlibs.list file
  1725   // into the array.
  1726   let deplibsFile = srcDir.clone();
  1727   deplibsFile.append("dependentlibs.list");
  1728   let istream = AUS_Cc["@mozilla.org/network/file-input-stream;1"].
  1729                 createInstance(AUS_Ci.nsIFileInputStream);
  1730   istream.init(deplibsFile, 0x01, 0o444, 0);
  1731   istream.QueryInterface(AUS_Ci.nsILineInputStream);
  1733   let hasMore;
  1734   let line = {};
  1735   do {
  1736     hasMore = istream.readLine(line);
  1737     fileRelPaths.push(line.value);
  1738   } while(hasMore);
  1740   istream.close();
  1742   fileRelPaths.forEach(function CMAF_FLN_FE(aFileRelPath) {
  1743     copyFileToTestAppDir(aFileRelPath);
  1744   });
  1746   logTestInfo("finish - copying or creating symlinks for application files " +
  1747               "for the test");
  1750 /**
  1751  * Copies the specified files from the dist/bin directory into the test's
  1752  * application directory.
  1754  * @param  aFileRelPath
  1755  *         The relative path of the file to copy.
  1756  */
  1757 function copyFileToTestAppDir(aFileRelPath) {
  1758   let fileRelPath = aFileRelPath;
  1759   let srcFile = gGREDirOrig.clone();
  1760   let pathParts = fileRelPath.split("/");
  1761   for (let i = 0; i < pathParts.length; i++) {
  1762     if (pathParts[i]) {
  1763       srcFile.append(pathParts[i]);
  1767   if (IS_MACOSX && !srcFile.exists()) {
  1768     logTestInfo("unable to copy file since it doesn't exist! Checking if " +
  1769                  fileRelPath + ".app exists. Path: " +
  1770                  srcFile.path);
  1771     srcFile = gGREDirOrig.clone();
  1772     for (let i = 0; i < pathParts.length; i++) {
  1773       if (pathParts[i]) {
  1774         srcFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : ""));
  1777     fileRelPath = fileRelPath + ".app";
  1780   if (!srcFile.exists()) {
  1781     do_throw("Unable to copy file since it doesn't exist! Path: " +
  1782              srcFile.path);
  1785   // Symlink libraries. Note that the XUL library on Mac OS X doesn't have a
  1786   // file extension and this will always be false on Windows.
  1787   let shouldSymlink = (pathParts[pathParts.length - 1] == "XUL" ||
  1788                        fileRelPath.substr(fileRelPath.length - 3) == ".so" ||
  1789                        fileRelPath.substr(fileRelPath.length - 6) == ".dylib");
  1790   let destFile = getApplyDirFile(DIR_BIN_REL_PATH + fileRelPath, true);
  1791   if (!shouldSymlink) {
  1792     if (!destFile.exists()) {
  1793       try {
  1794         srcFile.copyToFollowingLinks(destFile.parent, destFile.leafName);
  1795       } catch (e) {
  1796         // Just in case it is partially copied
  1797         if (destFile.exists()) {
  1798           try {
  1799             destFile.remove(true);
  1800           } catch (e) {
  1801             logTestInfo("unable to remove file that failed to copy! Path: " +
  1802                         destFile.path);
  1805         do_throw("Unable to copy file! Path: " + srcFile.path +
  1806                  ", Exception: " + e);
  1809   } else {
  1810     try {
  1811       if (destFile.exists()) {
  1812         destFile.remove(false);
  1814       let ln = AUS_Cc["@mozilla.org/file/local;1"].createInstance(AUS_Ci.nsILocalFile);
  1815       ln.initWithPath("/bin/ln");
  1816       let process = AUS_Cc["@mozilla.org/process/util;1"].createInstance(AUS_Ci.nsIProcess);
  1817       process.init(ln);
  1818       let args = ["-s", srcFile.path, destFile.path];
  1819       process.run(true, args, args.length);
  1820       logTestInfo("verifying symlink. Path: " + destFile.path);
  1821       do_check_true(destFile.isSymlink());
  1822     } catch (e) {
  1823       do_throw("Unable to create symlink for file! Path: " + srcFile.path +
  1824                ", Exception: " + e);
  1829 /**
  1830  * Attempts to upgrade the maintenance service if permissions are allowed.
  1831  * This is useful for XP where we have permission to upgrade in case an
  1832  * older service installer exists.  Also if the user manually installed into
  1833  * a unprivileged location.
  1834  */
  1835 function attemptServiceInstall() {
  1836   var version = AUS_Cc["@mozilla.org/system-info;1"]
  1837                 .getService(AUS_Ci.nsIPropertyBag2)
  1838                 .getProperty("version");
  1839   var isVistaOrHigher = (parseFloat(version) >= 6.0);
  1840   if (isVistaOrHigher) {
  1841     return;
  1844   let binDir = getGREDir();
  1845   let installerFile = binDir.clone();
  1846   installerFile.append(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN);
  1847   if (!installerFile.exists()) {
  1848     do_throw(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN + " not found.");
  1850   let installerProcess = AUS_Cc["@mozilla.org/process/util;1"].
  1851                          createInstance(AUS_Ci.nsIProcess);
  1852   installerProcess.init(installerFile);
  1853   logTestInfo("starting installer process...");
  1854   installerProcess.run(true, [], 0);
  1857 /**
  1858  * Helper function for updater tests for launching the updater using the
  1859  * maintenance service to apply a mar file.
  1861  * @param aInitialStatus
  1862  *        The initial value of update.status.
  1863  * @param aExpectedStatus
  1864  *        The expected value of update.status when the test finishes.
  1865  * @param aCheckSvcLog
  1866  *        Whether the service log should be checked (optional).
  1867  */
  1868 function runUpdateUsingService(aInitialStatus, aExpectedStatus, aCheckSvcLog) {
  1869   // Check the service logs for a successful update
  1870   function checkServiceLogs(aOriginalContents) {
  1871     let contents = readServiceLogFile();
  1872     logTestInfo("the contents of maintenanceservice.log:\n" + contents + "\n");
  1873     do_check_neq(contents, aOriginalContents);
  1874     do_check_neq(contents.indexOf(LOG_SVC_SUCCESSFUL_LAUNCH), -1);
  1876   function readServiceLogFile() {
  1877     let file = AUS_Cc["@mozilla.org/file/directory_service;1"].
  1878                getService(AUS_Ci.nsIProperties).
  1879                get("CmAppData", AUS_Ci.nsIFile);
  1880     file.append("Mozilla");
  1881     file.append("logs");
  1882     file.append("maintenanceservice.log");
  1883     return readFile(file);
  1885   function waitServiceApps() {
  1886     // maintenanceservice_installer.exe is started async during updates.
  1887     waitForApplicationStop("maintenanceservice_installer.exe");
  1888     // maintenanceservice_tmp.exe is started async from the service installer.
  1889     waitForApplicationStop("maintenanceservice_tmp.exe");
  1890     // In case the SCM thinks the service is stopped, but process still exists.
  1891     waitForApplicationStop("maintenanceservice.exe");
  1893   function waitForServiceStop(aFailTest) {
  1894     waitServiceApps();
  1895     logTestInfo("waiting for service to stop if necessary...");
  1896     // Use the helper bin to ensure the service is stopped. If not
  1897     // stopped then wait for the service to be stopped (at most 120 seconds)
  1898     let helperBin = getTestDirFile(FILE_HELPER_BIN);
  1899     let helperBinArgs = ["wait-for-service-stop",
  1900                          "MozillaMaintenance",
  1901                          "120"];
  1902     let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"].
  1903                            createInstance(AUS_Ci.nsIProcess);
  1904     helperBinProcess.init(helperBin);
  1905     logTestInfo("stopping service...");
  1906     helperBinProcess.run(true, helperBinArgs, helperBinArgs.length);
  1907     if (helperBinProcess.exitValue == 0xEE) {
  1908       do_throw("The service does not exist on this machine.  Return value: " +
  1909                helperBinProcess.exitValue);
  1910     } else if (helperBinProcess.exitValue != 0) {
  1911       if (aFailTest) {
  1912         do_throw("maintenance service did not stop, last state: " +
  1913                  helperBinProcess.exitValue + ". Forcing test failure.");
  1914       } else {
  1915         logTestInfo("maintenance service did not stop, last state: " +
  1916                     helperBinProcess.exitValue + ".  May cause failures.");
  1918     } else {
  1919       logTestInfo("service stopped.");
  1921     waitServiceApps();
  1923   function waitForApplicationStop(aApplication) {
  1924     logTestInfo("waiting for " + aApplication + " to stop if " +
  1925                 "necessary...");
  1926     // Use the helper bin to ensure the application is stopped.
  1927     // If not, then wait for it to be stopped (at most 120 seconds)
  1928     let helperBin = getTestDirFile(FILE_HELPER_BIN);
  1929     let helperBinArgs = ["wait-for-application-exit",
  1930                          aApplication,
  1931                          "120"];
  1932     let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"].
  1933                            createInstance(AUS_Ci.nsIProcess);
  1934     helperBinProcess.init(helperBin);
  1935     helperBinProcess.run(true, helperBinArgs, helperBinArgs.length);
  1936     if (helperBinProcess.exitValue != 0) {
  1937       do_throw(aApplication + " did not stop, last state: " +
  1938                helperBinProcess.exitValue + ". Forcing test failure.");
  1942   // Make sure the service from the previous test is already stopped.
  1943   waitForServiceStop(true);
  1945   // Prevent the cleanup function from begin run more than once
  1946   if (gRegisteredServiceCleanup === undefined) {
  1947     gRegisteredServiceCleanup = true;
  1949     do_register_cleanup(function RUUS_cleanup() {
  1950       resetEnvironment();
  1952       // This will delete the app arguments log file if it exists.
  1953       try {
  1954         getAppArgsLogPath();
  1955       } catch (e) {
  1956         logTestInfo("unable to remove file during cleanup. Exception: " + e);
  1958     });
  1961   let svcOriginalLog;
  1962   // Default to checking the service log if the parameter is not specified.
  1963   if (aCheckSvcLog === undefined || aCheckSvcLog) {
  1964     svcOriginalLog = readServiceLogFile();
  1967   let appArgsLogPath = getAppArgsLogPath();
  1968   gServiceLaunchedCallbackLog = appArgsLogPath.replace(/^"|"$/g, "");
  1970   let updatesDir = getUpdatesPatchDir();
  1971   let file = updatesDir.clone();
  1972   writeStatusFile(aInitialStatus);
  1974   // sanity check
  1975   do_check_eq(readStatusState(), aInitialStatus);
  1977   writeVersionFile(DEFAULT_UPDATE_VERSION);
  1979   gServiceLaunchedCallbackArgs = [
  1980     "-no-remote",
  1981     "-process-updates",
  1982     "-dump-args",
  1983     appArgsLogPath
  1984   ];
  1986   if (gSwitchApp) {
  1987     // We want to set the env vars again
  1988     gShouldResetEnv = undefined;
  1991   setEnvironment();
  1993   // There is a security check done by the service to make sure the updater
  1994   // we are executing is the same as the one in the apply-to dir.
  1995   // To make sure they match from tests we copy updater.exe to the apply-to dir.
  1996   copyFileToTestAppDir(FILE_UPDATER_BIN);
  1998   // The service will execute maintenanceservice_installer.exe and
  1999   // will copy maintenanceservice.exe out of the same directory from
  2000   // the installation directory.  So we need to make sure both of those
  2001   // bins always exist in the installation directory.
  2002   copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_BIN);
  2003   copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN);
  2005   let launchBin = getLaunchBin();
  2006   let args = getProcessArgs(["-dump-args", appArgsLogPath]);
  2008   let process = AUS_Cc["@mozilla.org/process/util;1"].
  2009                    createInstance(AUS_Ci.nsIProcess);
  2010   process.init(launchBin);
  2011   logTestInfo("launching " + launchBin.path + " " + args.join(" "));
  2012   // Firefox does not wait for the service command to finish, but
  2013   // we still launch the process sync to avoid intermittent failures with
  2014   // the log file not being written out yet.
  2015   // We will rely on watching the update.status file and waiting for the service
  2016   // to stop to know the service command is done.
  2017   process.run(true, args, args.length);
  2019   resetEnvironment();
  2021   function timerCallback(aTimer) {
  2022     // Wait for the expected status
  2023     let status = readStatusState();
  2024     // status will probably always be equal to STATE_APPLYING but there is a
  2025     // race condition where it would be possible on slower machines where status
  2026     // could be equal to STATE_PENDING_SVC.
  2027     if (status == STATE_APPLYING ||
  2028         status == STATE_PENDING_SVC) {
  2029       logTestInfo("still waiting to see the " + aExpectedStatus +
  2030                   " status, got " + status + " for now...");
  2031       return;
  2034     // Make sure all of the logs are written out.
  2035     waitForServiceStop(false);
  2037     aTimer.cancel();
  2038     aTimer = null;
  2040     if (status != aExpectedStatus) {
  2041       logTestInfo("update status is not the expected status! Got: " + status +
  2042                   ", Expected: " +  aExpectedStatus);
  2043       logTestInfo("update.status contents: " + readStatusFile());
  2044       let updateLog = getUpdatesPatchDir();
  2045       updateLog.append(FILE_UPDATE_LOG);
  2046       logTestInfo("contents of " + updateLog.path + ":\n" +
  2047                   readFileBytes(updateLog).replace(/\r\n/g, "\n"));
  2049     logTestInfo("testing update status against expected status");
  2050     do_check_eq(status, aExpectedStatus);
  2052     if (aCheckSvcLog) {
  2053       checkServiceLogs(svcOriginalLog);
  2056     checkUpdateFinished();
  2059   let timer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer);
  2060   timer.initWithCallback(timerCallback, 1000, timer.TYPE_REPEATING_SLACK);
  2063 /**
  2064  * Gets the platform specific shell binary that is launched using nsIProcess and
  2065  * in turn launches a binary used for the test (e.g. application, updater,
  2066  * etc.). A shell is used so debug console output can be redirected to a file so
  2067  * it doesn't end up in the test log.
  2069  * @return  nsIFile for the shell binary to launch using nsIProcess.
  2070  * @throws  if the shell binary doesn't exist.
  2071  */
  2072 function getLaunchBin() {
  2073   let launchBin;
  2074   if (IS_WIN) {
  2075     launchBin = Services.dirsvc.get("WinD", AUS_Ci.nsIFile);
  2076     launchBin.append("System32");
  2077     launchBin.append("cmd.exe");
  2078   } else {
  2079     launchBin = AUS_Cc["@mozilla.org/file/local;1"].
  2080                 createInstance(AUS_Ci.nsILocalFile);
  2081     launchBin.initWithPath("/bin/sh");
  2084   if (!launchBin.exists())
  2085     do_throw(launchBin.path + " must exist to run this test!");
  2087   return launchBin;
  2090 /**
  2091  * Helper function that waits until the helper has completed its operations and
  2092  * is in a sleep state before performing an update by calling doUpdate.
  2093  */
  2094 function waitForHelperSleep() {
  2095   gTimeoutRuns++;
  2096   // Give the lock file process time to lock the file before updating otherwise
  2097   // this test can fail intermittently on Windows debug builds.
  2098   let output = getApplyDirFile("a/b/output", true);
  2099   if (readFile(output) != "sleeping\n") {
  2100     if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
  2101       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the helper to " +
  2102                "finish its operation. Path: " + output.path);
  2104     do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
  2105     return;
  2107   try {
  2108     output.remove(false);
  2110   catch (e) {
  2111     if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
  2112       do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the helper " +
  2113                "message file to no longer be in use. Path: " + output.path);
  2115     logTestInfo("failed to remove file. Path: " + output.path);
  2116     do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
  2117     return;
  2119   doUpdate();
  2122 /**
  2123  * Helper function that waits until the helper has finished its operations
  2124  * before calling waitForHelperFinishFileUnlock to verify that the helper's
  2125  * input and output directories are no longer in use.
  2126  */
  2127 function waitForHelperFinished() {
  2128   // Give the lock file process time to lock the file before updating otherwise
  2129   // this test can fail intermittently on Windows debug builds.
  2130   let output = getApplyDirFile("a/b/output", true);
  2131   if (readFile(output) != "finished\n") {
  2132     do_timeout(TEST_HELPER_TIMEOUT, waitForHelperFinished);
  2133     return;
  2135   // Give the lock file process time to unlock the file before deleting the
  2136   // input and output files.
  2137   waitForHelperFinishFileUnlock();
  2140 /**
  2141  * Helper function that waits until the helper's input and output files are no
  2142  * longer in use before calling checkUpdate.
  2143  */
  2144 function waitForHelperFinishFileUnlock() {
  2145   try {
  2146     let output = getApplyDirFile("a/b/output", true);
  2147     if (output.exists()) {
  2148       output.remove(false);
  2150     let input = getApplyDirFile("a/b/input", true);
  2151     if (input.exists()) {
  2152       input.remove(false);
  2154   } catch (e) {
  2155     // Give the lock file process time to unlock the file before deleting the
  2156     // input and output files.
  2157     do_timeout(TEST_HELPER_TIMEOUT, waitForHelperFinishFileUnlock);
  2158     return;
  2160   checkUpdate();
  2163 /**
  2164  * Helper function to tell the helper to finish and exit its sleep state.
  2165  */
  2166 function setupHelperFinish() {
  2167   let input = getApplyDirFile("a/b/input", true);
  2168   writeFile(input, "finish\n");
  2169   waitForHelperFinished();
  2172 /**
  2173  * Helper function for updater binary tests that creates the files and
  2174  * directories used by the test.
  2176  * @param   aMarFile
  2177  *          The mar file for the update test.
  2178  */
  2179 function setupUpdaterTest(aMarFile, aUpdatedDirExists, aToBeDeletedDirExists) {
  2180   let updatesPatchDir = getUpdatesPatchDir();
  2181   if (!updatesPatchDir.exists()) {
  2182     updatesPatchDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
  2184   // Copy the mar that will be applied
  2185   let mar = getTestDirFile(aMarFile);
  2186   mar.copyToFollowingLinks(updatesPatchDir, FILE_UPDATE_ARCHIVE);
  2188   createUpdateSettingsINI();
  2190   let applyToDir = getApplyDirFile(null, true);
  2191   gTestFiles.forEach(function SUT_TF_FE(aTestFile) {
  2192     if (aTestFile.originalFile || aTestFile.originalContents) {
  2193       let testDir = getApplyDirFile(aTestFile.relPathDir, true);
  2194       if (!testDir.exists())
  2195         testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
  2197       let testFile;
  2198       if (aTestFile.originalFile) {
  2199         testFile = getTestDirFile(aTestFile.originalFile);
  2200         testFile.copyToFollowingLinks(testDir, aTestFile.fileName);
  2201         testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName);
  2202       } else {
  2203         testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName,
  2204                                    true);
  2205         writeFile(testFile, aTestFile.originalContents);
  2208       // Skip these tests on Windows and OS/2 since their
  2209       // implementaions of chmod doesn't really set permissions.
  2210       if (!IS_WIN && aTestFile.originalPerms) {
  2211         testFile.permissions = aTestFile.originalPerms;
  2212         // Store the actual permissions on the file for reference later after
  2213         // setting the permissions.
  2214         if (!aTestFile.comparePerms) {
  2215           aTestFile.comparePerms = testFile.permissions;
  2219   });
  2221   let helperBin = getTestDirFile(FILE_HELPER_BIN);
  2222   let afterApplyBinDir = getApplyDirFile("a/b/", true);
  2223   helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile);
  2225   // Add the test directory that will be updated for a successful update or left
  2226   // in the initial state for a failed update.
  2227   gTestDirs.forEach(function SUT_TD_FE(aTestDir) {
  2228     let testDir = getApplyDirFile(aTestDir.relPathDir, true);
  2229     if (!testDir.exists()) {
  2230       testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
  2233     if (aTestDir.files) {
  2234       aTestDir.files.forEach(function SUT_TD_F_FE(aTestFile) {
  2235         let testFile = getApplyDirFile(aTestDir.relPathDir + aTestFile, true);
  2236         if (!testFile.exists()) {
  2237           testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
  2239       });
  2242     if (aTestDir.subDirs) {
  2243       aTestDir.subDirs.forEach(function SUT_TD_SD_FE(aSubDir) {
  2244         let testSubDir = getApplyDirFile(aTestDir.relPathDir + aSubDir, true);
  2245         if (!testSubDir.exists()) {
  2246           testSubDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
  2249         if (aTestDir.subDirFiles) {
  2250           aTestDir.subDirFiles.forEach(function SUT_TD_SDF_FE(aTestFile) {
  2251             let testFile = getApplyDirFile(aTestDir.relPathDir + aSubDir + aTestFile, true);
  2252             if (!testFile.exists()) {
  2253               testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
  2255           });
  2257       });
  2259   });
  2261   gTestExtraDirs[0].dirExists = aUpdatedDirExists;
  2262   gTestExtraDirs[1].dirExists = IS_WIN ? aToBeDeletedDirExists : false;
  2265 /**
  2266  * Helper function for updater binary tests that creates the update-settings.ini
  2267  * file.
  2268  */
  2269 function createUpdateSettingsINI() {
  2270   updateSettingsIni = getApplyDirFile(null, true);
  2271   if (IS_MACOSX) {
  2272     updateSettingsIni.append("Contents");
  2273     updateSettingsIni.append("MacOS");
  2275   updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI);
  2276   writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS);
  2279 /**
  2280  * Helper function for updater binary tests for verifying the contents of the
  2281  * update log after a successful update.
  2283  * @param   aCompareLogFile
  2284  *          The log file to compare the update log with.
  2285  */
  2286 function checkUpdateLogContents(aCompareLogFile) {
  2287   let updateLog = getUpdatesPatchDir();
  2288   updateLog.append(FILE_UPDATE_LOG);
  2289   let updateLogContents = readFileBytes(updateLog);
  2291   // The channel-prefs.js is defined in gTestFilesCommon which will always be
  2292   // located to the end of gTestFiles.
  2293   if (gTestFiles.length > 1 &&
  2294 	  gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" &&
  2295 	  !gTestFiles[gTestFiles.length - 1].originalContents) {
  2296     updateLogContents = updateLogContents.replace(/.* a\/b\/defaults\/.*/g, "");
  2298   if (gTestFiles.length > 2 &&
  2299 	  gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI &&
  2300 	  !gTestFiles[gTestFiles.length - 2].originalContents) {
  2301     updateLogContents = updateLogContents.replace(/.* a\/b\/update-settings.ini.*/g, "");
  2303   if (gStageUpdate) {
  2304     // Skip the staged update messages
  2305     updateLogContents = updateLogContents.replace(/Performing a staged update/, "");
  2306   } else if (gSwitchApp) {
  2307     // Skip the switch app request messages
  2308     updateLogContents = updateLogContents.replace(/Performing a staged update/, "");
  2309     updateLogContents = updateLogContents.replace(/Performing a replace request/, "");
  2311   // Skip the source/destination lines since they contain absolute paths.
  2312   updateLogContents = updateLogContents.replace(/SOURCE DIRECTORY.*/g, "");
  2313   updateLogContents = updateLogContents.replace(/DESTINATION DIRECTORY.*/g, "");
  2314   // Skip lines that log failed attempts to open the callback executable.
  2315   updateLogContents = updateLogContents.replace(/NS_main: callback app file .*/g, "");
  2316   if (gSwitchApp) {
  2317     // Remove the lines which contain absolute paths
  2318     updateLogContents = updateLogContents.replace(/^Begin moving.*$/mg, "");
  2319     if (IS_MACOSX) {
  2320       // Remove the entire section about moving the precomplete file as it contains
  2321       // absolute paths.
  2322       updateLogContents = updateLogContents.replace(/\n/g, "%%%EOL%%%");
  2323       updateLogContents = updateLogContents.replace(/Moving the precomplete file.*Finished moving the precomplete file/, "");
  2324       updateLogContents = updateLogContents.replace(/%%%EOL%%%/g, "\n");
  2327   updateLogContents = updateLogContents.replace(/\r/g, "");
  2328   // Replace error codes since they are different on each platform.
  2329   updateLogContents = updateLogContents.replace(/, err:.*\n/g, "\n");
  2330   // Replace to make the log parsing happy.
  2331   updateLogContents = updateLogContents.replace(/non-fatal error /g, "");
  2332   // The FindFile results when enumerating the filesystem on Windows is not
  2333   // determistic so the results matching the following need to be ignored.
  2334   updateLogContents = updateLogContents.replace(/.* a\/b\/7\/7text.*\n/g, "");
  2335   // Remove consecutive newlines
  2336   updateLogContents = updateLogContents.replace(/\n+/g, "\n");
  2337   // Remove leading and trailing newlines
  2338   updateLogContents = updateLogContents.replace(/^\n|\n$/g, "");
  2339   // The update log when running the service tests sometimes starts with data
  2340   // from the previous launch of the updater.
  2341   updateLogContents = updateLogContents.replace(/^calling QuitProgressUI\n[^\n]*\nUPDATE TYPE/g, "UPDATE TYPE");
  2343   let compareLogContents = "";
  2344   if (aCompareLogFile) {
  2345     compareLogContents = readFileBytes(getTestDirFile(aCompareLogFile));
  2347   if (gSwitchApp) {
  2348     compareLogContents += LOG_SWITCH_SUCCESS;
  2350   // The channel-prefs.js is defined in gTestFilesCommon which will always be
  2351   // located to the end of gTestFiles.
  2352   if (gTestFiles.length > 1 &&
  2353 	  gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" &&
  2354 	  !gTestFiles[gTestFiles.length - 1].originalContents) {
  2355     compareLogContents = compareLogContents.replace(/.* a\/b\/defaults\/.*/g, "");
  2357   if (gTestFiles.length > 2 &&
  2358 	  gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI &&
  2359 	  !gTestFiles[gTestFiles.length - 2].originalContents) {
  2360     compareLogContents = compareLogContents.replace(/.* a\/b\/update-settings.ini.*/g, "");
  2362   // Remove leading and trailing newlines
  2363   compareLogContents = compareLogContents.replace(/\n+/g, "\n");
  2364   // Remove leading and trailing newlines
  2365   compareLogContents = compareLogContents.replace(/^\n|\n$/g, "");
  2367   // Don't write the contents of the file to the log to reduce log spam
  2368   // unless there is a failure.
  2369   if (compareLogContents == updateLogContents) {
  2370     logTestInfo("log contents are correct");
  2371     do_check_true(true);
  2372   } else {
  2373     logTestInfo("log contents are not correct");
  2374     do_check_eq(compareLogContents, updateLogContents);
  2378 /**
  2379  * Helper function to check if the update log contains a string.
  2381  * @param   aCheckString
  2382  *          The string to check if the update log contains.
  2383  */
  2384 function checkUpdateLogContains(aCheckString) {
  2385   let updateLog = getUpdatesPatchDir();
  2386   updateLog.append(FILE_UPDATE_LOG);
  2387   let updateLogContents = readFileBytes(updateLog);
  2388   if (updateLogContents.indexOf(aCheckString) != -1) {
  2389     logTestInfo("log file does contain: " + aCheckString);
  2390     do_check_true(true);
  2391   } else {
  2392     logTestInfo("log file does not contain: " + aCheckString);
  2393     logTestInfo("log file contents:\n" + updateLogContents);
  2394     do_check_true(false);
  2398 /**
  2399  * Helper function for updater binary tests for verifying the state of files and
  2400  * directories after a successful update.
  2401  */
  2402 function checkFilesAfterUpdateSuccess() {
  2403   logTestInfo("testing contents of files after a successful update");
  2404   gTestFiles.forEach(function CFAUS_TF_FE(aTestFile) {
  2405     let testFile = getTargetDirFile(aTestFile.relPathDir + aTestFile.fileName,
  2406                                     true);
  2407     logTestInfo("testing file: " + testFile.path);
  2408     if (aTestFile.compareFile || aTestFile.compareContents) {
  2409       do_check_true(testFile.exists());
  2411       // Skip these tests on Windows and OS/2 since their
  2412       // implementaions of chmod doesn't really set permissions.
  2413       if (!IS_WIN && aTestFile.comparePerms) {
  2414         // Check if the permssions as set in the complete mar file are correct.
  2415         let logPerms = "testing file permissions - ";
  2416         if (aTestFile.originalPerms) {
  2417           logPerms += "original permissions: " +
  2418                       aTestFile.originalPerms.toString(8) + ", ";
  2420         logPerms += "compare permissions : " +
  2421                     aTestFile.comparePerms.toString(8) + ", ";
  2422         logPerms += "updated permissions : " + testFile.permissions.toString(8);
  2423         logTestInfo(logPerms);
  2424         do_check_eq(testFile.permissions & 0xfff, aTestFile.comparePerms & 0xfff);
  2427       let fileContents1 = readFileBytes(testFile);
  2428       let fileContents2 = aTestFile.compareFile ?
  2429                           readFileBytes(getTestDirFile(aTestFile.compareFile)) :
  2430                           aTestFile.compareContents;
  2431       // Don't write the contents of the file to the log to reduce log spam
  2432       // unless there is a failure.
  2433       if (fileContents1 == fileContents2) {
  2434         logTestInfo("file contents are correct");
  2435         do_check_true(true);
  2436       } else {
  2437         logTestInfo("file contents are not correct");
  2438         do_check_eq(fileContents1, fileContents2);
  2440     } else {
  2441       do_check_false(testFile.exists());
  2443   });
  2445   logTestInfo("testing operations specified in removed-files were performed " +
  2446               "after a successful update");
  2447   gTestDirs.forEach(function CFAUS_TD_FE(aTestDir) {
  2448     let testDir = getTargetDirFile(aTestDir.relPathDir, true);
  2449     logTestInfo("testing directory: " + testDir.path);
  2450     if (aTestDir.dirRemoved) {
  2451       do_check_false(testDir.exists());
  2452     } else {
  2453       do_check_true(testDir.exists());
  2455       if (aTestDir.files) {
  2456         aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) {
  2457           let testFile = getTargetDirFile(aTestDir.relPathDir + aTestFile, true);
  2458           logTestInfo("testing directory file: " + testFile.path);
  2459           if (aTestDir.filesRemoved) {
  2460             do_check_false(testFile.exists());
  2461           } else {
  2462             do_check_true(testFile.exists());
  2464         });
  2467       if (aTestDir.subDirs) {
  2468         aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) {
  2469           let testSubDir = getTargetDirFile(aTestDir.relPathDir + aSubDir, true);
  2470           logTestInfo("testing sub-directory: " + testSubDir.path);
  2471           do_check_true(testSubDir.exists());
  2472           if (aTestDir.subDirFiles) {
  2473             aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) {
  2474               let testFile = getTargetDirFile(aTestDir.relPathDir + aSubDir + aTestFile, true);
  2475               logTestInfo("testing sub-directory file: " + testFile.path);
  2476               do_check_true(testFile.exists());
  2477             });
  2479         });
  2482   });
  2484   checkFilesAfterUpdateCommon();
  2487 /**
  2488  * Helper function for updater binary tests for verifying the state of files and
  2489  * directories after a failed update.
  2491  * @param aGetDirectory: the function used to get the files in the target directory.
  2492  * Pass getApplyDirFile if you want to test the case of a failed switch request.
  2493  */
  2494 function checkFilesAfterUpdateFailure(aGetDirectory) {
  2495   let getdir = aGetDirectory || getTargetDirFile;
  2496   logTestInfo("testing contents of files after a failed update");
  2497   gTestFiles.forEach(function CFAUF_TF_FE(aTestFile) {
  2498     let testFile = getdir(aTestFile.relPathDir + aTestFile.fileName, true);
  2499     logTestInfo("testing file: " + testFile.path);
  2500     if (aTestFile.compareFile || aTestFile.compareContents) {
  2501       do_check_true(testFile.exists());
  2503       // Skip these tests on Windows and OS/2 since their
  2504       // implementaions of chmod doesn't really set permissions.
  2505       if (!IS_WIN && aTestFile.comparePerms) {
  2506         // Check the original permssions are retained on the file.
  2507         let logPerms = "testing file permissions - ";
  2508         if (aTestFile.originalPerms) {
  2509           logPerms += "original permissions: " +
  2510                       aTestFile.originalPerms.toString(8) + ", ";
  2512         logPerms += "compare permissions : " +
  2513                     aTestFile.comparePerms.toString(8) + ", ";
  2514         logPerms += "updated permissions : " + testFile.permissions.toString(8);
  2515         logTestInfo(logPerms);
  2516         do_check_eq(testFile.permissions & 0xfff, aTestFile.comparePerms & 0xfff);
  2519       let fileContents1 = readFileBytes(testFile);
  2520       let fileContents2 = aTestFile.compareFile ?
  2521                           readFileBytes(getTestDirFile(aTestFile.compareFile)) :
  2522                           aTestFile.compareContents;
  2523       // Don't write the contents of the file to the log to reduce log spam
  2524       // unless there is a failure.
  2525       if (fileContents1 == fileContents2) {
  2526         logTestInfo("file contents are correct");
  2527         do_check_true(true);
  2528       } else {
  2529         logTestInfo("file contents are not correct");
  2530         do_check_eq(fileContents1, fileContents2);
  2532     } else {
  2533       do_check_false(testFile.exists());
  2535   });
  2537   logTestInfo("testing operations specified in removed-files were not " +
  2538               "performed after a failed update");
  2539   gTestDirs.forEach(function CFAUF_TD_FE(aTestDir) {
  2540     let testDir = getdir(aTestDir.relPathDir, true);
  2541     logTestInfo("testing directory: " + testDir.path);
  2542     do_check_true(testDir.exists());
  2544     if (aTestDir.files) {
  2545       aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) {
  2546         let testFile = getdir(aTestDir.relPathDir + aTestFile, true);
  2547         logTestInfo("testing directory file: " + testFile.path);
  2548         do_check_true(testFile.exists());
  2549       });
  2552     if (aTestDir.subDirs) {
  2553       aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) {
  2554         let testSubDir = getdir(aTestDir.relPathDir + aSubDir, true);
  2555         logTestInfo("testing sub-directory: " + testSubDir.path);
  2556         do_check_true(testSubDir.exists());
  2557         if (aTestDir.subDirFiles) {
  2558           aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) {
  2559             let testFile = getdir(aTestDir.relPathDir + aSubDir + aTestFile,
  2560                                   true);
  2561             logTestInfo("testing sub-directory file: " + testFile.path);
  2562             do_check_true(testFile.exists());
  2563           });
  2565       });
  2567   });
  2569   checkFilesAfterUpdateCommon();
  2572 /**
  2573  * Helper function for updater binary tests for verifying the state of common
  2574  * files and directories after a successful or failed update.
  2575  */
  2576 function checkFilesAfterUpdateCommon() {
  2577   logTestInfo("testing extra directories");
  2578   gTestExtraDirs.forEach(function CFAUC_TED_FE(aTestExtraDir) {
  2579     let testDir = getTargetDirFile(aTestExtraDir.relPathDir, true);
  2580     logTestInfo("testing directory: " + testDir.path);
  2581     if (aTestExtraDir.dirExists) {
  2582       do_check_true(testDir.exists());
  2583     } else {
  2584       do_check_false(testDir.exists());
  2586   });
  2588   logTestInfo("testing updating directory doesn't exist in the application " +
  2589 	          "directory");
  2590   let updatingDir = getTargetDirFile("updating", true);
  2591   do_check_false(updatingDir.exists());
  2593   if (gStageUpdate) {
  2594     logTestInfo("testing updating directory doesn't exist in the updated " +
  2595 		        "directory");
  2596     updatingDir = getApplyDirFile("updating", true);
  2597     do_check_false(updatingDir.exists());
  2599     // This should never exist since the update was applied to the updated
  2600 	// directory and the files should never be in use.
  2601     logTestInfo("testing tobedeleted directory doesn't exist in the updated " +
  2602                 "directory");
  2603     let toBeDeletedDir = getApplyDirFile(DIR_TOBEDELETED, true);
  2604     do_check_false(toBeDeletedDir.exists());
  2607   logTestInfo("testing patch files should not be left behind");
  2608   let updatesDir = getUpdatesPatchDir();
  2609   let entries = updatesDir.QueryInterface(AUS_Ci.nsIFile).directoryEntries;
  2610   while (entries.hasMoreElements()) {
  2611     let entry = entries.getNext().QueryInterface(AUS_Ci.nsIFile);
  2612     do_check_neq(getFileExtension(entry), "patch");
  2615   logTestInfo("testing backup files should not be left behind");
  2616   let applyToDir = getTargetDirFile(null, true);
  2617   checkFilesInDirRecursive(applyToDir, checkForBackupFiles);
  2620 /**
  2621  * Helper function for updater binary tests for verifying the contents of the
  2622  * updater callback application log which should contain the arguments passed to
  2623  * the callback application.
  2624  */
  2625 function checkCallbackAppLog() {
  2626   let appLaunchLog = getApplyDirFile("a/b/" + gCallbackArgs[1], true);
  2627   if (!appLaunchLog.exists()) {
  2628     do_timeout(TEST_HELPER_TIMEOUT, checkCallbackAppLog);
  2629     return;
  2632   let expectedLogContents = gCallbackArgs.join("\n") + "\n";
  2633   let logContents = readFile(appLaunchLog);
  2634   // It is possible for the log file contents check to occur before the log file
  2635   // contents are completely written so wait until the contents are the expected
  2636   // value. If the contents are never the expected value then the test will
  2637   // fail by timing out.
  2638   if (logContents != expectedLogContents) {
  2639     do_timeout(TEST_HELPER_TIMEOUT, checkCallbackAppLog);
  2640     return;
  2643   if (logContents == expectedLogContents) {
  2644     logTestInfo("callback log file contents are correct");
  2645     do_check_true(true);
  2646   } else {
  2647     logTestInfo("callback log file contents are not correct");
  2648     do_check_eq(logContents, expectedLogContents);
  2651   waitForFilesInUse();
  2654 /**
  2655  * Helper function for updater service tests for verifying the contents of the
  2656  * updater callback application log which should contain the arguments passed to
  2657  * the callback application.
  2658  */
  2659 function checkCallbackServiceLog() {
  2660   do_check_neq(gServiceLaunchedCallbackLog, null);
  2662   let expectedLogContents = gServiceLaunchedCallbackArgs.join("\n") + "\n";
  2663   let logFile = AUS_Cc["@mozilla.org/file/local;1"].createInstance(AUS_Ci.nsILocalFile);
  2664   logFile.initWithPath(gServiceLaunchedCallbackLog);
  2665   let logContents = readFile(logFile);
  2666   // It is possible for the log file contents check to occur before the log file
  2667   // contents are completely written so wait until the contents are the expected
  2668   // value. If the contents are never the expected value then the test will
  2669   // fail by timing out.
  2670   if (logContents != expectedLogContents) {
  2671     logTestInfo("callback service log not expected value, waiting longer");
  2672     do_timeout(TEST_HELPER_TIMEOUT, checkCallbackServiceLog);
  2673     return;
  2676   logTestInfo("testing that the callback application successfully launched " +
  2677               "and the expected command line arguments were passed to it");
  2678   do_check_eq(logContents, expectedLogContents);
  2680   waitForFilesInUse();
  2683 // Waits until files that are in use that break tests are no longer in use and
  2684 // then calls do_test_finished.
  2685 function waitForFilesInUse() {
  2686   if (IS_WIN) {
  2687     let appBin = getApplyDirFile(FILE_APP_BIN, true);
  2688     let maintSvcInstaller = getApplyDirFile(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN, true);
  2689     let helper = getApplyDirFile("uninstall/helper.exe", true);
  2690     let updater = getUpdatesPatchDir();
  2691     updater.append(FILE_UPDATER_BIN);
  2693     let files = [appBin, updater, maintSvcInstaller, helper];
  2695     for (var i = 0; i < files.length; ++i) {
  2696       let file = files[i];
  2697       let fileBak = file.parent.clone();
  2698       if (file.exists()) {
  2699         fileBak.append(file.leafName + ".bak");
  2700         try {
  2701           if (fileBak.exists()) {
  2702             fileBak.remove(false);
  2704           file.copyTo(fileBak.parent, fileBak.leafName);
  2705           file.remove(false);
  2706           fileBak.moveTo(file.parent, file.leafName);
  2707           logTestInfo("file is not in use. Path: " + file.path);
  2708         } catch (e) {
  2709           logTestInfo("file in use, will try again after " + TEST_CHECK_TIMEOUT +
  2710                       " ms, Path: " + file.path + ", Exception: " + e);
  2711           try {
  2712             if (fileBak.exists()) {
  2713               fileBak.remove(false);
  2715           } catch (e) {
  2716             logTestInfo("unable to remove file, this should never happen! " +
  2717                         "Path: " + fileBak.path + ", Exception: " + e);
  2719           do_timeout(TEST_CHECK_TIMEOUT, waitForFilesInUse);
  2720           return;
  2726   logTestInfo("calling doTestFinish");
  2727   doTestFinish();
  2730 /**
  2731  * Helper function for updater binary tests for verifying there are no update
  2732  * backup files left behind after an update.
  2734  * @param   aFile
  2735  *          An nsIFile to check if it has moz-backup for its extension.
  2736  */
  2737 function checkForBackupFiles(aFile) {
  2738   do_check_neq(getFileExtension(aFile), "moz-backup");
  2741 /**
  2742  * Helper function for updater binary tests for recursively enumerating a
  2743  * directory and calling a callback function with the file as a parameter for
  2744  * each file found.
  2746  * @param   aDir
  2747  *          A nsIFile for the directory to be deleted
  2748  * @param   aCallback
  2749  *          A callback function that will be called with the file as a
  2750  *          parameter for each file found.
  2751  */
  2752 function checkFilesInDirRecursive(aDir, aCallback) {
  2753   if (!aDir.exists())
  2754     do_throw("Directory must exist!");
  2756   let dirEntries = aDir.directoryEntries;
  2757   while (dirEntries.hasMoreElements()) {
  2758     let entry = dirEntries.getNext().QueryInterface(AUS_Ci.nsIFile);
  2760     if (entry.isDirectory()) {
  2761       checkFilesInDirRecursive(entry, aCallback);
  2762     } else {
  2763       aCallback(entry);
  2768 /**
  2769  * Sets up the bare bones XMLHttpRequest implementation below.
  2771  * @param   aCallback
  2772  *          The callback function that will call the nsIDomEventListener's
  2773  *          handleEvent method.
  2775  *          Example of the callback function
  2777  *            function callHandleEvent() {
  2778  *              gXHR.status = gExpectedStatus;
  2779  *              var e = { target: gXHR };
  2780  *              gXHR.onload.handleEvent(e);
  2781  *            }
  2782  */
  2783 function overrideXHR(aCallback) {
  2784   gXHRCallback = aCallback;
  2785   gXHR = new xhr();
  2786   var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar);
  2787   registrar.registerFactory(gXHR.classID, gXHR.classDescription,
  2788                             gXHR.contractID, gXHR);
  2792 /**
  2793  * Bare bones XMLHttpRequest implementation for testing onprogress, onerror,
  2794  * and onload nsIDomEventListener handleEvent.
  2795  */
  2796 function makeHandler(aVal) {
  2797   if (typeof aVal == "function")
  2798     return { handleEvent: aVal };
  2799   return aVal;
  2801 function xhr() {
  2803 xhr.prototype = {
  2804   overrideMimeType: function(aMimetype) { },
  2805   setRequestHeader: function(aHeader, aValue) { },
  2806   status: null,
  2807   channel: { set notificationCallbacks(aVal) { } },
  2808   _url: null,
  2809   _method: null,
  2810   open: function(aMethod, aUrl) {
  2811     gXHR.channel.originalURI = Services.io.newURI(aUrl, null, null);
  2812     gXHR._method = aMethod; gXHR._url = aUrl;
  2813   },
  2814   responseXML: null,
  2815   responseText: null,
  2816   send: function(aBody) {
  2817     do_execute_soon(gXHRCallback); // Use a timeout so the XHR completes
  2818   },
  2819   _onprogress: null,
  2820   set onprogress(aValue) { gXHR._onprogress = makeHandler(aValue); },
  2821   get onprogress() { return gXHR._onprogress; },
  2822   _onerror: null,
  2823   set onerror(aValue) { gXHR._onerror = makeHandler(aValue); },
  2824   get onerror() { return gXHR._onerror; },
  2825   _onload: null,
  2826   set onload(aValue) { gXHR._onload = makeHandler(aValue); },
  2827   get onload() { return gXHR._onload; },
  2828   addEventListener: function(aEvent, aValue, aCapturing) {
  2829     eval("gXHR._on" + aEvent + " = aValue");
  2830   },
  2831   flags: AUS_Ci.nsIClassInfo.SINGLETON,
  2832   implementationLanguage: AUS_Ci.nsIProgrammingLanguage.JAVASCRIPT,
  2833   getHelperForLanguage: function(aLanguage) null,
  2834   getInterfaces: function(aCount) {
  2835     var interfaces = [AUS_Ci.nsISupports];
  2836     aCount.value = interfaces.length;
  2837     return interfaces;
  2838   },
  2839   classDescription: "XMLHttpRequest",
  2840   contractID: "@mozilla.org/xmlextras/xmlhttprequest;1",
  2841   classID: Components.ID("{c9b37f43-4278-4304-a5e0-600991ab08cb}"),
  2842   createInstance: function(aOuter, aIID) {
  2843     if (aOuter == null)
  2844       return gXHR.QueryInterface(aIID);
  2845     throw AUS_Cr.NS_ERROR_NO_AGGREGATION;
  2846   },
  2847   QueryInterface: function(aIID) {
  2848     if (aIID.equals(AUS_Ci.nsIClassInfo) ||
  2849         aIID.equals(AUS_Ci.nsISupports))
  2850       return gXHR;
  2851     throw AUS_Cr.NS_ERROR_NO_INTERFACE;
  2852   },
  2853   get wrappedJSObject() { return this; }
  2854 };
  2856 /**
  2857  * Helper function to override the update prompt component to verify whether it
  2858  * is called or not.
  2860  * @param   aCallback
  2861  *          The callback to call if the update prompt component is called.
  2862  */
  2863 function overrideUpdatePrompt(aCallback) {
  2864   var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar);
  2865   gUpdatePrompt = new UpdatePrompt();
  2866   gUpdatePromptCallback = aCallback;
  2867   registrar.registerFactory(gUpdatePrompt.classID, gUpdatePrompt.classDescription,
  2868                             gUpdatePrompt.contractID, gUpdatePrompt);
  2871 function UpdatePrompt() {
  2872   var fns = ["checkForUpdates", "showUpdateAvailable", "showUpdateDownloaded",
  2873              "showUpdateError", "showUpdateHistory", "showUpdateInstalled"];
  2875   fns.forEach(function(aPromptFn) {
  2876     UpdatePrompt.prototype[aPromptFn] = function() {
  2877       if (!gUpdatePromptCallback) {
  2878         return;
  2881       var callback = gUpdatePromptCallback[aPromptFn];
  2882       if (!callback) {
  2883         return;
  2886       callback.apply(gUpdatePromptCallback,
  2887                      Array.prototype.slice.call(arguments));
  2889   });
  2892 UpdatePrompt.prototype = {
  2893   flags: AUS_Ci.nsIClassInfo.SINGLETON,
  2894   implementationLanguage: AUS_Ci.nsIProgrammingLanguage.JAVASCRIPT,
  2895   getHelperForLanguage: function(aLanguage) null,
  2896   getInterfaces: function(aCount) {
  2897     var interfaces = [AUS_Ci.nsISupports, AUS_Ci.nsIUpdatePrompt];
  2898     aCount.value = interfaces.length;
  2899     return interfaces;
  2900   },
  2901   classDescription: "UpdatePrompt",
  2902   contractID: "@mozilla.org/updates/update-prompt;1",
  2903   classID: Components.ID("{8c350a15-9b90-4622-93a1-4d320308664b}"),
  2904   createInstance: function(aOuter, aIID) {
  2905     if (aOuter == null)
  2906       return gUpdatePrompt.QueryInterface(aIID);
  2907     throw AUS_Cr.NS_ERROR_NO_AGGREGATION;
  2908   },
  2909   QueryInterface: function(aIID) {
  2910     if (aIID.equals(AUS_Ci.nsIClassInfo) ||
  2911         aIID.equals(AUS_Ci.nsISupports) ||
  2912         aIID.equals(AUS_Ci.nsIUpdatePrompt))
  2913       return gUpdatePrompt;
  2914     throw AUS_Cr.NS_ERROR_NO_INTERFACE;
  2915   },
  2916 };
  2918 /* Update check listener */
  2919 const updateCheckListener = {
  2920   onProgress: function UCL_onProgress(aRequest, aPosition, aTotalSize) {
  2921   },
  2923   onCheckComplete: function UCL_onCheckComplete(aRequest, aUpdates, aUpdateCount) {
  2924     gRequestURL = aRequest.channel.originalURI.spec;
  2925     gUpdateCount = aUpdateCount;
  2926     gUpdates = aUpdates;
  2927     logTestInfo("url = " + gRequestURL + ", " +
  2928                 "request.status = " + aRequest.status + ", " +
  2929                 "update.statusText = " + aRequest.statusText + ", " +
  2930                 "updateCount = " + aUpdateCount);
  2931     // Use a timeout to allow the XHR to complete
  2932     do_execute_soon(gCheckFunc);
  2933   },
  2935   onError: function UCL_onError(aRequest, aUpdate) {
  2936     gRequestURL = aRequest.channel.originalURI.spec;
  2937     gStatusCode = aRequest.status;
  2939     gStatusText = aUpdate.statusText;
  2940     logTestInfo("url = " + gRequestURL + ", " +
  2941                 "request.status = " + gStatusCode + ", " +
  2942                 "update.statusText = " + gStatusText);
  2943     // Use a timeout to allow the XHR to complete
  2944     do_execute_soon(gCheckFunc.bind(null, aRequest, aUpdate));
  2945   },
  2947   QueryInterface: function(aIID) {
  2948     if (!aIID.equals(AUS_Ci.nsIUpdateCheckListener) &&
  2949         !aIID.equals(AUS_Ci.nsISupports))
  2950       throw AUS_Cr.NS_ERROR_NO_INTERFACE;
  2951     return this;
  2953 };
  2955 /* Update download listener - nsIRequestObserver */
  2956 const downloadListener = {
  2957   onStartRequest: function DL_onStartRequest(aRequest, aContext) {
  2958   },
  2960   onProgress: function DL_onProgress(aRequest, aContext, aProgress, aMaxProgress) {
  2961   },
  2963   onStatus: function DL_onStatus(aRequest, aContext, aStatus, aStatusText) {
  2964   },
  2966   onStopRequest: function DL_onStopRequest(aRequest, aContext, aStatus) {
  2967     gStatusResult = aStatus;
  2968     // Use a timeout to allow the request to complete
  2969     do_execute_soon(gCheckFunc);
  2970   },
  2972   QueryInterface: function DL_QueryInterface(aIID) {
  2973     if (!aIID.equals(AUS_Ci.nsIRequestObserver) &&
  2974         !aIID.equals(AUS_Ci.nsIProgressEventSink) &&
  2975         !aIID.equals(AUS_Ci.nsISupports))
  2976       throw AUS_Cr.NS_ERROR_NO_INTERFACE;
  2977     return this;
  2979 };
  2981 /**
  2982  * Helper for starting the http server used by the tests
  2983  */
  2984 function start_httpserver() {
  2985   let dir = getTestDirFile();
  2986   logTestInfo("http server directory path: " + dir.path);
  2988   if (!dir.isDirectory()) {
  2989     do_throw("A file instead of a directory was specified for HttpServer " +
  2990              "registerDirectory! Path: " + dir.path + "\n");
  2993   AUS_Cu.import("resource://testing-common/httpd.js");
  2994   gTestserver = new HttpServer();
  2995   gTestserver.registerDirectory("/", dir);
  2996   gTestserver.start(-1);
  2997   let testserverPort = gTestserver.identity.primaryPort;
  2998   gURLData = URL_HOST + ":" + testserverPort + "/";
  2999   logTestInfo("http server port = " + testserverPort);
  3002 /**
  3003  * Helper for stopping the http server used by the tests
  3005  * @param   aCallback
  3006  *          The callback to call after stopping the http server.
  3007  */
  3008 function stop_httpserver(aCallback) {
  3009   do_check_true(!!aCallback);
  3010   gTestserver.stop(aCallback);
  3013 /**
  3014  * Creates an nsIXULAppInfo
  3016  * @param   aID
  3017  *          The ID of the test application
  3018  * @param   aName
  3019  *          A name for the test application
  3020  * @param   aVersion
  3021  *          The version of the application
  3022  * @param   aPlatformVersion
  3023  *          The gecko version of the application
  3024  */
  3025 function createAppInfo(aID, aName, aVersion, aPlatformVersion) {
  3026   const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
  3027   const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
  3028   var XULAppInfo = {
  3029     vendor: APP_INFO_VENDOR,
  3030     name: aName,
  3031     ID: aID,
  3032     version: aVersion,
  3033     appBuildID: "2007010101",
  3034     platformVersion: aPlatformVersion,
  3035     platformBuildID: "2007010101",
  3036     inSafeMode: false,
  3037     logConsoleErrors: true,
  3038     OS: "XPCShell",
  3039     XPCOMABI: "noarch-spidermonkey",
  3041     QueryInterface: function QueryInterface(aIID) {
  3042       if (aIID.equals(AUS_Ci.nsIXULAppInfo) ||
  3043           aIID.equals(AUS_Ci.nsIXULRuntime) ||
  3044 #ifdef XP_WIN
  3045           aIID.equals(AUS_Ci.nsIWinAppHelper) ||
  3046 #endif
  3047           aIID.equals(AUS_Ci.nsISupports))
  3048         return this;
  3049       throw AUS_Cr.NS_ERROR_NO_INTERFACE;
  3051   };
  3053   var XULAppInfoFactory = {
  3054     createInstance: function (aOuter, aIID) {
  3055       if (aOuter == null)
  3056         return XULAppInfo.QueryInterface(aIID);
  3057       throw AUS_Cr.NS_ERROR_NO_AGGREGATION;
  3059   };
  3061   var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar);
  3062   registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
  3063                             XULAPPINFO_CONTRACTID, XULAppInfoFactory);
  3066 /**
  3067  * Returns the platform specific arguments used by nsIProcess when launching
  3068  * the application.
  3070  * @param   aExtraArgs (optional)
  3071  *          An array of extra arguments to append to the default arguments.
  3072  * @return  an array of arguments to be passed to nsIProcess.
  3074  * Note: a shell is necessary to pipe the application's console output which
  3075  *       would otherwise pollute the xpcshell log.
  3077  * Command line arguments used when launching the application:
  3078  * -no-remote prevents shell integration from being affected by an existing
  3079  * application process.
  3080  * -process-updates makes the application exits after being relaunched by the
  3081  * updater.
  3082  * the platform specific string defined by PIPE_TO_NULL to output both stdout
  3083  * and stderr to null. This is needed to prevent output from the application
  3084  * from ending up in the xpchsell log.
  3085  */
  3086 function getProcessArgs(aExtraArgs) {
  3087   if (!aExtraArgs) {
  3088     aExtraArgs = [];
  3091   let appBinPath = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false).path;
  3092   if (/ /.test(appBinPath)) {
  3093     appBinPath = '"' + appBinPath + '"';
  3096   let args;
  3097   if (IS_UNIX) {
  3098     let launchScript = getLaunchScript();
  3099     // Precreate the script with executable permissions
  3100     launchScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
  3102     let scriptContents = "#! /bin/sh\n";
  3103     scriptContents += appBinPath + " -no-remote -process-updates " +
  3104                       aExtraArgs.join(" ") + " " + PIPE_TO_NULL;
  3105     writeFile(launchScript, scriptContents);
  3106     logTestInfo("created " + launchScript.path + " containing:\n" +
  3107                 scriptContents);
  3108     args = [launchScript.path];
  3109   } else {
  3110     args = ["/D", "/Q", "/C", appBinPath, "-no-remote", "-process-updates"].
  3111            concat(aExtraArgs).concat([PIPE_TO_NULL]);
  3113   return args;
  3116 /**
  3117  * Gets a file path for the application to dump its arguments into.  This is used
  3118  * to verify that a callback application is launched.
  3120  * @return  the file for the application to dump its arguments into.
  3121  */
  3122 function getAppArgsLogPath() {
  3123   let appArgsLog = do_get_file("/", true);
  3124   appArgsLog.append(gTestID + "_app_args_log");
  3125   if (appArgsLog.exists()) {
  3126     appArgsLog.remove(false);
  3128   let appArgsLogPath = appArgsLog.path;
  3129   if (/ /.test(appArgsLogPath)) {
  3130     appArgsLogPath = '"' + appArgsLogPath + '"';
  3132   return appArgsLogPath;
  3135 /**
  3136  * Gets the nsIFile reference for the shell script to launch the application. If
  3137  * the file exists it will be removed by this function.
  3139  * @return  the nsIFile for the shell script to launch the application.
  3140  */
  3141 function getLaunchScript() {
  3142   let launchScript = do_get_file("/", true);
  3143   launchScript.append(gTestID + "_launch.sh");
  3144   if (launchScript.exists()) {
  3145     launchScript.remove(false);
  3147   return launchScript;
  3150 /**
  3151  * Makes GreD, XREExeF, and UpdRootD point to unique file system locations so
  3152  * xpcshell tests can run in parallel and to keep the environment clean.
  3153  */
  3154 function adjustGeneralPaths() {
  3155   let dirProvider = {
  3156     getFile: function AGP_DP_getFile(aProp, aPersistent) {
  3157       aPersistent.value = true;
  3158       switch (aProp) {
  3159         case NS_GRE_DIR:
  3160           if (gUseTestAppDir) {
  3161             return getApplyDirFile(DIR_BIN_REL_PATH, true);
  3163           break;
  3164         case XRE_EXECUTABLE_FILE:
  3165           if (gUseTestAppDir) {
  3166             return getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, true);
  3168           break;
  3169         case XRE_UPDATE_ROOT_DIR:
  3170           return getMockUpdRootD();
  3172       return null;
  3173     },
  3174     QueryInterface: function(aIID) {
  3175       if (aIID.equals(AUS_Ci.nsIDirectoryServiceProvider) ||
  3176           aIID.equals(AUS_Ci.nsISupports))
  3177         return this;
  3178       throw AUS_Cr.NS_ERROR_NO_INTERFACE;
  3180   };
  3181   let ds = Services.dirsvc.QueryInterface(AUS_Ci.nsIDirectoryService);
  3182   ds.QueryInterface(AUS_Ci.nsIProperties).undefine(NS_GRE_DIR);
  3183   ds.QueryInterface(AUS_Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE);
  3184   ds.registerProvider(dirProvider);
  3185   do_register_cleanup(function AGP_cleanup() {
  3186     logTestInfo("start - unregistering directory provider");
  3188     if (gAppTimer) {
  3189       logTestInfo("start - cancel app timer");
  3190       gAppTimer.cancel();
  3191       gAppTimer = null;
  3192       logTestInfo("finish - cancel app timer");
  3195     if (gProcess && gProcess.isRunning) {
  3196       logTestInfo("start - kill process");
  3197       try {
  3198         gProcess.kill();
  3199       } catch (e) {
  3200         logTestInfo("kill process failed. Exception: " + e);
  3202       gProcess = null;
  3203       logTestInfo("finish - kill process");
  3206     if (gHandle) {
  3207       try {
  3208         logTestInfo("start - closing handle");
  3209         let kernel32 = ctypes.open("kernel32");
  3210         let CloseHandle = kernel32.declare("CloseHandle", ctypes.default_abi,
  3211                                            ctypes.bool, /*return*/
  3212                                            ctypes.voidptr_t /*handle*/);
  3213         if (!CloseHandle(gHandle)) {
  3214           logTestInfo("call to CloseHandle failed");
  3216         kernel32.close();
  3217         gHandle = null;
  3218         logTestInfo("finish - closing handle");
  3219       } catch (e) {
  3220         logTestInfo("call to CloseHandle failed. Exception: " + e);
  3224     // Call end_test first before the directory provider is unregistered
  3225     if (typeof(end_test) == typeof(Function)) {
  3226       logTestInfo("calling end_test");
  3227       end_test();
  3230     ds.unregisterProvider(dirProvider);
  3231     cleanupTestCommon();
  3233     logTestInfo("finish - unregistering directory provider");
  3234   });
  3238 /**
  3239  * Helper function for launching the application to apply an update.
  3240  */
  3241 function launchAppToApplyUpdate() {
  3242   logTestInfo("start - launching application to apply update");
  3244   let appBin = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false);
  3246   if (typeof(customLaunchAppToApplyUpdate) == typeof(Function)) {
  3247     customLaunchAppToApplyUpdate();
  3250   let launchBin = getLaunchBin();
  3251   let args = getProcessArgs();
  3252   logTestInfo("launching " + launchBin.path + " " + args.join(" "));
  3254   gProcess = AUS_Cc["@mozilla.org/process/util;1"].
  3255                 createInstance(AUS_Ci.nsIProcess);
  3256   gProcess.init(launchBin);
  3258   gAppTimer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer);
  3259   gAppTimer.initWithCallback(gTimerCallback, APP_TIMER_TIMEOUT,
  3260                              AUS_Ci.nsITimer.TYPE_ONE_SHOT);
  3262   setEnvironment();
  3263   logTestInfo("launching application");
  3264   gProcess.runAsync(args, args.length, gProcessObserver);
  3265   resetEnvironment();
  3267   logTestInfo("finish - launching application to apply update");
  3270 /**
  3271  * The observer for the call to nsIProcess:runAsync.
  3272  */
  3273 let gProcessObserver = {
  3274   observe: function PO_observe(aSubject, aTopic, aData) {
  3275     logTestInfo("topic: " + aTopic + ", process exitValue: " +
  3276                 gProcess.exitValue);
  3277     if (gAppTimer) {
  3278       gAppTimer.cancel();
  3279       gAppTimer = null;
  3281     if (aTopic != "process-finished" || gProcess.exitValue != 0) {
  3282       do_throw("Failed to launch application");
  3284     do_timeout(TEST_CHECK_TIMEOUT, checkUpdateFinished);
  3285   },
  3286   QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIObserver])
  3287 };
  3289 /**
  3290  * The timer callback to kill the process if it takes too long.
  3291  */
  3292 let gTimerCallback = {
  3293   notify: function TC_notify(aTimer) {
  3294     gAppTimer = null;
  3295     if (gProcess.isRunning) {
  3296       logTestInfo("attempt to kill process");
  3297       gProcess.kill();
  3299     do_throw("launch application timer expired");
  3300   },
  3301   QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsITimerCallback])
  3302 };
  3304 /**
  3305  * The update-staged observer for the call to nsIUpdateProcessor:processUpdate.
  3306  */
  3307 let gUpdateStagedObserver = {
  3308   observe: function(aSubject, aTopic, aData) {
  3309     if (aTopic == "update-staged") {
  3310       Services.obs.removeObserver(gUpdateStagedObserver, "update-staged");
  3311       checkUpdateApplied();
  3313   },
  3314   QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIObserver])
  3315 };
  3317 /**
  3318  * Sets the environment that will be used by the application process when it is
  3319  * launched.
  3320  */
  3321 function setEnvironment() {
  3322   // Prevent setting the environment more than once.
  3323   if (gShouldResetEnv !== undefined)
  3324     return;
  3326   gShouldResetEnv = true;
  3328   let env = AUS_Cc["@mozilla.org/process/environment;1"].
  3329             getService(AUS_Ci.nsIEnvironment);
  3330   if (IS_WIN && !env.exists("XRE_NO_WINDOWS_CRASH_DIALOG")) {
  3331     gAddedEnvXRENoWindowsCrashDialog = true;
  3332     logTestInfo("setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
  3333                 "variable to 1... previously it didn't exist");
  3334     env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1");
  3337   if (IS_UNIX) {
  3338     let appGreDir = gGREDirOrig.clone();
  3339     let envGreDir = AUS_Cc["@mozilla.org/file/local;1"].
  3340                     createInstance(AUS_Ci.nsILocalFile);
  3341     let shouldSetEnv = true;
  3342     if (IS_MACOSX) {
  3343       if (env.exists("DYLD_LIBRARY_PATH")) {
  3344         gEnvDyldLibraryPath = env.get("DYLD_LIBRARY_PATH");
  3345         envGreDir.initWithPath(gEnvDyldLibraryPath);
  3346         if (envGreDir.path == appGreDir.path) {
  3347           gEnvDyldLibraryPath = null;
  3348           shouldSetEnv = false;
  3352       if (shouldSetEnv) {
  3353         logTestInfo("setting DYLD_LIBRARY_PATH environment variable value to " +
  3354                     appGreDir.path);
  3355         env.set("DYLD_LIBRARY_PATH", appGreDir.path);
  3357     } else {
  3358       if (env.exists("LD_LIBRARY_PATH")) {
  3359         gEnvLdLibraryPath = env.get("LD_LIBRARY_PATH");
  3360         envGreDir.initWithPath(gEnvLdLibraryPath);
  3361         if (envGreDir.path == appGreDir.path) {
  3362           gEnvLdLibraryPath = null;
  3363           shouldSetEnv = false;
  3367       if (shouldSetEnv) {
  3368         logTestInfo("setting LD_LIBRARY_PATH environment variable value to " +
  3369                     appGreDir.path);
  3370         env.set("LD_LIBRARY_PATH", appGreDir.path);
  3375   if (env.exists("XPCOM_MEM_LEAK_LOG")) {
  3376     gEnvXPCOMMemLeakLog = env.get("XPCOM_MEM_LEAK_LOG");
  3377     logTestInfo("removing the XPCOM_MEM_LEAK_LOG environment variable... " +
  3378                 "previous value " + gEnvXPCOMMemLeakLog);
  3379     env.set("XPCOM_MEM_LEAK_LOG", "");
  3382   if (env.exists("XPCOM_DEBUG_BREAK")) {
  3383     gEnvXPCOMDebugBreak = env.get("XPCOM_DEBUG_BREAK");
  3384     logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
  3385                 "warn... previous value " + gEnvXPCOMDebugBreak);
  3386   } else {
  3387     logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
  3388                 "warn... previously it didn't exist");
  3391   env.set("XPCOM_DEBUG_BREAK", "warn");
  3393   if (gStageUpdate) {
  3394     logTestInfo("setting the MOZ_UPDATE_STAGING environment variable to 1\n");
  3395     env.set("MOZ_UPDATE_STAGING", "1");
  3398   logTestInfo("setting MOZ_NO_SERVICE_FALLBACK environment variable to 1");
  3399   env.set("MOZ_NO_SERVICE_FALLBACK", "1");
  3402 /**
  3403  * Sets the environment back to the original values after launching the
  3404  * application.
  3405  */
  3406 function resetEnvironment() {
  3407   // Prevent resetting the environment more than once.
  3408   if (gShouldResetEnv !== true)
  3409     return;
  3411   gShouldResetEnv = false;
  3413   let env = AUS_Cc["@mozilla.org/process/environment;1"].
  3414             getService(AUS_Ci.nsIEnvironment);
  3416   if (gEnvXPCOMMemLeakLog) {
  3417     logTestInfo("setting the XPCOM_MEM_LEAK_LOG environment variable back to " +
  3418                 gEnvXPCOMMemLeakLog);
  3419     env.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog);
  3422   if (gEnvXPCOMDebugBreak) {
  3423     logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable back to " +
  3424                 gEnvXPCOMDebugBreak);
  3425     env.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak);
  3426   } else {
  3427     logTestInfo("clearing the XPCOM_DEBUG_BREAK environment variable");
  3428     env.set("XPCOM_DEBUG_BREAK", "");
  3431   if (IS_UNIX) {
  3432     if (IS_MACOSX) {
  3433       if (gEnvDyldLibraryPath) {
  3434         logTestInfo("setting DYLD_LIBRARY_PATH environment variable value " +
  3435                     "back to " + gEnvDyldLibraryPath);
  3436         env.set("DYLD_LIBRARY_PATH", gEnvDyldLibraryPath);
  3437       } else {
  3438         logTestInfo("removing DYLD_LIBRARY_PATH environment variable");
  3439         env.set("DYLD_LIBRARY_PATH", "");
  3441     } else {
  3442       if (gEnvLdLibraryPath) {
  3443         logTestInfo("setting LD_LIBRARY_PATH environment variable value back " +
  3444                     "to " + gEnvLdLibraryPath);
  3445         env.set("LD_LIBRARY_PATH", gEnvLdLibraryPath);
  3446       } else {
  3447         logTestInfo("removing LD_LIBRARY_PATH environment variable");
  3448         env.set("LD_LIBRARY_PATH", "");
  3453   if (IS_WIN && gAddedEnvXRENoWindowsCrashDialog) {
  3454     logTestInfo("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
  3455                 "variable");
  3456     env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "");
  3459   if (gStageUpdate) {
  3460     logTestInfo("removing the MOZ_UPDATE_STAGING environment variable\n");
  3461     env.set("MOZ_UPDATE_STAGING", "");
  3464   logTestInfo("removing MOZ_NO_SERVICE_FALLBACK environment variable");
  3465   env.set("MOZ_NO_SERVICE_FALLBACK", "");

mercurial