Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/getrandom/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 7 kB image not shown  

Quelle  xpcshellUtilsAUS.js   Sprache: unbekannt

 
/* 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

[ Original von:0.45Diese Quellcodebibliothek enthält Beispiele in vielen Programmiersprachen. Man kann per Verzeichnistruktur darin navigieren. Der Code wird farblich markiert angezeigt.  Datei übertragen  ]