Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/toolkit/mozapps/update/tests/data/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 180 kB image not shown  

Quelle  xpcshellUtilsAUS.js   Sprache: JAVA

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/**
 * Test log warnings that happen before the test has started
 * "Couldn't get the user appdata directory. Crash events may not be produced."
 * in nsExceptionHandler.cpp (possibly bug 619104)
 *
 * Test log warnings that happen after the test has finished
 * "OOPDeinit() without successful OOPInit()" in nsExceptionHandler.cpp
 * (bug 619104)
 * "XPCOM objects created/destroyed from static ctor/dtor" in nsTraceRefcnt.cpp
 * (possibly bug 457479)
 *
 * Other warnings printed to the test logs
 * "site security information will not be persisted" in
 * nsSiteSecurityService.cpp and the error in nsSystemInfo.cpp preceding this
 * error are due to not having a profile when running some of the xpcshell
 * tests. Since most xpcshell tests also log these errors these tests don't
 * call do_get_profile unless necessary for the test.
 * "!mMainThread" in nsThreadManager.cpp are due to using timers and it might be
 * possible to fix some or all of these in the test itself.
 * "NS_FAILED(rv)" in nsThreadUtils.cpp are due to using timers and it might be
 * possible to fix some or all of these in the test itself.
 */


"use strict";

const EXIT_CODE_BASE = ChromeUtils.importESModule(
  "resource://gre/modules/BackgroundTasksManager.sys.mjs"
).EXIT_CODE;
const { AppConstants } = ChromeUtils.importESModule(
  "resource://gre/modules/AppConstants.sys.mjs"
);
const { Subprocess } = ChromeUtils.importESModule(
  "resource://gre/modules/Subprocess.sys.mjs"
);
const { TestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/TestUtils.sys.mjs"
);

ChromeUtils.defineESModuleGetters(this, {
  MockRegistrar: "resource://testing-common/MockRegistrar.sys.mjs",
  updateAppInfo: "resource://testing-common/AppInfo.sys.mjs",
});

const Cm = Components.manager;

/* global MOZ_APP_VENDOR, MOZ_APP_BASENAME */
/* global MOZ_VERIFY_MAR_SIGNATURE, IS_AUTHENTICODE_CHECK_ENABLED */
load("../data/xpcshellConstantsPP.js");

// Note: DIR_CONTENTS, DIR_MACOS and DIR_RESOURCES only differ on macOS. They
//       default to "" on all other platforms.
const DIR_CONTENTS = AppConstants.platform == "macosx" ? "Contents/" : "";
const DIR_MACOS =
  AppConstants.platform == "macosx" ? DIR_CONTENTS + "MacOS/" : "";
const DIR_RESOURCES =
  AppConstants.platform == "macosx" ? DIR_CONTENTS + "Resources/" : "";
const TEST_FILE_SUFFIX = AppConstants.platform == "macosx" ? "_mac" : "";
const FILE_COMPLETE_MAR = "complete" + TEST_FILE_SUFFIX + ".mar";
const FILE_PARTIAL_MAR = "partial" + TEST_FILE_SUFFIX + ".mar";
const FILE_COMPLETE_PRECOMPLETE = "complete_precomplete" + TEST_FILE_SUFFIX;
const FILE_PARTIAL_PRECOMPLETE = "partial_precomplete" + TEST_FILE_SUFFIX;
const FILE_COMPLETE_REMOVEDFILES = "complete_removed-files" + TEST_FILE_SUFFIX;
const FILE_PARTIAL_REMOVEDFILES = "partial_removed-files" + TEST_FILE_SUFFIX;
const FILE_UPDATE_IN_PROGRESS_LOCK = "updated.update_in_progress.lock";
const COMPARE_LOG_SUFFIX = "_" + mozinfo.os;
const LOG_COMPLETE_SUCCESS = "complete_log_success" + COMPARE_LOG_SUFFIX;
const LOG_PARTIAL_SUCCESS = "partial_log_success" + COMPARE_LOG_SUFFIX;
const LOG_PARTIAL_FAILURE = "partial_log_failure" + COMPARE_LOG_SUFFIX;
const LOG_REPLACE_SUCCESS = "replace_log_success";
const MAC_APP_XATTR_KEY = "com.apple.application-instance";
const MAC_APP_XATTR_VALUE = "dlsource%3Dmozillaci";

const USE_EXECV = AppConstants.platform == "linux";

const URL_HOST = "http://localhost";

const APP_INFO_NAME = "XPCShell";
const APP_INFO_VENDOR = "Mozilla";

const APP_BIN_SUFFIX =
  AppConstants.platform == "linux" ? "-bin" : mozinfo.bin_suffix;
const FILE_APP_BIN = AppConstants.MOZ_APP_NAME + APP_BIN_SUFFIX;
const FILE_COMPLETE_EXE = "complete.exe";
const FILE_HELPER_BIN =
  AppConstants.platform == "macosx"
    ? "callback_app.app/Contents/MacOS/TestAUSHelper"
    : "TestAUSHelper" + mozinfo.bin_suffix;
const FILE_HELPER_APP =
  AppConstants.platform == "macosx" ? "callback_app.app" : FILE_HELPER_BIN;
const FILE_MAINTENANCE_SERVICE_BIN = "maintenanceservice.exe";
const FILE_MAINTENANCE_SERVICE_INSTALLER_BIN =
  "maintenanceservice_installer.exe";
const FILE_OLD_VERSION_MAR = "old_version.mar";
const FILE_PARTIAL_EXE = "partial.exe";
const FILE_UPDATER_BIN =
  "updater" + (AppConstants.platform == "macosx" ? ".app" : mozinfo.bin_suffix);

const PERFORMING_STAGED_UPDATE = "Performing a staged update";
const CALL_QUIT = "calling QuitProgressUI";
const ERR_UPDATE_IN_PROGRESS = "Update already in progress! Exiting";
const ERR_RENAME_FILE = "rename_file: failed to rename file";
const ERR_ENSURE_COPY = "ensure_copy: failed to copy the file";
const ERR_UNABLE_OPEN_DEST = "unable to open destination file";
const ERR_BACKUP_DISCARD = "backup_discard: unable to remove";
const ERR_MOVE_DESTDIR_7 = "Moving destDir to tmpDir failed, err: 7";
const ERR_BACKUP_CREATE_7 = "backup_create failed: 7";
const ERR_LOADSOURCEFILE_FAILED = "LoadSourceFile failed";
const ERR_PARENT_PID_PERSISTS =
  "The parent process didn't exit! Continuing with update.";
const ERR_BGTASK_EXCLUSIVE =
  "failed to exclusively open executable file from background task: ";

const LOG_SVC_SUCCESSFUL_LAUNCH = "Process was started... waiting on result.";
const LOG_SVC_UNSUCCESSFUL_LAUNCH =
  "The install directory path is not valid for this application.";

// Typical end of a message when calling assert
const MSG_SHOULD_EQUAL = " should equal the expected value";
const MSG_SHOULD_EXIST = "the file or directory should exist";
const MSG_SHOULD_NOT_EXIST = "the file or directory should not exist";

const CONTINUE_CHECK = "continueCheck";
const CONTINUE_DOWNLOAD = "continueDownload";
const CONTINUE_STAGING = "continueStaging";

// Time in seconds the helper application should sleep before exiting. The
// helper can also be made to exit by writing |finish| to its input file.
const HELPER_SLEEP_TIMEOUT = 180;

// How many of do_timeout calls using FILE_IN_USE_TIMEOUT_MS to wait before the
// test is aborted.
const FILE_IN_USE_TIMEOUT_MS = 1000;

const PIPE_TO_NULL =
  AppConstants.platform == "win" ? ">nul" : "> /dev/null 2>&1";

const LOG_FUNCTION = info;

const gHTTPHandlerPath = "updates.xml";

var gIsServiceTest;
var gTestID;

// This default value will be overridden when using the http server.
var gURLData = URL_HOST + "/";
var gTestserver;
var gUpdateCheckCount = 0;

const REL_PATH_DATA = "";
const APP_UPDATE_SJS_HOST = "http://127.0.0.1";
const APP_UPDATE_SJS_PATH = "/" + REL_PATH_DATA + "app_update.sjs";

var gIncrementalDownloadErrorType;

var gResponseBody;

var gProcess;
var gAppTimer;
var gHandle;

var gGREDirOrig;
var gGREBinDirOrig;

var gPIDPersistProcess;

// Variables are used instead of contants so tests can override these values if
// necessary.
var gCallbackArgs = ["./""callback.log""Test Arg 2""Test Arg 3"];
var gCallbackApp = (() => {
  if (AppConstants.platform == "macosx") {
    return "callback_app.app";
  }
  return "callback_app" + mozinfo.bin_suffix;
})();

var gCallbackBinFile = (() => {
  if (AppConstants.platform == "macosx") {
    return FILE_HELPER_BIN;
  }
  return "callback_app" + mozinfo.bin_suffix;
})();

var gPostUpdateBinFile = "postup_app" + mozinfo.bin_suffix;

var gTimeoutRuns = 0;

// Environment related globals
var gShouldResetEnv = undefined;
var gAddedEnvXRENoWindowsCrashDialog = false;
var gCrashReporterDisabled;
var gEnvXPCOMDebugBreak;
var gEnvXPCOMMemLeakLog;
var gEnvForceServiceFallback = false;

const URL_HTTP_UPDATE_SJS = "http://test_details/";
const DATA_URI_SPEC = Services.io.newFileURI(do_get_file(""false)).spec;

/* import-globals-from shared.js */
load("shared.js");

// Set to true to log additional information for debugging. To log additional
// information for individual tests set gDebugTest to false here and to true in
// the test's onload function.
gDebugTest = true;

// Setting gDebugTestLog to true will create log files for the tests in
// <objdir>/_tests/xpcshell/toolkit/mozapps/update/tests/<testdir>/ except for
// the service tests since they run sequentially. This can help when debugging
// failures for the tests that intermittently fail when they run in parallel.
// Never set gDebugTestLog to true except when running tests locally.
var gDebugTestLog = false;
// An empty array for gTestsToLog will log most of the output of all of the
// update tests except for the service tests. To only log specific tests add the
// test file name without the file extension to the array below.
var gTestsToLog = [];
var gRealDump;
var gFOS;
var gUpdateBin;

var gTestFiles = [];
var gTestDirs = [];

// Common files for both successful and failed updates.
var gTestFilesCommon = [
  {
    description: "Should never change",
    fileName: FILE_CHANNEL_PREFS,
    relPathDir:
      AppConstants.platform == "macosx"
        ? "Contents/Frameworks/ChannelPrefs.framework/"
        : DIR_RESOURCES + "defaults/pref/",
    originalContents: "ShouldNotBeReplaced\n",
    compareContents: "ShouldNotBeReplaced\n",
    originalFile: null,
    compareFile: null,
    originalPerms: 0o767,
    comparePerms: 0o767,
  },
];

var gTestFilesCommonNonMac = [
  {
    description: "Should never change",
    fileName: FILE_UPDATE_SETTINGS_INI,
    relPathDir: DIR_RESOURCES,
    originalContents: UPDATE_SETTINGS_CONTENTS,
    compareContents: UPDATE_SETTINGS_CONTENTS,
    originalFile: null,
    compareFile: null,
    originalPerms: 0o767,
    comparePerms: 0o767,
  },
];

if (AppConstants.platform != "macosx") {
  gTestFilesCommon = gTestFilesCommon.concat(gTestFilesCommonNonMac);
}

var gTestFilesCommonMac = [
  {
    description: "Should never change",
    fileName: FILE_UPDATE_SETTINGS_FRAMEWORK,
    relPathDir:
      DIR_MACOS + "updater.app/Contents/Frameworks/UpdateSettings.framework/",
    originalContents: null,
    compareContents: null,
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: null,
    existingFile: true,
  },
  {
    description: "Should never change",
    fileName: FILE_INFO_PLIST,
    relPathDir: DIR_CONTENTS,
    originalContents: DIR_APP_INFO_PLIST_FILE_CONTENTS,
    compareContents: DIR_APP_INFO_PLIST_FILE_CONTENTS,
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: null,
    existingFile: true,
  },
  {
    description: "Should never change",
    fileName: FILE_INFO_PLIST,
    relPathDir: DIR_MACOS + "updater.app/Contents/",
    originalContents: null,
    compareContents: null,
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: null,
    existingFile: true,
  },
  {
    description: "Should never change",
    fileName: FILE_INFO_PLIST,
    relPathDir: DIR_MACOS + "callback_app.app/Contents/",
    originalContents: null,
    compareContents: null,
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: null,
    existingFile: true,
  },
];

if (AppConstants.platform == "macosx") {
  gTestFilesCommon = gTestFilesCommon.concat(gTestFilesCommonMac);
}

// Files for a complete successful update. This can be used for a complete
// failed update by calling setTestFilesAndDirsForFailure.
var gTestFilesCompleteSuccess = [
  {
    description: "Added by update.manifest (add)",
    fileName: "precomplete",
    relPathDir: DIR_RESOURCES,
    originalContents: null,
    compareContents: null,
    originalFile: FILE_PARTIAL_PRECOMPLETE,
    compareFile: FILE_COMPLETE_PRECOMPLETE,
    originalPerms: 0o666,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "searchpluginstext0",
    relPathDir: DIR_RESOURCES + "searchplugins/",
    originalContents: "ToBeReplacedWithFromComplete\n",
    compareContents: "FromComplete\n",
    originalFile: null,
    compareFile: null,
    originalPerms: 0o775,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "searchpluginspng1.png",
    relPathDir: DIR_RESOURCES + "searchplugins/",
    originalContents: null,
    compareContents: null,
    originalFile: null,
    compareFile: "complete.png",
    originalPerms: null,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "searchpluginspng0.png",
    relPathDir: DIR_RESOURCES + "searchplugins/",
    originalContents: null,
    compareContents: null,
    originalFile: "partial.png",
    compareFile: "complete.png",
    originalPerms: 0o666,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "removed-files",
    relPathDir: DIR_RESOURCES,
    originalContents: null,
    compareContents: null,
    originalFile: FILE_PARTIAL_REMOVEDFILES,
    compareFile: FILE_COMPLETE_REMOVEDFILES,
    originalPerms: 0o666,
    comparePerms: 0o644,
  },
  {
    description:
      "Added by update.manifest if the parent directory exists (add-if)",
    fileName: "extensions1text0",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
    originalContents: null,
    compareContents: "FromComplete\n",
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: 0o644,
  },
  {
    description:
      "Added by update.manifest if the parent directory exists (add-if)",
    fileName: "extensions1png1.png",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
    originalContents: null,
    compareContents: null,
    originalFile: "partial.png",
    compareFile: "complete.png",
    originalPerms: 0o666,
    comparePerms: 0o644,
  },
  {
    description:
      "Added by update.manifest if the parent directory exists (add-if)",
    fileName: "extensions1png0.png",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
    originalContents: null,
    compareContents: null,
    originalFile: null,
    compareFile: "complete.png",
    originalPerms: null,
    comparePerms: 0o644,
  },
  {
    description:
      "Added by update.manifest if the parent directory exists (add-if)",
    fileName: "extensions0text0",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
    originalContents: "ToBeReplacedWithFromComplete\n",
    compareContents: "FromComplete\n",
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: 0o644,
  },
  {
    description:
      "Added by update.manifest if the parent directory exists (add-if)",
    fileName: "extensions0png1.png",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
    originalContents: null,
    compareContents: null,
    originalFile: null,
    compareFile: "complete.png",
    originalPerms: null,
    comparePerms: 0o644,
  },
  {
    description:
      "Added by update.manifest if the parent directory exists (add-if)",
    fileName: "extensions0png0.png",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
    originalContents: null,
    compareContents: null,
    originalFile: null,
    compareFile: "complete.png",
    originalPerms: null,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "exe0.exe",
    relPathDir: DIR_MACOS,
    originalContents: null,
    compareContents: null,
    originalFile: FILE_HELPER_BIN,
    compareFile: FILE_COMPLETE_EXE,
    originalPerms: 0o777,
    comparePerms: 0o755,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "10text0",
    relPathDir: DIR_RESOURCES + "1/10/",
    originalContents: "ToBeReplacedWithFromComplete\n",
    compareContents: "FromComplete\n",
    originalFile: null,
    compareFile: null,
    originalPerms: 0o767,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "0exe0.exe",
    relPathDir: DIR_RESOURCES + "0/",
    originalContents: null,
    compareContents: null,
    originalFile: FILE_HELPER_BIN,
    compareFile: FILE_COMPLETE_EXE,
    originalPerms: 0o777,
    comparePerms: 0o755,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "00text1",
    relPathDir: DIR_RESOURCES + "0/00/",
    originalContents: "ToBeReplacedWithFromComplete\n",
    compareContents: "FromComplete\n",
    originalFile: null,
    compareFile: null,
    originalPerms: 0o677,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "00text0",
    relPathDir: DIR_RESOURCES + "0/00/",
    originalContents: "ToBeReplacedWithFromComplete\n",
    compareContents: "FromComplete\n",
    originalFile: null,
    compareFile: null,
    originalPerms: 0o775,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "00png0.png",
    relPathDir: DIR_RESOURCES + "0/00/",
    originalContents: null,
    compareContents: null,
    originalFile: null,
    compareFile: "complete.png",
    originalPerms: 0o776,
    comparePerms: 0o644,
  },
  {
    description: "Removed by precomplete (remove)",
    fileName: "20text0",
    relPathDir: DIR_RESOURCES + "2/20/",
    originalContents: "ToBeDeleted\n",
    compareContents: null,
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: null,
  },
  {
    description: "Removed by precomplete (remove)",
    fileName: "20png0.png",
    relPathDir: DIR_RESOURCES + "2/20/",
    originalContents: "ToBeDeleted\n",
    compareContents: null,
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: null,
  },
];

// Concatenate the common files to the end of the array.
gTestFilesCompleteSuccess = gTestFilesCompleteSuccess.concat(gTestFilesCommon);

// Files for a partial successful update. This can be used for a partial failed
// update by calling setTestFilesAndDirsForFailure.
var gTestFilesPartialSuccess = [
  {
    description: "Added by update.manifest (add)",
    fileName: "precomplete",
    relPathDir: DIR_RESOURCES,
    originalContents: null,
    compareContents: null,
    originalFile: FILE_COMPLETE_PRECOMPLETE,
    compareFile: FILE_PARTIAL_PRECOMPLETE,
    originalPerms: 0o666,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "searchpluginstext0",
    relPathDir: DIR_RESOURCES + "searchplugins/",
    originalContents: "ToBeReplacedWithFromPartial\n",
    compareContents: "FromPartial\n",
    originalFile: null,
    compareFile: null,
    originalPerms: 0o775,
    comparePerms: 0o644,
  },
  {
    description: "Patched by update.manifest if the file exists (patch-if)",
    fileName: "searchpluginspng1.png",
    relPathDir: DIR_RESOURCES + "searchplugins/",
    originalContents: null,
    compareContents: null,
    originalFile: "complete.png",
    compareFile: "partial.png",
    originalPerms: 0o666,
    comparePerms: 0o666,
  },
  {
    description: "Patched by update.manifest if the file exists (patch-if)",
    fileName: "searchpluginspng0.png",
    relPathDir: DIR_RESOURCES + "searchplugins/",
    originalContents: null,
    compareContents: null,
    originalFile: "complete.png",
    compareFile: "partial.png",
    originalPerms: 0o666,
    comparePerms: 0o666,
  },
  {
    description:
      "Added by update.manifest if the parent directory exists (add-if)",
    fileName: "extensions1text0",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
    originalContents: null,
    compareContents: "FromPartial\n",
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: 0o644,
  },
  {
    description:
      "Patched by update.manifest if the parent directory exists (patch-if)",
    fileName: "extensions1png1.png",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
    originalContents: null,
    compareContents: null,
    originalFile: "complete.png",
    compareFile: "partial.png",
    originalPerms: 0o666,
    comparePerms: 0o666,
  },
  {
    description:
      "Patched by update.manifest if the parent directory exists (patch-if)",
    fileName: "extensions1png0.png",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions1/",
    originalContents: null,
    compareContents: null,
    originalFile: "complete.png",
    compareFile: "partial.png",
    originalPerms: 0o666,
    comparePerms: 0o666,
  },
  {
    description:
      "Added by update.manifest if the parent directory exists (add-if)",
    fileName: "extensions0text0",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
    originalContents: "ToBeReplacedWithFromPartial\n",
    compareContents: "FromPartial\n",
    originalFile: null,
    compareFile: null,
    originalPerms: 0o644,
    comparePerms: 0o644,
  },
  {
    description:
      "Patched by update.manifest if the parent directory exists (patch-if)",
    fileName: "extensions0png1.png",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
    originalContents: null,
    compareContents: null,
    originalFile: "complete.png",
    compareFile: "partial.png",
    originalPerms: 0o644,
    comparePerms: 0o644,
  },
  {
    description:
      "Patched by update.manifest if the parent directory exists (patch-if)",
    fileName: "extensions0png0.png",
    relPathDir: DIR_RESOURCES + "distribution/extensions/extensions0/",
    originalContents: null,
    compareContents: null,
    originalFile: "complete.png",
    compareFile: "partial.png",
    originalPerms: 0o644,
    comparePerms: 0o644,
  },
  {
    description: "Patched by update.manifest (patch)",
    fileName: "exe0.exe",
    relPathDir: DIR_MACOS,
    originalContents: null,
    compareContents: null,
    originalFile: FILE_COMPLETE_EXE,
    compareFile: FILE_PARTIAL_EXE,
    originalPerms: 0o755,
    comparePerms: 0o755,
  },
  {
    description: "Patched by update.manifest (patch)",
    fileName: "0exe0.exe",
    relPathDir: DIR_RESOURCES + "0/",
    originalContents: null,
    compareContents: null,
    originalFile: FILE_COMPLETE_EXE,
    compareFile: FILE_PARTIAL_EXE,
    originalPerms: 0o755,
    comparePerms: 0o755,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "00text0",
    relPathDir: DIR_RESOURCES + "0/00/",
    originalContents: "ToBeReplacedWithFromPartial\n",
    compareContents: "FromPartial\n",
    originalFile: null,
    compareFile: null,
    originalPerms: 0o644,
    comparePerms: 0o644,
  },
  {
    description: "Patched by update.manifest (patch)",
    fileName: "00png0.png",
    relPathDir: DIR_RESOURCES + "0/00/",
    originalContents: null,
    compareContents: null,
    originalFile: "complete.png",
    compareFile: "partial.png",
    originalPerms: 0o666,
    comparePerms: 0o666,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "20text0",
    relPathDir: DIR_RESOURCES + "2/20/",
    originalContents: null,
    compareContents: "FromPartial\n",
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "20png0.png",
    relPathDir: DIR_RESOURCES + "2/20/",
    originalContents: null,
    compareContents: null,
    originalFile: null,
    compareFile: "partial.png",
    originalPerms: null,
    comparePerms: 0o644,
  },
  {
    description: "Added by update.manifest (add)",
    fileName: "00text2",
    relPathDir: DIR_RESOURCES + "0/00/",
    originalContents: null,
    compareContents: "FromPartial\n",
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: 0o644,
  },
  {
    description: "Removed by update.manifest (remove)",
    fileName: "10text0",
    relPathDir: DIR_RESOURCES + "1/10/",
    originalContents: "ToBeDeleted\n",
    compareContents: null,
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: null,
  },
  {
    description: "Removed by update.manifest (remove)",
    fileName: "00text1",
    relPathDir: DIR_RESOURCES + "0/00/",
    originalContents: "ToBeDeleted\n",
    compareContents: null,
    originalFile: null,
    compareFile: null,
    originalPerms: null,
    comparePerms: null,
  },
];

// Concatenate the common files to the end of the array.
gTestFilesPartialSuccess = gTestFilesPartialSuccess.concat(gTestFilesCommon);

/**
 * Searches `gTestFiles` for the file with the given filename. This is currently
 * not very efficient (it searches the whole array every time).
 *
 * @param filename
 *        The name of the file to search for (i.e. the `fileName` attribute).
 * @returns
 *        The object in `gTestFiles` that describes the requested file.
 *        Or `null`, if the file is not in `gTestFiles`.
 */

function getTestFileByName(filename) {
  return gTestFiles.find(f => f.fileName == filename) ?? null;
}

var gTestDirsCommon = [
  {
    relPathDir: DIR_RESOURCES + "3/",
    dirRemoved: false,
    files: ["3text0""3text1"],
    filesRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "4/",
    dirRemoved: true,
    files: ["4text0""4text1"],
    filesRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "5/",
    dirRemoved: true,
    files: ["5test.exe""5text0""5text1"],
    filesRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "6/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "7/",
    dirRemoved: true,
    files: ["7text0""7text1"],
    subDirs: ["70/""71/"],
    subDirFiles: ["7xtest.exe""7xtext0""7xtext1"],
  },
  {
    relPathDir: DIR_RESOURCES + "8/",
    dirRemoved: false,
  },
  {
    relPathDir: DIR_RESOURCES + "8/80/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "8/81/",
    dirRemoved: false,
    files: ["81text0""81text1"],
  },
  {
    relPathDir: DIR_RESOURCES + "8/82/",
    dirRemoved: false,
    subDirs: ["820/""821/"],
  },
  {
    relPathDir: DIR_RESOURCES + "8/83/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "8/84/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "8/85/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "8/86/",
    dirRemoved: true,
    files: ["86text0""86text1"],
  },
  {
    relPathDir: DIR_RESOURCES + "8/87/",
    dirRemoved: true,
    subDirs: ["870/""871/"],
    subDirFiles: ["87xtext0""87xtext1"],
  },
  {
    relPathDir: DIR_RESOURCES + "8/88/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "8/89/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "9/90/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "9/91/",
    dirRemoved: false,
    files: ["91text0""91text1"],
  },
  {
    relPathDir: DIR_RESOURCES + "9/92/",
    dirRemoved: false,
    subDirs: ["920/""921/"],
  },
  {
    relPathDir: DIR_RESOURCES + "9/93/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "9/94/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "9/95/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "9/96/",
    dirRemoved: true,
    files: ["96text0""96text1"],
  },
  {
    relPathDir: DIR_RESOURCES + "9/97/",
    dirRemoved: true,
    subDirs: ["970/""971/"],
    subDirFiles: ["97xtext0""97xtext1"],
  },
  {
    relPathDir: DIR_RESOURCES + "9/98/",
    dirRemoved: true,
  },
  {
    relPathDir: DIR_RESOURCES + "9/99/",
    dirRemoved: true,
  },
  {
    description:
      "Silences 'WARNING: Failed to resolve XUL App Dir.' in debug builds",
    relPathDir: DIR_RESOURCES + "browser",
    dirRemoved: false,
  },
];

// Directories for a complete successful update. This array can be used for a
// complete failed update by calling setTestFilesAndDirsForFailure.
var gTestDirsCompleteSuccess = [
  {
    description: "Removed by precomplete (rmdir)",
    relPathDir: DIR_RESOURCES + "2/20/",
    dirRemoved: true,
  },
  {
    description: "Removed by precomplete (rmdir)",
    relPathDir: DIR_RESOURCES + "2/",
    dirRemoved: true,
  },
];

// Concatenate the common files to the beginning of the array.
gTestDirsCompleteSuccess = gTestDirsCommon.concat(gTestDirsCompleteSuccess);

// Directories for a partial successful update. This array can be used for a
// partial failed update by calling setTestFilesAndDirsForFailure.
var gTestDirsPartialSuccess = [
  {
    description: "Removed by update.manifest (rmdir)",
    relPathDir: DIR_RESOURCES + "1/10/",
    dirRemoved: true,
  },
  {
    description: "Removed by update.manifest (rmdir)",
    relPathDir: DIR_RESOURCES + "1/",
    dirRemoved: true,
  },
];

// Concatenate the common files to the beginning of the array.
gTestDirsPartialSuccess = gTestDirsCommon.concat(gTestDirsPartialSuccess);

/**
 * Helper function for setting up the test environment.
 *
 * @param  aAppUpdateAutoEnabled
 *         See setAppUpdateAutoSync in shared.js for details.
 * @param  aAllowBits
 *         If true, allow update downloads via the Windows BITS service.
 *         If false, this download mechanism will not be used.
 */

function setupTestCommon(aAppUpdateAutoEnabled = false, aAllowBits = false) {
  debugDump("start - general test setup");

  Assert.strictEqual(
    gTestID,
    undefined,
    "gTestID should be 'undefined' (setupTestCommon should " +
      "only be called once)"
  );

  let caller = Components.stack.caller;
  gTestID = caller.filename.toString().split("/").pop().split(".")[0];

  if (gDebugTestLog && !gIsServiceTest) {
    if (!gTestsToLog.length || gTestsToLog.includes(gTestID)) {
      let logFile = do_get_file(gTestID + ".log"true);
      if (!logFile.exists()) {
        logFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
      }
      gFOS = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
        Ci.nsIFileOutputStream
      );
      gFOS.init(logFile, MODE_WRONLY | MODE_APPEND, PERMS_FILE, 0);

      gRealDump = dump;
      dump = dumpOverride;
    }
  }

  createAppInfo("xpcshell@tests.mozilla.org", APP_INFO_NAME, "1.0""2.0");

  if (gIsServiceTest && !shouldRunServiceTest()) {
    return false;
  }

  do_test_pending();

  setDefaultPrefs();

  gGREDirOrig = getGREDir();
  gGREBinDirOrig = getGREBinDir();

  let applyDir = getApplyDirFile().parent;

  // Try to remove the directory used to apply updates and the updates directory
  // on platforms other than Windows. This is non-fatal for the test since if
  // this fails a different directory will be used.
  if (applyDir.exists()) {
    debugDump("attempting to remove directory. Path: " + applyDir.path);
    try {
      removeDirRecursive(applyDir);
    } catch (e) {
      logTestInfo(
        "non-fatal error removing directory. Path: " +
          applyDir.path +
          ", Exception: " +
          e
      );
      // When the application doesn't exit properly it can cause the test to
      // fail again on the second run with an NS_ERROR_FILE_ACCESS_DENIED error
      // along with no useful information in the test log. To prevent this use
      // a different directory for the test when it isn't possible to remove the
      // existing test directory (bug 1294196).
      gTestID += "_new";
      logTestInfo(
        "using a new directory for the test by changing gTestID " +
          "since there is an existing test directory that can't be " +
          "removed, gTestID: " +
          gTestID
      );
    }
  }

  if (AppConstants.platform == "win") {
    Services.prefs.setBoolPref(
      PREF_APP_UPDATE_SERVICE_ENABLED,
      !!gIsServiceTest
    );
  }

  if (gIsServiceTest) {
    let exts = ["id""log""status"];
    for (let i = 0; i < exts.length; ++i) {
      let file = getSecureOutputFile(exts[i]);
      if (file.exists()) {
        try {
          file.remove(false);
        } catch (e) {}
      }
    }
  }

  adjustGeneralPaths();
  createWorldWritableAppUpdateDir();

  // Logged once here instead of in the mock directory provider to lessen test
  // log spam.
  debugDump("Updates Directory (UpdRootD) Path: " + getMockUpdRootD().path);

  // This prevents a warning about not being able to find the greprefs.js file
  // from being logged.
  let grePrefsFile = getGREDir();
  if (!grePrefsFile.exists()) {
    grePrefsFile.create(Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
  }
  grePrefsFile.append("greprefs.js");
  if (!grePrefsFile.exists()) {
    grePrefsFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
  }

  // The name of the update lock needs to be changed to match the path
  // overridden in adjustGeneralPaths() above. Wait until now to reset
  // because the GRE dir now exists, which may cause the "install
  // path" to be normalized differently now that it can be resolved.
  debugDump("resetting update lock");
  resetSyncManagerLock();

  // Remove the updates directory on Windows and macOS which is located
  // outside of the application directory after the call to adjustGeneralPaths
  // has set it up. Since the test hasn't run yet, the directory shouldn't
  // exist and failure to remove the directory should be non-fatal for the test.
  if (AppConstants.platform == "win" || AppConstants.platform == "macosx") {
    let updatesDir = getMockUpdRootD();
    if (updatesDir.exists()) {
      debugDump("attempting to remove directory. Path: " + updatesDir.path);
      try {
        removeDirRecursive(updatesDir);
      } catch (e) {
        logTestInfo(
          "non-fatal error removing directory. Path: " +
            updatesDir.path +
            ", Exception: " +
            e
        );
      }
    }
  }

  setAppUpdateAutoSync(aAppUpdateAutoEnabled);
  Services.prefs.setBoolPref(PREF_APP_UPDATE_BITS_ENABLED, aAllowBits);

  debugDump("finish - general test setup");
  return true;
}

/**
 * Cleans up all the files we may have created by simulating an update.
 */

function cleanupUpdateFiles() {
  if (AppConstants.platform == "macosx" || AppConstants.platform == "linux") {
    // This will delete the launch script if it exists.
    getLaunchScript();
  }

  if (gIsServiceTest) {
    let exts = ["id""log""status"];
    for (let i = 0; i < exts.length; ++i) {
      let file = getSecureOutputFile(exts[i]);
      if (file.exists()) {
        try {
          file.remove(false);
        } catch (e) {}
      }
    }
  }

  // The updates directory is located outside of the application directory and
  // needs to be removed on Windows and Mac OS X.
  if (AppConstants.platform == "win" || AppConstants.platform == "macosx") {
    let updatesDir = getMockUpdRootD();
    // Try to remove the directory used to apply updates. Since the test has
    // already finished this is non-fatal for the test.
    if (updatesDir.exists()) {
      debugDump("attempting to remove directory. Path: " + updatesDir.path);
      try {
        removeDirRecursive(updatesDir);
      } catch (e) {
        logTestInfo(
          "non-fatal error removing directory. Path: " +
            updatesDir.path +
            ", Exception: " +
            e
        );
      }
      if (AppConstants.platform == "macosx") {
        let updatesRootDir = gUpdatesRootDir.clone();
        while (updatesRootDir.path != updatesDir.path) {
          if (updatesDir.exists()) {
            debugDump(
              "attempting to remove directory. Path: " + updatesDir.path
            );
            try {
              // Try to remove the directory without the recursive flag set
              // since the top level directory has already had its contents
              // removed and the parent directory might still be used by a
              // different test.
              updatesDir.remove(false);
            } catch (e) {
              logTestInfo(
                "non-fatal error removing directory. Path: " +
                  updatesDir.path +
                  ", Exception: " +
                  e
              );
              if (e == Cr.NS_ERROR_FILE_DIR_NOT_EMPTY) {
                break;
              }
            }
          }
          updatesDir = updatesDir.parent;
        }
      }
    }
  }

  let applyDir = getApplyDirFile().parent;

  // Try to remove the directory used to apply updates. Since the test has
  // already finished this is non-fatal for the test.
  if (applyDir.exists()) {
    debugDump("attempting to remove directory. Path: " + applyDir.path);
    try {
      removeDirRecursive(applyDir);
    } catch (e) {
      logTestInfo(
        "non-fatal error removing directory. Path: " +
          applyDir.path +
          ", Exception: " +
          e
      );
    }
  }
  // We just deleted this.
  gUpdateBin = null;
}

/**
 * Nulls out the most commonly used global vars used by tests to prevent leaks
 * as needed and attempts to restore the system to its original state.
 */

function cleanupTestCommon() {
  debugDump("start - general test cleanup");

  if (gChannel) {
    gPrefRoot.removeObserver(PREF_APP_UPDATE_CHANNEL, observer);
  }

  gTestserver = null;

  if (AppConstants.platform == "win" && MOZ_APP_BASENAME) {
    let appDir = getApplyDirFile();
    let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla";
    const REG_PATH =
      "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME + "\\TaskBarIDs";
    let key = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
      Ci.nsIWindowsRegKey
    );
    try {
      key.open(
        Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
        REG_PATH,
        Ci.nsIWindowsRegKey.ACCESS_ALL
      );
      if (key.hasValue(appDir.path)) {
        key.removeValue(appDir.path);
      }
    } catch (e) {}
    try {
      key.open(
        Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
        REG_PATH,
        Ci.nsIWindowsRegKey.ACCESS_ALL
      );
      if (key.hasValue(appDir.path)) {
        key.removeValue(appDir.path);
      }
    } catch (e) {}
  }

  cleanupUpdateFiles();

  resetEnvironment();
  Services.prefs.clearUserPref(PREF_APP_UPDATE_BITS_ENABLED);

  debugDump("finish - general test cleanup");

  if (gRealDump) {
    dump = gRealDump;
    gRealDump = null;
  }

  if (gFOS) {
    gFOS.close();
  }
}

/**
 * Helper function to store the log output of calls to dump in a variable so the
 * values can be written to a file for a parallel run of a test and printed to
 * the log file when the test runs synchronously.
 */

function dumpOverride(aText) {
  gFOS.write(aText, aText.length);
  gRealDump(aText);
}

/**
 * Helper function that calls do_test_finished that tracks whether a parallel
 * run of a test passed when it runs synchronously so the log output can be
 * inspected.
 */

async function doTestFinish() {
  if (gDebugTest) {
    // This prevents do_print errors from being printed by the xpcshell test
    // harness due to nsUpdateService.js logging to the console when the
    // app.update.log preference is true.
    Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, false);
    gAUS.observe(null"nsPref:changed", PREF_APP_UPDATE_LOG);
  }

  await reloadUpdateManagerData(true);

  // Call app update's observe method passing quit-application to test that the
  // shutdown of app update runs without throwing or leaking. The observer
  // method is used directly instead of calling notifyObservers so components
  // outside of the scope of this test don't assert and thereby cause app update
  // tests to fail.
  gAUS.observe(null"quit-application""");

  executeSoon(do_test_finished);
}

/**
 * Sets the most commonly used preferences used by tests
 */

function setDefaultPrefs() {
  Services.prefs.setBoolPref(PREF_APP_UPDATE_DISABLEDFORTESTING, false);
  if (gDebugTest) {
    // Enable Update logging
    Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true);
  } else {
    // Some apps set this preference to true by default
    Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, false);
  }
}

/**
 * Helper function for updater binary tests that sets the appropriate values
 * to check for update failures.
 */

function setTestFilesAndDirsForFailure() {
  gTestFiles.forEach(function STFADFF_Files(aTestFile) {
    aTestFile.compareContents = aTestFile.originalContents;
    aTestFile.compareFile = aTestFile.originalFile;
    aTestFile.comparePerms = aTestFile.originalPerms;
  });

  gTestDirs.forEach(function STFADFF_Dirs(aTestDir) {
    aTestDir.dirRemoved = false;
    if (aTestDir.filesRemoved) {
      aTestDir.filesRemoved = false;
    }
  });
}

/**
 * Helper function for updater binary tests that prevents the distribution
 * directory files from being created.
 */

function preventDistributionFiles() {
  gTestFiles = gTestFiles.filter(function (aTestFile) {
    return !aTestFile.relPathDir.includes("distribution/");
  });

  gTestDirs = gTestDirs.filter(function (aTestDir) {
    return !aTestDir.relPathDir.includes("distribution/");
  });
}

/**
 * On Mac OS X this sets the last modified time for the app bundle directory to
 * a date in the past to test that the last modified time is updated when an
 * update has been successfully applied (bug 600098).
 */

function setAppBundleModTime() {
  if (AppConstants.platform != "macosx") {
    return;
  }
  let now = Date.now();
  let yesterday = now - 1000 * 60 * 60 * 24;
  let applyToDir = getApplyDirFile();
  applyToDir.lastModifiedTime = yesterday;
}

/**
 * On Mac OS X this checks that the last modified time for the app bundle
 * directory has been updated when an update has been successfully applied
 * (bug 600098).
 */

function checkAppBundleModTime() {
  if (AppConstants.platform != "macosx") {
    return;
  }
  // All we care about is that the last modified time has changed so that Mac OS
  // X Launch Services invalidates its cache so the test allows up to one minute
  // difference in the last modified time.
  const MAC_MAX_TIME_DIFFERENCE = 60000;
  let now = Date.now();
  let applyToDir = getApplyDirFile();
  let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
  Assert.ok(
    timeDiff < MAC_MAX_TIME_DIFFERENCE,
    "the last modified time on the apply to directory should " +
      "change after a successful update"
  );
}

/**
 * Performs Update Manager checks to verify that the update metadata is correct
 * and that it is the same after the update xml files are reloaded.
 *
 * @param   aStatusFileState
 *          The expected state of the status file.
 * @param   aHasActiveUpdate
 *          Should there be an active update.
 * @param   aUpdateStatusState
 *          The expected update's status state.
 * @param   aUpdateErrCode
 *          The expected update's error code.
 * @param   aUpdateCount
 *          The update history's update count.
 */

async function checkUpdateManager(
  aStatusFileState,
  aHasActiveUpdate,
  aUpdateStatusState,
  aUpdateErrCode,
  aUpdateCount
) {
  let activeUpdate = await (aUpdateStatusState == STATE_DOWNLOADING
    ? gUpdateManager.getDownloadingUpdate()
    : gUpdateManager.getReadyUpdate());
  Assert.equal(
    readStatusState(),
    aStatusFileState,
    "the status file state" + MSG_SHOULD_EQUAL
  );
  let msgTags = [" after startup "" after a file reload "];
  for (let i = 0; i < msgTags.length; ++i) {
    logTestInfo(
      "checking Update Manager updates" + msgTags[i] + "is performed"
    );
    if (aHasActiveUpdate) {
      Assert.ok(
        !!activeUpdate,
        msgTags[i] + "the active update should be defined"
      );
    } else {
      Assert.ok(
        !activeUpdate,
        msgTags[i] + "the active update should not be defined"
      );
    }
    const history = await gUpdateManager.getHistory();
    Assert.equal(
      history.length,
      aUpdateCount,
      msgTags[i] + "the update manager updateCount attribute" + MSG_SHOULD_EQUAL
    );
    if (aUpdateCount > 0) {
      let update = history[0];
      Assert.equal(
        update.state,
        aUpdateStatusState,
        msgTags[i] + "the first update state" + MSG_SHOULD_EQUAL
      );
      Assert.equal(
        update.errorCode,
        aUpdateErrCode,
        msgTags[i] + "the first update errorCode" + MSG_SHOULD_EQUAL
      );
    }
    if (i != msgTags.length - 1) {
      reloadUpdateManagerData();
    }
  }
}

/**
 * Waits until the update files exist or not based on the parameters specified
 * when calling this function or the default values if the parameters are not
 * specified. This is necessary due to the update xml files being written
 * asynchronously by nsIUpdateManager.
 *
 * @param   aActiveUpdateExists (optional)
 *          Whether the active-update.xml file should exist (default is false).
 * @param   aUpdatesExists (optional)
 *          Whether the updates.xml file should exist (default is true).
 */

async function waitForUpdateXMLFiles(
  aActiveUpdateExists = false,
  aUpdatesExists = true
) {
  function areFilesStabilized() {
    let file = getUpdateDirFile(FILE_ACTIVE_UPDATE_XML_TMP);
    if (file.exists()) {
      debugDump("file exists, Path: " + file.path);
      return false;
    }
    file = getUpdateDirFile(FILE_UPDATES_XML_TMP);
    if (file.exists()) {
      debugDump("file exists, Path: " + file.path);
      return false;
    }
    file = getUpdateDirFile(FILE_ACTIVE_UPDATE_XML);
    if (file.exists() != aActiveUpdateExists) {
      debugDump(
        "file exists should equal: " +
          aActiveUpdateExists +
          ", Path: " +
          file.path
      );
      return false;
    }
    file = getUpdateDirFile(FILE_UPDATES_XML);
    if (file.exists() != aUpdatesExists) {
      debugDump(
        "file exists should equal: " +
          aActiveUpdateExists +
          ", Path: " +
          file.path
      );
      return false;
    }
    return true;
  }

  await TestUtils.waitForCondition(
    () => areFilesStabilized(),
    "Waiting for update xml files to stabilize"
  );
}

/**
 * On Mac OS X and Windows this checks if the post update '.running' file exists
 * to determine if the post update binary was launched.
 *
 * @param   aShouldExist
 *          Whether the post update '.running' file should exist.
 */

function checkPostUpdateRunningFile(aShouldExist) {
  if (AppConstants.platform == "linux") {
    return;
  }
  let postUpdateRunningFile = getPostUpdateFile(".running");
  if (aShouldExist) {
    Assert.ok(
      postUpdateRunningFile.exists(),
      MSG_SHOULD_EXIST + getMsgPath(postUpdateRunningFile.path)
    );
  } else {
    Assert.ok(
      !postUpdateRunningFile.exists(),
      MSG_SHOULD_NOT_EXIST + getMsgPath(postUpdateRunningFile.path)
    );
  }
}

/**
 * Initializes the most commonly used settings and creates an instance of the
 * update service stub.
 */

async function standardInit() {
  // Initialize the update service stub component
  await initUpdateServiceStub();
}

/**
 * Helper function for getting the application version from the application.ini
 * file. This will look in both the GRE and the application directories for the
 * application.ini file.
 *
 * @return  The version string from the application.ini file.
 */

function getAppVersion() {
  // Read the application.ini and use its application version.
  let iniFile = gGREDirOrig.clone();
  iniFile.append(FILE_APPLICATION_INI);
  if (!iniFile.exists()) {
    iniFile = gGREBinDirOrig.clone();
    iniFile.append(FILE_APPLICATION_INI);
  }
  Assert.ok(iniFile.exists(), MSG_SHOULD_EXIST + getMsgPath(iniFile.path));
  let iniParser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]
    .getService(Ci.nsIINIParserFactory)
    .createINIParser(iniFile);
  return iniParser.getString("App""Version");
}

/**
 * Helper function for getting the path to the directory where the
 * application binary is located (e.g. <test_file_leafname>/dir.app/).
 *
 * Note: The dir.app subdirectory under <test_file_leafname> is needed for
 *       platforms other than Mac OS X so the tests can run in parallel due to
 *       update staging creating a lock file named moz_update_in_progress.lock in
 *       the parent directory of the installation directory.
 * Note: For service tests with IS_AUTHENTICODE_CHECK_ENABLED we use an absolute
 *       path inside Program Files because the service itself will refuse to
 *       update an installation not located in Program Files.
 *
 * @return  The path to the directory where application binary is located.
 */

function getApplyDirPath() {
  if (gIsServiceTest && IS_AUTHENTICODE_CHECK_ENABLED) {
    let dir = getMaintSvcDir();
    dir.append(gTestID);
    dir.append("dir.app");
    return dir.path;
  }
  return gTestID + "/dir.app/";
}

/**
 * Helper function for getting the nsIFile for a file in the directory where the
 * update will be applied.
 *
 * The files for the update are located two directories below the apply to
 * directory since macOS sets the last modified time for the root directory
 * to the current time and if the update changes any files in the root directory
 * then it wouldn't be possible to test (bug 600098).
 *
 * @param   aRelPath (optional)
 *          The relative path to the file or directory to get from the root of
 *          the test's directory. If not specified the test's directory will be
 *          returned.
 * @return  The nsIFile for the file in the directory where the update will be
 *          applied.
 */

function getApplyDirFile(aRelPath) {
  // do_get_file only supports relative paths, but under these conditions we
  // need to use an absolute path in Program Files instead.
  if (gIsServiceTest && IS_AUTHENTICODE_CHECK_ENABLED) {
    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
    file.initWithPath(getApplyDirPath());
    if (aRelPath) {
      if (aRelPath == "..") {
        file = file.parent;
      } else {
        aRelPath = aRelPath.replace(/\//g, "\\");
        file.appendRelativePath(aRelPath);
      }
    }
    return file;
  }
  let relpath = getApplyDirPath() + (aRelPath ? aRelPath : "");
  return do_get_file(relpath, true);
}

/**
 * Helper function for getting the relative path to the directory where the
 * test data files are located.
 *
 * @return  The relative path to the directory where the test data files are
 *          located.
 */

function getTestDirPath() {
  return "../data/";
}

/**
 * Helper function for getting the nsIFile for a file in the test data
 * directory.
 *
 * @param   aRelPath (optional)
 *          The relative path to the file or directory to get from the root of
 *          the test's data directory. If not specified the test's data
 *          directory will be returned.
 * @param   aAllowNonExists (optional)
 *          Whether or not to throw an error if the path exists.
 *          If not specified, then false is used.
 * @return  The nsIFile for the file in the test data directory.
 * @throws  If the file or directory does not exist.
 */

function getTestDirFile(aRelPath, aAllowNonExists) {
  let relpath = getTestDirPath() + (aRelPath ? aRelPath : "");
  return do_get_file(relpath, !!aAllowNonExists);
}

/**
 * Helper function for getting the nsIFile for the maintenance service
 * directory on Windows.
 *
 * @return  The nsIFile for the maintenance service directory.
 * @throws  If called from a platform other than Windows.
 */

function getMaintSvcDir() {
  if (AppConstants.platform != "win") {
    do_throw("Windows only function called by a different platform!");
  }

  const CSIDL_PROGRAM_FILES = 0x26;
  const CSIDL_PROGRAM_FILESX86 = 0x2a;
  // This will return an empty string on our Win XP build systems.
  let maintSvcDir = getSpecialFolderDir(CSIDL_PROGRAM_FILESX86);
  if (maintSvcDir) {
    maintSvcDir.append("Mozilla Maintenance Service");
    debugDump(
      "using CSIDL_PROGRAM_FILESX86 - maintenance service install " +
        "directory path: " +
        maintSvcDir.path
    );
  }
  if (!maintSvcDir || !maintSvcDir.exists()) {
    maintSvcDir = getSpecialFolderDir(CSIDL_PROGRAM_FILES);
    if (maintSvcDir) {
      maintSvcDir.append("Mozilla Maintenance Service");
      debugDump(
        "using CSIDL_PROGRAM_FILES - maintenance service install " +
          "directory path: " +
          maintSvcDir.path
      );
    }
  }
  if (!maintSvcDir) {
    do_throw("Unable to find the maintenance service install directory");
  }

  return maintSvcDir;
}

/**
 * Reads the current update operation/state in the status file in the secure
 * update log directory.
 *
 * @return The status value.
 */

function readSecureStatusFile() {
  let file = getSecureOutputFile("status");
  if (!file.exists()) {
    debugDump("update status file does not exist, path: " + file.path);
    return STATE_NONE;
  }
  return readFile(file).split("\n")[0];
}

/**
 * Get an nsIFile for a file in the secure update log directory. The file name
 * is always the value of gTestID and the file extension is specified by the
 * aFileExt parameter.
 *
 * @param  aFileExt
 *         The file extension.
 * @return The nsIFile of the secure update file.
 */

function getSecureOutputFile(aFileExt) {
  let file = getMaintSvcDir();
  file.append("UpdateLogs");
  file.append(gTestID + "." + aFileExt);
  return file;
}

/**
 * Get the nsIFile for a Windows special folder determined by the CSIDL
 * passed.
 *
 * @param   aCSIDL
 *          The CSIDL for the Windows special folder.
 * @return  The nsIFile for the Windows special folder.
 * @throws  If called from a platform other than Windows.
 */

function getSpecialFolderDir(aCSIDL) {
  if (AppConstants.platform != "win") {
    do_throw("Windows only function called by a different platform!");
  }

  let lib = ctypes.open("shell32");
  let SHGetSpecialFolderPath = lib.declare(
    "SHGetSpecialFolderPathW",
    ctypes.winapi_abi,
    ctypes.bool /* bool(return) */,
    ctypes.int32_t /* HWND hwndOwner */,
    ctypes.char16_t.ptr /* LPTSTR lpszPath */,
    ctypes.int32_t /* int csidl */,
    ctypes.bool /* BOOL fCreate */
  );

  let aryPath = ctypes.char16_t.array()(260);
  let rv = SHGetSpecialFolderPath(0, aryPath, aCSIDL, false);
  if (!rv) {
    do_throw(
      "SHGetSpecialFolderPath failed to retrieve " +
        aCSIDL +
        " with Win32 error " +
        ctypes.winLastError
    );
  }
  lib.close();

  let path = aryPath.readString(); // Convert the c-string to js-string
  if (!path) {
    return null;
  }
  debugDump("SHGetSpecialFolderPath returned path: " + path);
  let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
  dir.initWithPath(path);
  return dir;
}

ChromeUtils.defineLazyGetter(
  this,
  "gInstallDirPathHash",
  function test_gIDPH() {
    if (AppConstants.platform != "win") {
      do_throw("Windows only function called by a different platform!");
    }

    if (!MOZ_APP_BASENAME) {
      return null;
    }

    let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla";
    let appDir = getApplyDirFile();

    const REG_PATH =
      "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME + "\\TaskBarIDs";
    let regKey = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
      Ci.nsIWindowsRegKey
    );
    try {
      regKey.open(
        Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
        REG_PATH,
        Ci.nsIWindowsRegKey.ACCESS_ALL
      );
      regKey.writeStringValue(appDir.path, gTestID);
      return gTestID;
    } catch (e) {}

    try {
      regKey.create(
        Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
        REG_PATH,
        Ci.nsIWindowsRegKey.ACCESS_ALL
      );
      regKey.writeStringValue(appDir.path, gTestID);
      return gTestID;
    } catch (e) {
      logTestInfo(
        "failed to create registry value. Registry Path: " +
          REG_PATH +
          ", Value Name: " +
          appDir.path +
          ", Value Data: " +
          gTestID +
          ", Exception " +
          e
      );
      do_throw(
        "Unable to write HKLM or HKCU TaskBarIDs registry value, key path: " +
          REG_PATH
      );
    }
    return null;
  }
);

ChromeUtils.defineLazyGetter(this"gLocalAppDataDir"function test_gLADD() {
  if (AppConstants.platform != "win") {
    do_throw("Windows only function called by a different platform!");
  }

  const CSIDL_LOCAL_APPDATA = 0x1c;
  return getSpecialFolderDir(CSIDL_LOCAL_APPDATA);
});

ChromeUtils.defineLazyGetter(this"gCommonAppDataDir"function test_gCDD() {
  if (AppConstants.platform != "win") {
    do_throw("Windows only function called by a different platform!");
  }

  const CSIDL_COMMON_APPDATA = 0x0023;
  return getSpecialFolderDir(CSIDL_COMMON_APPDATA);
});

ChromeUtils.defineLazyGetter(this"gProgFilesDir"function test_gPFD() {
  if (AppConstants.platform != "win") {
    do_throw("Windows only function called by a different platform!");
  }

  const CSIDL_PROGRAM_FILES = 0x26;
  return getSpecialFolderDir(CSIDL_PROGRAM_FILES);
});

/**
 * Helper function for getting the update root directory used by the tests. This
 * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir
 * in nsXREDirProvider.cpp so an application will be able to find the update
 * when running a test that launches the application.
 *
 * The aGetOldLocation argument performs the same function that the argument
 * with the same name in nsXREDirProvider::GetUpdateRootDir performs. If true,
 * the old (pre-migration) update directory is returned.
 */

function getMockUpdRootD(aGetOldLocation = false) {
  if (AppConstants.platform == "win") {
    return getMockUpdRootDWin(aGetOldLocation);
  }

  if (AppConstants.platform == "macosx") {
    return getMockUpdRootDMac();
  }

  return getApplyDirFile(DIR_MACOS);
}

/**
 * Helper function for getting the update root directory used by the tests. This
 * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir
 * in nsXREDirProvider.cpp so an application will be able to find the update
 * when running a test that launches the application.
 *
 * @throws  If called from a platform other than Windows.
 */

function getMockUpdRootDWin(aGetOldLocation) {
  if (AppConstants.platform != "win") {
    do_throw("Windows only function called by a different platform!");
  }

  let relPathUpdates = "";
  let dataDirectory = gCommonAppDataDir.clone();
  if (aGetOldLocation) {
    relPathUpdates += "Mozilla";
  } else {
    relPathUpdates += "Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38";
  }

  relPathUpdates += "\\" + DIR_UPDATES + "\\" + gInstallDirPathHash;
  let updatesDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
  updatesDir.initWithPath(dataDirectory.path + "\\" + relPathUpdates);
  return updatesDir;
}

function createWorldWritableAppUpdateDir() {
  // This function is only necessary in Windows
  if (AppConstants.platform == "win") {
    let installDir = Services.dirsvc.get(
      XRE_EXECUTABLE_FILE,
      Ci.nsIFile
    ).parent;
    let exitValue = runTestHelperSync(["create-update-dir", installDir.path]);
    Assert.equal(exitValue, 0, "The helper process exit value should be 0");
  }
}

ChromeUtils.defineLazyGetter(this"gUpdatesRootDir"function test_gURD() {
  if (AppConstants.platform != "macosx") {
    do_throw("Mac OS X only function called by a different platform!");
  }

  let dir = Services.dirsvc.get("ULibDir", Ci.nsIFile);
  dir.append("Caches");
  if (MOZ_APP_VENDOR || MOZ_APP_BASENAME) {
    dir.append(MOZ_APP_VENDOR ? MOZ_APP_VENDOR : MOZ_APP_BASENAME);
  } else {
    dir.append("Mozilla");
  }
  dir.append(DIR_UPDATES);
  return dir;
});

/**
 * Helper function for getting the update root directory used by the tests. This
 * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir
 * in nsXREDirProvider.cpp so an application will be able to find the update
 * when running a test that launches the application.
 */

function getMockUpdRootDMac() {
  if (AppConstants.platform != "macosx") {
    do_throw("Mac OS X only function called by a different platform!");
  }

  let exeFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
  exeFile.initWithPath(gCustomGeneralPaths[XRE_EXECUTABLE_FILE]);
  let appDir = exeFile.parent.parent.parent;
  let appDirPath = appDir.path;
  appDirPath = appDirPath.substr(0, appDirPath.length - 4);

  let pathUpdates = gUpdatesRootDir.path + appDirPath;
  let updatesDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
  updatesDir.initWithPath(pathUpdates);
  return updatesDir;
}

/**
 * Creates an update in progress lock file in the specified directory on
 * Windows.
 *
 * @param   aDir
 *          The nsIFile for the directory where the lock file should be created.
 * @throws  If called from a platform other than Windows.
 */

function createUpdateInProgressLockFile(aDir) {
  if (AppConstants.platform != "win") {
    do_throw("Windows only function called by a different platform!");
  }

  let file = aDir.clone();
  file.append(FILE_UPDATE_IN_PROGRESS_LOCK);
  file.create(file.NORMAL_FILE_TYPE, 0o444);
  file.QueryInterface(Ci.nsILocalFileWin);
  file.readOnly = true;
  Assert.ok(file.exists(), MSG_SHOULD_EXIST + getMsgPath(file.path));
  Assert.ok(!file.isWritable(), "the lock file should not be writeable");
}

/**
 * Removes an update in progress lock file in the specified directory on
 * Windows.
 *
 * @param   aDir
 *          The nsIFile for the directory where the lock file is located.
 * @throws  If called from a platform other than Windows.
 */

function removeUpdateInProgressLockFile(aDir) {
  if (AppConstants.platform != "win") {
    do_throw("Windows only function called by a different platform!");
  }

  let file = aDir.clone();
  file.append(FILE_UPDATE_IN_PROGRESS_LOCK);
  file.QueryInterface(Ci.nsILocalFileWin);
  file.readOnly = false;
  file.remove(false);
  Assert.ok(!file.exists(), MSG_SHOULD_NOT_EXIST + getMsgPath(file.path));
}

function stripQuarantineBitFromPath(aPath) {
  if (AppConstants.platform != "macosx") {
    do_throw("macOS-only function called by a different platform!");
  }

  let args = ["-dr""com.apple.quarantine", aPath];
  let stripQuarantineBitProcess = Cc[
    "@mozilla.org/process/util;1"
  ].createInstance(Ci.nsIProcess);
  let xattrBin = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
  xattrBin.initWithPath("/usr/bin/xattr");
  stripQuarantineBitProcess.init(xattrBin);
  stripQuarantineBitProcess.run(true, args, args.length);
}

/**
 * Copies the test updater to the GRE binary directory and returns the nsIFile
 * for the copied test updater.
 *
 * @return  nsIFIle for the copied test updater.
 */

function copyTestUpdaterToBinDir() {
  let testUpdater = getTestDirFile(FILE_UPDATER_BIN);
  let updater = getGREBinDir();
  updater.append(FILE_UPDATER_BIN);
  if (!updater.exists()) {
    testUpdater.copyToFollowingLinks(updater.parent, FILE_UPDATER_BIN);
  }

  if (AppConstants.platform == "macosx") {
    stripQuarantineBitFromPath(updater.path);
    updater.append("Contents");
    updater.append("MacOS");
    updater.append("org.mozilla.updater");
  }
  return updater;
}

/**
 * Logs the contents of an update log and for maintenance service tests this
 * will log the contents of the latest maintenanceservice.log.
 *
 * @param   aLogLeafName
 *          The leaf name of the update log.
 */

function logUpdateLog(aLogLeafName) {
  let updateLog = getUpdateDirFile(aLogLeafName);
  if (updateLog.exists()) {
    // xpcshell tests won't display the entire contents so log each line.
    let updateLogContents = readFileBytes(updateLog).replace(/\r\n/g, "\n");
    updateLogContents = removeTimeStamps(updateLogContents);
    updateLogContents = replaceLogPaths(updateLogContents);
    let aryLogContents = updateLogContents.split("\n");
    logTestInfo("contents of " + updateLog.path + ":");
    aryLogContents.forEach(function LU_ULC_FE(aLine) {
      logTestInfo(aLine);
    });
  } else {
    logTestInfo("update log doesn't exist, path: " + updateLog.path);
  }

  if (gIsServiceTest) {
    let secureStatus = readSecureStatusFile();
    logTestInfo("secure update status: " + secureStatus);

    updateLog = getSecureOutputFile("log");
    if (updateLog.exists()) {
      // xpcshell tests won't display the entire contents so log each line.
      let updateLogContents = readFileBytes(updateLog).replace(/\r\n/g, "\n");
      updateLogContents = removeTimeStamps(updateLogContents);
      updateLogContents = replaceLogPaths(updateLogContents);
      let aryLogContents = updateLogContents.split("\n");
      logTestInfo("contents of " + updateLog.path + ":");
      aryLogContents.forEach(function LU_SULC_FE(aLine) {
        logTestInfo(aLine);
      });
    } else {
      logTestInfo("secure update log doesn't exist, path: " + updateLog.path);
    }

    let serviceLog = getMaintSvcDir();
    serviceLog.append("logs");
    serviceLog.append("maintenanceservice.log");
    if (serviceLog.exists()) {
      // xpcshell tests won't display the entire contents so log each line.
      let serviceLogContents = readFileBytes(serviceLog).replace(/\r\n/g, "\n");
      serviceLogContents = replaceLogPaths(serviceLogContents);
      let aryLogContents = serviceLogContents.split("\n");
      logTestInfo("contents of " + serviceLog.path + ":");
      aryLogContents.forEach(function LU_MSLC_FE(aLine) {
        logTestInfo(aLine);
      });
    } else {
      logTestInfo(
        "maintenance service log doesn't exist, path: " + serviceLog.path
      );
    }
  }
}

/**
 * Gets the maintenance service log contents.
 */

function readServiceLogFile() {
  let file = getMaintSvcDir();
  file.append("logs");
  file.append("maintenanceservice.log");
  return readFile(file);
}

/**
 * Launches the updater binary to apply an update for updater tests.
 *
 * @param   aExpectedStatus
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=99 G=96

¤ Dauer der Verarbeitung: 0.9 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.